Need help with wifi mesh network simulation code bug

INTRODUCTION:

Hey everyone, I am working on a simulation for a wifi mesh algorithm before implementing on a prototype for a client. The code below is the whole program, it’s a little long for a forum post, but I didn’t want to cut anything out before posting here because I want to make sure you can run and see the exact same results as I have, and it’s easier for debugging.

CODE EXPLANATION:

Most of the code you can ignore because it is just for drawing to the screen. What this algorithm does is there will be neighbors in a network (nodes/dots displayed on the screen). There are lines connected nodes if they are in range. Solid if both in range of each other (two-way connection), dashed line flowing toward the direction of the node only it is connected to (one-way connection). Each node has an ID which is the number next to the nodes on the canvas. Each node has “data”, which are the line of boxes next to each node, this can be ignored and is not related to the issue I’m seeing. One of the nodes is your mouse cursor to test connection and change the network. Each time you run the code, the locations of all the nodes move randomly to test different configurations for the algorithm. This is only the first step of the algorithm but I am very stuck. The algorithm basically will send out a ping every MONITOR_INTERVAL milliseconds and send out a broadcast to all other nodes in the network in range to see which are neighbors and add to its neighbor list. Each node has a listener running every frame to process incoming packets. If a packet received has a destination (dst) which is “broadcast”, then it will broadcast out a return packet directly to the source (src) id of the packet. It will broadcast out to all nodes in range and the node that receives it should see the destination as its own ID and the source as the node that it broadcast out to prior. If that node that is potentially a neighbor is not already in its neighbors list it will be updated. There is a print statement in loop() which will output the state of each node and its neighbors list. There are also commented out print lines which may help that are in the listener() function.

What I’m seeing wrong is, the nodes are adding the right neighbors, but the neighbors array will not add more than one ID of its neighbors. I’m just getting odd behavior.

NOTE:

I’m confused because the logic seems good, I have written other more complex algorithms similar to this and have not run in to any issues. Is there something about p5.js I don’t understand? I don’t have anyone else that I know that knows how to code so I have to post somewhere to get help.

Let me know if there’s anything I can add to clarify or help.

The only functions to look at for the logic to solve this issue would be:

  • loop()
    ***Node class functions:
  • getNeighbors()
  • broadcastPacket()
  • listener()

CODE:


////////////////////////////////////////////////////////////////////////////////
////////////////////////////// V A R I A B L E S ///////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// Setup variables
const CANVAS_WIDTH = 630;
const CANVAS_HEIGHT = 630;
const BACKGROUND_COLOR = 240;
const FPS = 60;

// Sketch variables
const NODE_SIZE = 6;
const NODE_FILL = 50;
const RADIUS_FILL = 'rgba(0, 0, 0, 0.04)';
const RADIUS_STROKE = 'rgba(0, 0, 0, 0.1)';
const NODE_EDGE_STROKE = 50;
const RADIUS_MIN = 200;
const RADIUS_MAX =300;
const NUM_NODES = 4;
const DISPLAY_RADIUS = false;
let nodes;
const TEST_RADIUS = 300;
const MONITOR_INTERVAL = 100;
let prevTimeMonitor;
const FRAMERATE_INTERVAL = 1000;
let prevTimeFramerate;
let currFrameRate;

////////////////////////////////////////////////////////////////////////////////
//////////////////////////////// C L A S S E S /////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// Node class
class Node {
  constructor(x, y, radius, id, data) {
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.id = id;
    this.data = data;
    this.neighbors = [];
    this.packets = [];
  }
  
  drawRadius() {
    stroke(RADIUS_STROKE);
    strokeWeight(1);
    fill(RADIUS_FILL);
    ellipse(this.x, this.y, this.radius * 2);
  }
  
