From edf483b51da633a87070e6f97fd338c294e38261 Mon Sep 17 00:00:00 2001 From: selankon Date: Wed, 3 Jul 2024 16:29:42 +0200 Subject: [PATCH] chore(mesh-wide): refactor to use links coordinates --- .../components/FeatureDetail/LinkDetail.tsx | 34 +++- .../src/components/Map/LinkLine.tsx | 2 + .../src/containers/Map.tsx | 8 +- .../src/hooks/useLocatedLinks.tsx | 12 +- .../src/hooks/useNodes.tsx | 12 +- .../src/lib/links/PointToPointLink.ts | 60 ++++-- .../src/lib/links/getLinksCoordinates.spec.ts | 2 + .../src/lib/links/getLinksCoordinates.ts | 180 +++++++++++++----- .../src/meshWideMocks.tsx | 2 + .../src/meshWideQueries.tsx | 19 +- .../src/meshWideTypes.tsx | 34 ++-- 11 files changed, 261 insertions(+), 104 deletions(-) diff --git a/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/LinkDetail.tsx b/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/LinkDetail.tsx index a2af2f32..c8ccc00c 100644 --- a/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/LinkDetail.tsx +++ b/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/LinkDetail.tsx @@ -1,4 +1,5 @@ import { Trans } from "@lingui/macro"; +import { useMemo } from "preact/compat"; import { useState } from "preact/hooks"; import { useCallback } from "react"; @@ -12,6 +13,7 @@ import { getQueryByLinkType, usePointToPointErrors, } from "plugins/lime-plugin-mesh-wide/src/hooks/useLocatedLinks"; +import { useNodes } from "plugins/lime-plugin-mesh-wide/src/hooks/useNodes"; import { MacToMacLink } from "plugins/lime-plugin-mesh-wide/src/lib/links/PointToPointLink"; import { readableBytes } from "plugins/lime-plugin-mesh-wide/src/lib/utils"; import { useSetLinkReferenceState } from "plugins/lime-plugin-mesh-wide/src/meshWideQueries"; @@ -227,7 +229,11 @@ export const LinkReferenceStatus = ({ reference }: LinkMapFeature) => { type: reference.type, }); - const isDown = !errors.linkUp; + const { + allNodes: { meshWideNodesReference, meshWideNodesActual }, + } = useNodes(); + + const isDown = !errors?.linkUp; // Check if there are errors of global reference state to shown const { reference: fetchDataReference } = getQueryByLinkType( @@ -246,10 +252,28 @@ export const LinkReferenceStatus = ({ reference }: LinkMapFeature) => { const { showToast } = useToast(); // Generate a list of nodes to update - const nodesToUpdate = reference.nodes.reduce((acc, node) => { - acc[node.ipv4] = node.hostname; - return acc; - }, {}); + // const nodesToUpdate = reference.nodes.reduce((acc, node) => { + // acc[node.ipv4] = node.hostname; + // return acc; + // }, {}); + + // Get nodes to update + const nodesToUpdate = useMemo(() => { + // First get an object with all nodes + const allNodes = { + ...(meshWideNodesReference || {}), + ...(meshWideNodesActual || {}), + }; + if (!allNodes) return {}; + // Then reduce the nodes to update + return reference.nodes.reduce((acc, node) => { + // If the node with node name exist get the ipv4 and hostname + if (allNodes[node]) { + acc[allNodes[node].ipv4] = allNodes[node].hostname; + } + return acc; + }, {}); + }, [meshWideNodesReference, meshWideNodesActual, reference.nodes]); // todo(kon): Sync mutations, used to sync data between nodes and local node after setting the reference state // useSharedStateSync diff --git a/plugins/lime-plugin-mesh-wide/src/components/Map/LinkLine.tsx b/plugins/lime-plugin-mesh-wide/src/components/Map/LinkLine.tsx index d2bb126d..caf35ddb 100644 --- a/plugins/lime-plugin-mesh-wide/src/components/Map/LinkLine.tsx +++ b/plugins/lime-plugin-mesh-wide/src/components/Map/LinkLine.tsx @@ -53,6 +53,8 @@ export const LinkLine = ({ referenceLink, actualLink }: ILinkLineProps) => { }; }; + if (linkToShow.hasInValidCoordinates()) return <>; + console.log("LinkToShow", linkToShow); const coordinates = linkToShow.coordinates.map((c) => [c.lat, c.long]); return ( diff --git a/plugins/lime-plugin-mesh-wide/src/containers/Map.tsx b/plugins/lime-plugin-mesh-wide/src/containers/Map.tsx index 0c87fc20..e29f6d4c 100644 --- a/plugins/lime-plugin-mesh-wide/src/containers/Map.tsx +++ b/plugins/lime-plugin-mesh-wide/src/containers/Map.tsx @@ -14,10 +14,7 @@ import { useLoadLeaflet, useLocation, } from "plugins/lime-plugin-locate/src/locateQueries"; -import { - BatmanLinksLayer, - WifiLinksLayer, -} from "plugins/lime-plugin-mesh-wide/src/containers/MapLayers/LinksLayers"; +import { WifiLinksLayer } from "plugins/lime-plugin-mesh-wide/src/containers/MapLayers/LinksLayers"; import NodesLayer from "plugins/lime-plugin-mesh-wide/src/containers/MapLayers/NodesLayer"; import { useSelectedMapFeature } from "plugins/lime-plugin-mesh-wide/src/meshWideQueries"; @@ -78,13 +75,14 @@ export const MeshWideMap = ({ } }, [loading, nodeLocation]); + // @ts-ignore const mapSupportedLayers: Record< keyof MeshWideMapTypes, { name: string; layer: ComponentChildren } > = { node_info: { name: "Nodes", layer: }, wifi_links_info: { name: "Wifi Links", layer: }, - bat_links_info: { name: "Batman", layer: }, + // bat_links_info: { name: "Batman", layer: }, }; return ( diff --git a/plugins/lime-plugin-mesh-wide/src/hooks/useLocatedLinks.tsx b/plugins/lime-plugin-mesh-wide/src/hooks/useLocatedLinks.tsx index 12894ec9..afdd5bfb 100644 --- a/plugins/lime-plugin-mesh-wide/src/hooks/useLocatedLinks.tsx +++ b/plugins/lime-plugin-mesh-wide/src/hooks/useLocatedLinks.tsx @@ -77,18 +77,18 @@ const useCalculateLocatedLinks = ({ } = useNodes(); const locatedLinksReference: LocatedLinkData = useMemo(() => { - if (meshWideNodes && linksReference) { + if (linksReference) { return mergeLinksAndCoordinates( - meshWideNodes, linksReference, - type + type, + meshWideNodes ); } - }, [meshWideNodes, linksReference, type]); + }, [linksReference, meshWideNodes, type]); const locatedLinks: LocatedLinkData = useMemo(() => { - if (links && meshWideNodes) { - return mergeLinksAndCoordinates(meshWideNodes, links, type); + if (links) { + return mergeLinksAndCoordinates(links, type, meshWideNodes); } }, [links, meshWideNodes, type]); diff --git a/plugins/lime-plugin-mesh-wide/src/hooks/useNodes.tsx b/plugins/lime-plugin-mesh-wide/src/hooks/useNodes.tsx index d1f0f8d4..e0f1f490 100644 --- a/plugins/lime-plugin-mesh-wide/src/hooks/useNodes.tsx +++ b/plugins/lime-plugin-mesh-wide/src/hooks/useNodes.tsx @@ -15,15 +15,16 @@ interface NodesContextType { meshWideNodesReference: INodes; meshWideNodesActual: INodes; }; + // Invalid nodes doesn't contain a correct lat long invalidNodes: { invalidNodesReference: INodes; invalidNodesActual: INodes; }; locatedNodes: { - locatedNodesReference: INodes; - locatedNodesActual: INodes; - allLocatedNodes: INodes; - locatedNewNodes: INodes; + locatedNodesReference: INodes; // All located reference nodes + locatedNodesActual: INodes; // Located nodes of the actual state + allLocatedNodes: INodes; // Sum of actual state and ref state + locatedNewNodes: INodes; // New nodes (not on the ref state) }; } @@ -99,7 +100,7 @@ export const NodesProvider = ({ }, {} as INodes); } - // Used to have on an a single list all the located nodes + // Used to have on a single list all the located nodes // This is used to have an easier way to draw links between nodes // that are not active, or not on reference or new const allLocatedNodes = { @@ -116,7 +117,6 @@ export const NodesProvider = ({ meshWideNodesReference, meshWideNodesActual, }, - // Invalid nodes doesn't contain a correct lat long invalidNodes: { invalidNodesReference, invalidNodesActual, diff --git a/plugins/lime-plugin-mesh-wide/src/lib/links/PointToPointLink.ts b/plugins/lime-plugin-mesh-wide/src/lib/links/PointToPointLink.ts index 853afd50..f69f1e7a 100644 --- a/plugins/lime-plugin-mesh-wide/src/lib/links/PointToPointLink.ts +++ b/plugins/lime-plugin-mesh-wide/src/lib/links/PointToPointLink.ts @@ -1,8 +1,7 @@ import { BaseMacToMacLink, Coordinates, - ILocatedLink, - INodeInfo, + IBaseLink, LinkDataTypes, LinkType, MacToMacLinkId, @@ -16,19 +15,26 @@ import { */ export class PontToPointLink { private _links: BaseMacToMacLink[] = []; - private _nodes: INodeInfo[] = []; + // private _nodes: INodeInfo[] = []; public readonly id: PointToPointLinkId; - public readonly coordinates: Coordinates[] = []; - - constructor(node1: INodeInfo, node2: INodeInfo) { - const coord1 = node1.coordinates; - const coord2 = node2.coordinates; + public readonly coordinates: Array = []; + private _nodes: Set = new Set([]); + + // constructor(node1: INodeInfo, node2: INodeInfo) { + constructor( + coord1?: Coordinates | undefined, + coord2?: Coordinates | undefined + ) { + // const coord1 = node1.coordinates; + // const coord2 = node2.coordinates; this.id = PontToPointLink.generateId(coord1, coord2); - this.nodes.push(node1, node2); + // this.nodes.push(node1, node2); this.coordinates.push(coord1, coord2); } addLink(link: typeof this._links[number]) { + console.log("addLink INNER", link); + this.links.push(link); } @@ -48,23 +54,31 @@ export class PontToPointLink { } get nodes() { - return this._nodes; + return [...this._nodes]; + } + + addNodes(nodes: string[]) { + nodes.forEach((node) => { + this._nodes.add(node); + }); } /** * Generate a deterministic unique id based on the coordinates of a node. + * + * It accepts undefined coordinets for those links which don't have all the information yet. * @param coord1 * @param coord2 */ - static generateId(coord1: Coordinates, coord2: Coordinates): string { + static generateId(coord1?: Coordinates, coord2?: Coordinates): string { const _prepareCoord = (coord: string) => parseFloat(coord.replace("-", "").replace(".", "")); const allCoordinates = [ - _prepareCoord(coord1.long), - _prepareCoord(coord1.lat), - _prepareCoord(coord2.long), - _prepareCoord(coord2.lat), + _prepareCoord(coord1?.long ?? "0"), + _prepareCoord(coord1?.lat ?? "0"), + _prepareCoord(coord2?.long ?? "0"), + _prepareCoord(coord2?.lat ?? "0"), ]; return allCoordinates.sort((a, b) => a - b).toString(); @@ -73,17 +87,29 @@ export class PontToPointLink { get type(): LinkType { return this._links[0].type; } + + /** + * Return true if coordinates are not undefined + */ + hasInValidCoordinates = () => { + return this.coordinates.some((coord) => coord === undefined); + }; + + // // todo(kon): do this on a best way + // hasValidCoordinates = () => { + // this.coordinates.any((coord) => coord !== undefined); + // }; } /** * Store link info between two macs */ export class MacToMacLink { - private _data: ILocatedLink; + private _data: IBaseLink; private _id: MacToMacLinkId; public type: T; - constructor(id: MacToMacLinkId, data: ILocatedLink, type: T) { + constructor(id: MacToMacLinkId, data: IBaseLink, type: T) { this._data = data; this._id = id; this.type = type; diff --git a/plugins/lime-plugin-mesh-wide/src/lib/links/getLinksCoordinates.spec.ts b/plugins/lime-plugin-mesh-wide/src/lib/links/getLinksCoordinates.spec.ts index fad847d3..d38c819c 100644 --- a/plugins/lime-plugin-mesh-wide/src/lib/links/getLinksCoordinates.spec.ts +++ b/plugins/lime-plugin-mesh-wide/src/lib/links/getLinksCoordinates.spec.ts @@ -13,6 +13,7 @@ describe("tests for the algorithm that merge point and links data types", () => const locatedLinksReference = mergeLinksAndCoordinates( nodesReferenceState, linksReferenceState, + // @ts-ignore "wifi_links_info" ); // Iterate between merged link objects @@ -21,6 +22,7 @@ describe("tests for the algorithm that merge point and links data types", () => for (const link of merged.links) { Object.entries(link.data).map(([name, linkData], i) => { const node = nodesReferenceState[name]; + // @ts-ignore expect(linkData.coordinates).toBe(node.coordinates); }); } diff --git a/plugins/lime-plugin-mesh-wide/src/lib/links/getLinksCoordinates.ts b/plugins/lime-plugin-mesh-wide/src/lib/links/getLinksCoordinates.ts index 0ec6cf97..9fc1157a 100644 --- a/plugins/lime-plugin-mesh-wide/src/lib/links/getLinksCoordinates.ts +++ b/plugins/lime-plugin-mesh-wide/src/lib/links/getLinksCoordinates.ts @@ -3,8 +3,8 @@ import { PontToPointLink, } from "plugins/lime-plugin-mesh-wide/src/lib/links/PointToPointLink"; import { + IBaseLink, ILinks, - ILocatedLink, INodes, LinkType, LocatedLinkData, @@ -13,79 +13,159 @@ import { import { isEmpty } from "utils/utils"; export const mergeLinksAndCoordinates = ( - nodes: INodes, links: ILinks, - type: T + type: T, + nodes?: INodes ): LocatedLinkData => { - if (!nodes || isEmpty(nodes) || !links || isEmpty(links)) return {}; + // if (!nodes || isEmpty(nodes) || !links || isEmpty(links)) return {}; + if (!links || isEmpty(links)) return {}; const result: LocatedLinkData = {}; + console.log("--------------------"); + console.log("LINKS & NODES", links, nodes); // for every node check all links - for (const linkNodeName in links) { - if (isEmpty(links[linkNodeName])) continue; - for (const [linkKey, linkData] of Object.entries(links[linkNodeName])) { - if (!linkData.dst_mac) continue; + for (const actualNodeName in links) { + if ( + isEmpty(links[actualNodeName]) || + typeof links[actualNodeName].links !== "object" // todo(kon): this is an error from the backend + ) + continue; + const srcLoc = links[actualNodeName].src_loc; + for (const [linkKey, linkData] of Object.entries( + links[actualNodeName].links + )) { + // If destination mac or locations are not defined, ignore the link + // todo(kon): what happen if the destination mac or location is undefined? + // if (!linkData.dst_mac || !linkData.dst_loc) continue; + // Get the nodeName of the destination node - const dstNodeName = Object.keys(nodes).find((pid) => { - return nodes[pid].macs.find( - (mac) => - mac.toLowerCase() === linkData.dst_mac.toLowerCase() - ); - }); + // const dstNodeName = Object.keys(nodes).find((pid) => { + // return nodes[pid].macs.find( + // (mac) => + // mac.toLowerCase() === linkData.dst_mac.toLowerCase() + // ); + // }); // Is possible that the destination node is not on the list of links, // just ignore it - if (!dstNodeName || !links[dstNodeName]) continue; + // if (!dstNodeName || !links[dstNodeName]) continue; // If the destination node is not the same as the link node // and the destination node is on the list of nodes // and the link is not already added - if ( - dstNodeName && - dstNodeName !== linkNodeName && - nodes[linkNodeName] - ) { - // Generate a unique id of the point to point link based on the coordinates to check if already exists - const geoLinkKey = PontToPointLink.generateId( - nodes[linkNodeName].coordinates, - nodes[dstNodeName].coordinates + // if ( + // dstNodeName && + // dstNodeName !== linkNodeName && + // nodes[linkNodeName] + // ) { + // Generate a unique id of the point to point link based on the coordinates to check if already exists + // const geoLinkKey = PontToPointLink.generateId( + // nodes[linkNodeName].coordinates, + // nodes[dstNodeName].coordinates + // ); + + // If this point to point link no exists, instantiate it + // if (!result[geoLinkKey]) { + // // result[geoLinkKey] = new PontToPointLink( + // // nodes[linkNodeName], + // // nodes[dstNodeName] + // // ); + // } + + // Find destination link info from shared state + let dest: IBaseLink; + for (const destNodeKey in links) { + const link = Object.entries(links[destNodeKey].links).find( + ([key]) => key === linkKey && destNodeKey !== actualNodeName ); + if (link) { + dest = { [destNodeKey]: link[1] }; + } + } - // If this point to point link no exists, instantiate it - if (!result[geoLinkKey]) { - result[geoLinkKey] = new PontToPointLink( - nodes[linkNodeName], - nodes[dstNodeName] + let destLoc = linkData?.dst_loc; + // If destination coords are undefined, try to find it on other ways. + if (!destLoc) { + console.log("Destination loc not found"); + if (dest && links[Object.keys(dest)[0]].src_loc) { + // If we have destination link info, try to find the src_loc + destLoc = links[Object.keys(dest)[0]].src_loc; + console.log( + "Destination loc found via dest link mac", + dest ); + } else { + // Find the destination MAC between existing located nodes to get the position + const dstNode = Object.values(nodes).find((node) => { + return node.macs.find((mac) => { + console.log("NININNINININNI", linkData); + return ( + mac.toLowerCase() === + linkData.dst_mac.toLowerCase() + ); + }); + }); + if (dstNode) { + destLoc = dstNode.coordinates; + console.log("Destination loc found via dest node"); + } } - // If the link PontToPointLink already exists and the link is already added, ignore it - else if ( - result[geoLinkKey].linkExists(linkKey) || - !links[dstNodeName] - ) { - continue; - } + } - // Find destination link info from shared state - const destLinkData = links[dstNodeName][linkKey]; + // todo(kon): what happen if the destination link or location is undefined? + // Maybe drawing somehow to the map and show the user that the link is not complete + // For the moment lets ignore the link + if (!destLoc) { + console.log("LINK NOT SHOWN, NOT ENUGH INFO", dest, destLoc); + continue; + } - const entry = { - [linkNodeName]: { - ...linkData, - coordinates: nodes[linkNodeName].coordinates, + if (!dest) { + dest = { + node_down: { + dst_loc: destLoc, }, - [dstNodeName]: { - ...destLinkData, - coordinates: nodes[dstNodeName].coordinates, - }, - } as ILocatedLink; + } as IBaseLink; + } - result[geoLinkKey].addLink( - new MacToMacLink(linkKey, entry, type) - ); + // Get Geolink key to check if is already added + const geoLinkKey = PontToPointLink.generateId(srcLoc, destLoc); + + // If the link PontToPointLink already exists and the link is already added, ignore it + if (result[geoLinkKey] && result[geoLinkKey].linkExists(linkKey)) { + continue; } + + console.log("NEW GEOLINK", geoLinkKey, linkData); + // Instantiate new point to point link on the results array + if (!result[geoLinkKey]) { + result[geoLinkKey] = new PontToPointLink(srcLoc, destLoc); + } + + // Add node names to the link data + result[geoLinkKey].addNodes([actualNodeName, ...Object.keys(dest)]); + + // const entry = { + // [linkNodeName]: { + // ...linkData, + // coordinates: nodes[linkNodeName].coordinates, + // }, + // [dstNodeName]: { + // ...destLinkData, + // coordinates: nodes[dstNodeName].coordinates, + // }, + // } as IBaseLink; + const entry = { + [actualNodeName]: { + ...linkData, + }, + ...dest, + } as IBaseLink; + + result[geoLinkKey].addLink(new MacToMacLink(linkKey, entry, type)); } + // } } return result; diff --git a/plugins/lime-plugin-mesh-wide/src/meshWideMocks.tsx b/plugins/lime-plugin-mesh-wide/src/meshWideMocks.tsx index f9c9959b..dff57625 100644 --- a/plugins/lime-plugin-mesh-wide/src/meshWideMocks.tsx +++ b/plugins/lime-plugin-mesh-wide/src/meshWideMocks.tsx @@ -99,6 +99,7 @@ const newNode = { }, }; +// @ts-ignore export const linksReferenceState: IWifiLinks = { primero: { A0F3C1462897a840411df935: { @@ -219,6 +220,7 @@ export const linksReferenceState: IWifiLinks = { } as { [linkKey: MacToMacLinkId]: IWifiLinkData }, } as ILinks<"wifi_links_info">; +// @ts-ignore export const batManReferenceState: IBatmanLinks = { primero: { "02dbd646289502dbd6da4eaa": { diff --git a/plugins/lime-plugin-mesh-wide/src/meshWideQueries.tsx b/plugins/lime-plugin-mesh-wide/src/meshWideQueries.tsx index c279b409..16ea350f 100644 --- a/plugins/lime-plugin-mesh-wide/src/meshWideQueries.tsx +++ b/plugins/lime-plugin-mesh-wide/src/meshWideQueries.tsx @@ -10,7 +10,9 @@ import { getQueryByLinkType } from "plugins/lime-plugin-mesh-wide/src/hooks/useL import { PontToPointLink } from "plugins/lime-plugin-mesh-wide/src/lib/links/PointToPointLink"; import { getMeshWideConfig } from "plugins/lime-plugin-mesh-wide/src/meshWideMocks"; import { + IBaseLink, IBatmanLinks, + ILinks, IMeshWideConfig, INodes, IWifiLinks, @@ -165,21 +167,32 @@ export const useSetLinkReferenceState = ({ mutationFn: ({ ip }) => { const hostname = nodesToUpdate[ip]; - const newReferenceLinks = referenceData[hostname] ?? {}; + let newReferenceLinks = (referenceData[hostname] ?? + {}) as IBaseLink; + // todo(kon): this is a hotfix because backend returns an empty string somtimes + if (typeof newReferenceLinks !== "object") newReferenceLinks = {}; + for (const mactomac of linkToUpdate.links) { if (isDown) { delete newReferenceLinks[mactomac.id]; continue; } - newReferenceLinks[mactomac.id] = data[hostname][mactomac.id]; + newReferenceLinks[mactomac.id] = + data[hostname].links[mactomac.id]; } + const queryKey = sharedStateQueries.insertIntoReferenceState( linkType, // For some reason I have to ignore the types here because it not infers properly. // Using the same code but for a specific link type, it works. // For some reason with the use of getQueryByLinkType it doesn't work. // @ts-ignore - { [hostname]: newReferenceLinks } + { + [hostname]: { + links: newReferenceLinks, + src_loc: data[hostname].src_loc, + }, + } as ILinks ); return doSharedStateApiCall(queryKey, ip); }, diff --git a/plugins/lime-plugin-mesh-wide/src/meshWideTypes.tsx b/plugins/lime-plugin-mesh-wide/src/meshWideTypes.tsx index 0a1cbc02..5f31ac87 100644 --- a/plugins/lime-plugin-mesh-wide/src/meshWideTypes.tsx +++ b/plugins/lime-plugin-mesh-wide/src/meshWideTypes.tsx @@ -20,13 +20,18 @@ export type LinkDataTypes = { }; export type LinkType = keyof LinkDataTypes; export type IBaseLink = { - [linkKey: string]: LinkDataTypes[T]; -}; -export type ILocatedLink = IBaseLink & { - [key: string]: { - coordinates: Coordinates; + [linkKey: string]: LinkDataTypes[T] & { + dst_mac: string; + src_mac: string; + dst_loc?: Coordinates; }; }; +// export type ILocatedLink = IBaseLink & { +// [key: string]: { +// src_loc: Coordinates; +// dst_loc?: Coordinates; +// }; +// }; export type BaseMacToMacLink = MacToMacLink; @@ -41,10 +46,12 @@ export type LocatedLinkData = { [key: string]: PontToPointLink; }; -type MacPair = { - dst_mac: string; - src_mac: string; -}; +// type LinkCommon = { +// dst_mac: string; +// src_mac: string; +// src_loc: Coordinates; +// dst_loc?: Coordinates; +// }; /** * Link info retrieved from the API with the wifi data @@ -55,7 +62,7 @@ export type IWifiLinkData = { signal: number; rx_rate: number; channel: number; -} & MacPair; +}; /** * Link info retrieved from the API with the batman data @@ -64,13 +71,16 @@ export type IBatManLinkData = { hard_ifindex: number; last_seen_msecs: number; iface: string; -} & MacPair; +}; /** * List of Link info retrieved from the API */ export interface ILinks { - [nodeKey: string]: IBaseLink; + [nodeKey: string]: { + src_loc: Coordinates; + links: IBaseLink; + }; } export type IWifiLinks = ILinks<"wifi_links_info">;