diff --git a/docker/grafana/Dockerfile.grafana b/docker/grafana/Dockerfile.grafana index ff2c3a9..00cf957 100644 --- a/docker/grafana/Dockerfile.grafana +++ b/docker/grafana/Dockerfile.grafana @@ -1,7 +1,4 @@ FROM grafana/grafana-oss:10.4.2 -# Install the Redis datasource plugin -RUN grafana-cli plugins install redis-datasource - # Copy the datasource configuration COPY docker/grafana/datasources.yml /etc/grafana/provisioning/datasources/datasources.yml \ No newline at end of file diff --git a/docker/grafana/datasources.yml b/docker/grafana/datasources.yml index 35bd6dc..19d426b 100644 --- a/docker/grafana/datasources.yml +++ b/docker/grafana/datasources.yml @@ -7,4 +7,16 @@ datasources: isDefault: true editable: true jsonData: - httpMethod: POST \ No newline at end of file + httpMethod: POST + - name: postgres + type: postgres + access: proxy + url: postgres:5432 + jsonData: + database: meshtastic + sslmode: "disable" + user: postgres + secureJsonData: + password: postgres + isDefault: false + editable: true \ No newline at end of file diff --git a/docker/postgres/init.sql b/docker/postgres/init.sql index 92321fb..7d16017 100644 --- a/docker/postgres/init.sql +++ b/docker/postgres/init.sql @@ -28,3 +28,27 @@ CREATE TABLE IF NOT EXISTS client_details role VARCHAR, mqtt_status VARCHAR default 'none' ); + +CREATE TABLE IF NOT EXISTS node_graph +( + node_id VARCHAR PRIMARY KEY, + last_sent_by_node_id VARCHAR, + last_sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + broadcast_interval_secs INTEGER, + FOREIGN KEY (node_id) REFERENCES client_details (node_id) +); + +CREATE TABLE IF NOT EXISTS node_neighbors +( + id SERIAL PRIMARY KEY, + node_id VARCHAR, + neighbor_id VARCHAR, + snr FLOAT, + FOREIGN KEY (node_id) REFERENCES client_details (node_id), + FOREIGN KEY (neighbor_id) REFERENCES node_graph (node_id), + UNIQUE (node_id, neighbor_id) +); + +CREATE INDEX idx_node_neighbors_node_id ON node_neighbors (node_id); +CREATE INDEX idx_node_neighbors_neighbor_id ON node_neighbors (neighbor_id); +CREATE UNIQUE INDEX idx_unique_node_neighbor ON node_neighbors (node_id, neighbor_id); diff --git a/exporter/processors.py b/exporter/processors.py index a1ead5a..21d29d5 100644 --- a/exporter/processors.py +++ b/exporter/processors.py @@ -472,7 +472,47 @@ class NeighborInfoAppProcessor(Processor): logger.debug("Received NEIGHBORINFO_APP packet") neighbor_info = NeighborInfo() neighbor_info.ParseFromString(payload) - pass + self.update_node_graph(neighbor_info, client_details) + self.update_node_neighbors(neighbor_info, client_details) + + def update_node_graph(self, neighbor_info: NeighborInfo, client_details: ClientDetails): + def operation(cur, conn): + cur.execute(""" + INSERT INTO node_graph (node_id, last_sent_by_node_id, broadcast_interval_secs) + VALUES (%s, %s, %s) + ON CONFLICT (node_id) + DO UPDATE SET + last_sent_by_node_id = EXCLUDED.last_sent_by_node_id, + broadcast_interval_secs = EXCLUDED.broadcast_interval_secs, + last_sent_at = CURRENT_TIMESTAMP + """, (client_details.node_id, neighbor_info.last_sent_by_id, neighbor_info.node_broadcast_interval_secs)) + conn.commit() + + self.execute_db_operation(operation) + + def update_node_neighbors(self, neighbor_info: NeighborInfo, client_details: ClientDetails): + def operation(cur, conn): + new_neighbor_ids = [str(neighbor.node_id) for neighbor in neighbor_info.neighbors] + if new_neighbor_ids: + placeholders = ','.join(['%s'] * len(new_neighbor_ids)) + cur.execute(f""" + DELETE FROM node_neighbors + WHERE node_id = %s AND neighbor_id NOT IN ({placeholders}) + """, (client_details.node_id, *new_neighbor_ids)) + else: + cur.execute("DELETE FROM node_neighbors WHERE node_id = %s", (client_details.node_id,)) + + for neighbor in neighbor_info.neighbors: + cur.execute(""" + INSERT INTO node_neighbors (node_id, neighbor_id, snr) + VALUES (%s, %s, %s) + ON CONFLICT (node_id, neighbor_id) + DO UPDATE SET snr = EXCLUDED.snr + """, (client_details.node_id, str(neighbor.node_id), float(neighbor.snr))) + + conn.commit() + + self.execute_db_operation(operation) @ProcessorRegistry.register_processor(PortNum.ATAK_PLUGIN)