Files
map/webapp/frontend/src/composables/useNodeProcessor.js
2025-04-17 18:45:15 -04:00

117 lines
3.8 KiB
JavaScript

import { useMapStore } from '@/stores/mapStore';
import moment from 'moment';
import { nodesMaxAge, nodesOfflineAge } from '@/config'; // TODO: use config store
import { icons } from '@/map';
import { hasNodeUplinkedToMqttRecently, isValidCoordinates } from '@/utils';
export function useNodeProcessor() {
const mapStore = useMapStore(); // Access your mapStore from Pinia
// This function processes new node data
const processNewNodes = (newNodes) => {
const now = moment();
const processedNodes = [];
const processedMarkers = {};
for (const node of newNodes) {
// Skip nodes older than configured node max age
if (nodesMaxAge.value) {
const lastUpdatedAgeInMillis = now.diff(moment(node.updated_at));
if (lastUpdatedAgeInMillis > nodesMaxAge.value * 1000) {
continue;
}
}
// Compute background/text colors beforehand
node.backgroundColor = getNodeColor(node.node_id) || '#888'; // Default to '#888' if undefined
node.textColor = getNodeTextColor(node.node_id) || '#FFFFFF'; // Default to white if undefined
// Add node to the processed list regardless of its position
processedNodes.push(node);
// Skip nodes with invalid or missing position
if (!node.latitude || !node.longitude || isNaN(node.latitude) || isNaN(node.longitude)) {
continue;
}
// Fix latitude and longitude
node.latitude = node.latitude / 10000000;
node.longitude = node.longitude / 10000000;
// Icon based on MQTT connection state
let icon = icons.mqttDisconnected;
// Use offline icon for nodes older than configured node offline age
if (nodesOfflineAge.value) {
const lastUpdatedAgeInMillis = now.diff(moment(node.updated_at));
if (lastUpdatedAgeInMillis > nodesOfflineAge.value * 1000) {
icon = icons.offline;
}
}
// Check if node has uplinked to MQTT recently
if (hasNodeUplinkedToMqttRecently(node)) {
icon = icons.mqttDisconnected;
}
// Filter invalid coordinates
if (!isValidCoordinates(node.latitude, node.longitude)) {
continue;
}
// Prepare marker data
const marker = {
type: 'Feature',
properties: {
id: node.node_id,
role: node.role_name,
layer: 'nodes',
color: icon,
},
geometry: {
type: 'Point',
coordinates: [node.longitude, node.latitude],
},
};
// Add the marker to the processed markers dictionary (using node_id as the key)
processedMarkers[node.node_id] = marker;
}
// Return processed data (nodes and markers)
return { processedNodes, processedMarkers };
};
// Process new data and store it (bulk processing)
const parseNodesResponse = (newNodes) => {
const { processedNodes, processedMarkers } = processNewNodes(newNodes);
// Clear old data and update in bulk
mapStore.clearNodes();
mapStore.setNodes(processedNodes);
mapStore.setNodeMarkers(processedMarkers);
};
return {
parseNodesResponse,
};
};
// convert node id to a hex colour
function getNodeColor(nodeId) {
return "#" + (nodeId & 0x00FFFFFF).toString(16).padStart(6, '0');
};
function getNodeTextColor(nodeId) {
// extract rgb components
const r = (nodeId & 0xFF0000) >> 16;
const g = (nodeId & 0x00FF00) >> 8;
const b = nodeId & 0x0000FF;
// calculate brightness
const brightness = ((r * 0.299) + (g * 0.587) + (b * 0.114)) / 255;
// determine text color based on brightness
return brightness > 0.5 ? "#000000" : "#FFFFFF";
};