  drawEdges() {
    fill(NODE_EDGE_STROKE);
    stroke(NODE_EDGE_STROKE);
    strokeWeight(1);
    
    for (let node of nodes) {
      if (this.id !== node.id && this.isInRadius(node) && !node.isInRadius(this)) {
        drawingContext.setLineDash([1, 4]);
        drawingContext.lineDashOffset = -frameCount;
        line(this.x, this.y, node.x, node.y);
      }
      else if (this.id !== node.id && this.isInRadius(node)) {
        drawingContext.setLineDash([]);
        line(this.x, this.y, node.x, node.y);         
      }
    }
  }
  
  drawNode() {
    noStroke();
    colorMode(RGB);
    fill(NODE_FILL);
    ellipse(this.x, this.y, NODE_SIZE, NODE_SIZE);
  }
  
  drawInfo() {
    // ID
    noStroke();
    fill(0);
    textSize(10);
    text(this.id, this.x + (NODE_SIZE / 1.5), this.y - NODE_SIZE / 1.5);
    drawingContext.setLineDash([]);
    
    stroke(0);
    
    // Data
    let SIZE = 7;
    for (let i = 0; i < NUM_NODES; i++) {
      if (this.data[i]) {
        fill(240);
        rect(this.x + textWidth(this.id) + 8 + (i * SIZE), this.y - 2 * NODE_SIZE, SIZE, SIZE);
      }
      else {
        fill(75);
        rect(this.x + textWidth(this.id) + 8 + (i * SIZE), this.y - 2 * NODE_SIZE, SIZE, SIZE);
      }
    }
  }
  
  isSameNode(id) {
    return this.id === id;
  }
  
  isInRadius(node) {
    let distance = dist(this.x, this.y, node.x, node.y);
    return distance <= this.radius;
  }
  
  getNeighbors() {
    this.neighbors = [];
    this.packets = [];
    let packet = new HelloPacket(this.id, "broadcast");
    this.broadcastPacket(packet);
  }
  
  broadcastPacket(packet) {
    for (let neighbor of nodes) {
      if (!this.isSameNode(neighbor.id) && this.isInRadius(neighbor)) {
        neighbor.packets.push(packet);
      }
    }
  }

  listener() {
    if (this.packets.length !== 0) {
      // print("Node " + this.id + ": START");
      // print(this.toString());
    }
    
    while (this.packets.length !== 0) {
      let packet = this.packets.shift();
      
      // print(packet.toString());
      
      if (packet instanceof HelloPacket) {
        if (packet.dst === "broadcast") {
          packet.dst = packet.src;
          packet.src = this.id;
          this.broadcastPacket(packet);
        } 
        else if (packet.dst === this.id && !this.neighbors.includes(packet.src)) {
          // print("Add neighbor: " + packet.src);
          this.neighbors.push(packet.src);
          // print(JSON.stringify(this.neighbors));
        }
        else {
          // print(this.toString() + `Packet dropped:\n${packet.toString()}`);
        }
      } 
      else {
        print(this.toString() + `Unknown packet type`);
      }
    }

    if (this.packets.length !== 0) {
      // print("Node " + this.id + ": END");
    }
  }
  
  toString() {
    return `Node {\n\t"id": ${this.id},\n\t"data": ${JSON.stringify(this.data)},\n\t"neighbors": ${JSON.stringify(this.neighbors)},\n\t"packets": ${JSON.stringify(this.packets)}\n}\n`;
  }
}

////////////////////////////////////////////////////////////////////////////////

class HelloPacket {
  constructor(src, dst) {
    this.src = src;
    this.dst = dst;
  }
  
  toString() {
    return `HelloPacket {\n\t"src": "${this.src}",\n\t"dst": "${this.dst}"\n}\n`;
  }
}

// class HelloPacket {
//   constructor(src, dst) {
//     this.src = src;
//     this.neighbors = neighbors;
//     this.data = data;
//   }
  
//   toString() {
//     return "{\n" +
//            "\t\"src\": " + JSON.stringify(this.src) + ",\n" +
//            "\t\"neighbors\": " + JSON.stringify(this.neighbors) + ",\n" +
//            "\t\"data\": " + JSON.stringify(this.data) + ",\n" + 
//            "}";
//   }
// }

////////////////////////////////////////////////////////////////////////////////
////////////////////////////// F U N C T I O N S ///////////////////////////////
////////////////////////////////////////////////////////////////////////////////

function updateTestNode(testNode) {
  testNode.x = mouseX;
  testNode.y = mouseY;
}

function keyPressed() {
  for (let i = 1; i <= NUM_NODES; i++) {
    if (key == i.toString()) {
      nodes[i-1].data[i-1] = !nodes[i-1].data[i-1];
    }
  }
}

function drawFPS() {  
  let fontSize = 12;
  let textX = 10;
  let textY = 20;

  // Get the current frame rate
  let fps = "FPS: " + currFrameRate;

  fill(color(150, 150, 150, 191));
  noStroke();
  textSize(12);

  let fontScale = fontSize / 12;
  let rectOffset = 2;
  let rectWidth = textWidth(fps) * fontScale + (fontScale + rectOffset);
  let rectHeight = -textAscent() * fontScale - (fontScale + rectOffset);
  let rectX = textX - (fontScale + rectOffset) / 2;
  let rectY = textY + fontScale + (fontScale + rectOffset) / 2;

  rect(rectX, rectY, rectWidth, rectHeight);

  fill(0);
  noStroke();
  textSize(fontSize);

  // Display frame rate in the top left corner
  text(fps, textX, textY);
  
}
  
function updateFPS() {
  if (millis() - prevTimeFramerate >= FRAMERATE_INTERVAL) {
    currFrameRate = int(frameRate());
    prevTimeFramerate = millis();
  }
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////// S E T U P ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

function setup() {
  createCanvas(CANVAS_WIDTH, CANVAS_HEIGHT);
  frameRate(FPS);
  ellipseMode(CENTER);
  noCursor();
  
  nodes = [];
  
  prevTimeMonitor = millis() - MONITOR_INTERVAL;
  prevTimeFramerate = millis() - FRAMERATE_INTERVAL;
  
  for (let id = 0; id < NUM_NODES-1; id++) {
    let board = [];
    for (let i = 0; i < NUM_NODES; i++) {
      board.push(false);
    }

    let x = int(random(width / 16, width - (2 * (width / 16))));
    let y = int(random(height / 16, height - (2 * (height / 16))));
    let radius = int(random(RADIUS_MIN, RADIUS_MAX));
    node = new Node(x, y, radius, id, board);
    nodes.push(node);
  }
  
  let board = [];
  for (let i = 0; i < NUM_NODES; i++) {
    board.push(false);
  }
  testNode = new Node(mouseX, mouseY, TEST_RADIUS, NUM_NODES-1, board);   
  nodes.push(testNode);
}


////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////// D R A W ////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

function draw() {
  // Draw
  colorMode(RGB);
  background(BACKGROUND_COLOR);
    
  if (DISPLAY_RADIUS) {
    for (let node of nodes) {node.drawRadius();}
  }
  for (let node of nodes) {
    node.drawEdges();
  }
  for (let node of nodes) {
    node.drawNode();
  }
  for (let node of nodes) {
    node.drawInfo();
  }
  
////////////////////////////////////////////////////////////////////////////////
  
  // Get neighbors of all nodes in the network
  if (millis() - prevTimeMonitor >= MONITOR_INTERVAL) {
    for (let node of nodes) {
      node.getNeighbors();
    }
    prevTimeMonitor = millis();
  }
  
////////////////////////////////////////////////////////////////////////////////
    
  // Process incoming packets for all nodes in the network
  for (let node of nodes) {
    node.listener();
  }
  
////////////////////////////////////////////////////////////////////////////////
  
  // Print the neighbors of all nodes
  for (let node of nodes) {
    const jsonString = JSON.stringify({ id: node.id, neighbors: node.neighbors });
    print(jsonString);
  }
  print("\n");
  
  drawFPS();
  updateFPS();
  updateTestNode(nodes[NUM_NODES-1]);
}