Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FR-310 lsp count #1605

Merged
merged 5 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions packages/frinx-device-topology/src/__generated__/graphql.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { assert, describe, expect, test } from 'vitest';
import { getDistanceBetweenPoints, getPointOnCircle, getPointOnSlope } from './graph.helpers';
import { getDistanceBetweenPoints, getPointAtLength, getPointOnCircle, getPointOnSlope } from './graph.helpers';

describe('graph helpers', () => {
test('test edge curve position 0 degrees', () => {
Expand Down Expand Up @@ -56,4 +56,19 @@ describe('graph helpers', () => {
const distance = getDistanceBetweenPoints(p1, p2);
expect(distance).toBeCloseTo(1.414);
});
test('test get point at length', () => {
const source = { x: 5, y: 5 };
const target = { x: 3, y: 3 };
const { x: x1, y: y1 } = getPointAtLength({ start: source, end: target }, 0.5);
expect(x1).toBeCloseTo(4);
expect(y1).toBeCloseTo(4);

const { x: x2, y: y2 } = getPointAtLength({ start: source, end: target }, 0.1);
expect(x2).toBeCloseTo(4.8);
expect(y2).toBeCloseTo(4.8);

const { x: x3, y: y3 } = getPointAtLength({ start: source, end: target }, 0.9);
expect(x3).toBeCloseTo(3.2);
expect(y3).toBeCloseTo(3.2);
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import unwrap from '@frinx/shared/src/helpers/unwrap';
import { GraphEdgeWithDiff } from '../../helpers/topology-helpers';
import { DeviceSize } from '../../__generated__/graphql';
import { DeviceSize, MplsLspCountItem } from '../../__generated__/graphql';

export const width = 1248;
export const height = 600;
Expand Down Expand Up @@ -197,6 +197,12 @@ export type GraphMplsNodeInterface = {
status: string;
};

export type LspCount = {
deviceName: string;
incomingLsps: number;
outcomingLsps: number;
};

export const NODE_CIRCLE_RADIUS = 30;

export type PositionsMap = {
Expand Down Expand Up @@ -490,3 +496,23 @@ export function normalizeNodeInterfaceData<
...details,
};
}

export function getLspCounts(input: MplsLspCountItem): LspCount {
return {
deviceName: input.target ?? '',
incomingLsps: input.incomingLsps ?? 0,
outcomingLsps: input.outcomingLsps ?? 0,
};
}

// distance is number between 0-1 - thats ratio distance/length
export function getPointAtLength(line: Line, distance: number): Position {
const { start, end } = line;
const length = Math.sqrt((end.x - start.x) ** 2 + (end.y - start.y) ** 2);
const x = start.x + ((distance * length) / length) * (end.x - start.x);
const y = start.y + ((distance * length) / length) * (end.y - start.y);
return {
x,
y,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { chakra } from '@chakra-ui/react';
import React, { useRef, VoidFunctionComponent } from 'react';
import { getCurvePath, getPointAtLength, Line, LspCount } from '../graph.helpers';

const G = chakra('g');
const Circle = chakra('circle');
const Text = chakra('text');

type Props = {
lspCount: LspCount;
linePoints: Line;
};

const LspCountItem: VoidFunctionComponent<Props> = ({ linePoints, lspCount }) => {
const edgeRef = useRef<SVGPathElement>(null);

const { start, end } = linePoints;

const incomingPosition = getPointAtLength(linePoints, 0.3);
const outcomingPosition = getPointAtLength(linePoints, 0.7);

return (
<g>
<path
ref={edgeRef}
strokeWidth={3}
stroke="red"
strokeLinejoin="round"
fill="red"
d={getCurvePath(start, end, [])}
cursor="pointer"
/>
<G transform={`translate3d(${incomingPosition.x}px, ${incomingPosition.y}px, 0)`} transformOrigin="center center">
<G transform="translate3d(-18px, -18px, 0) scale(1.5)">
<path
d="M11 9L8 12M8 12L11 15M8 12H16M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z"
stroke="#000000"
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
fill="#fff"
/>
</G>
<Circle r="12" fillOpacity={0}>
<title>incoming Laps: {lspCount.incomingLsps}</title>
</Circle>
<Text
height="sm"
textAnchor="middle"
y="-20"
paintOrder="stroke"
strokeWidth={3}
stroke="white"
strokeLinecap="butt"
strokeLinejoin="round"
>
{lspCount.incomingLsps}
</Text>
</G>
<G
transform={`translate3d(${outcomingPosition.x}px, ${outcomingPosition.y}px, 0)`}
transformOrigin="center center"
>
<G transform="translate3d(-18px, -18px, 0) scale(1.5)">
<path
d="M17 12H7M17 12L13 16M17 12L13 8M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z"
stroke="#000000"
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
fill="#fff"
/>
</G>
<Circle r="12" fillOpacity={0}>
<title>outcoming Laps: {lspCount.outcomingLsps}</title>
</Circle>
<Text
height="sm"
textAnchor="middle"
y="-20"
paintOrder="stroke"
strokeWidth={2}
stroke="white"
strokeLinecap="butt"
strokeLinejoin="round"
>
{lspCount.outcomingLsps}
</Text>
</G>
</g>
);
};

export default LspCountItem;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { VoidFunctionComponent } from 'react';
import { GraphEdgeWithDiff } from '../../../helpers/topology-helpers';
import { useStateContext } from '../../../state.provider';
import { getLinePoints, getNameFromNode, isTargetingActiveNode, LspCount } from '../graph.helpers';
import LspCountItem from './lsp-count-item';

type Props = {
edges: GraphEdgeWithDiff[];
lspCounts: LspCount[];
};

const LspCounts: VoidFunctionComponent<Props> = ({ edges, lspCounts }) => {
const { state } = useStateContext();
const {
connectedNodeIds,
selectedNode,
mplsNodePositions: nodePositions,
mplsInterfaceGroupPositions: interfaceGroupPositions,
} = state;

const activeEdges = edges.filter((e) =>
isTargetingActiveNode(e, getNameFromNode(selectedNode), interfaceGroupPositions),
);

const lspCountsMap = new Map(lspCounts.map((c) => [c.deviceName, c]));

return (
<g>
{activeEdges.map((edge) => {
const linePoints = getLinePoints({
edge,
connectedNodeIds,
nodePositions,
interfaceGroupPositions,
});

const lspCount = lspCountsMap.get(edge.source.nodeId) ?? null;
if (!linePoints || !lspCount) {
return null;
}

return <LspCountItem key={`lsp-count-item-${edge.id}`} linePoints={linePoints} lspCount={lspCount} />;
})}
</g>
);
};

export default LspCounts;
Original file line number Diff line number Diff line change
@@ -1,38 +1,25 @@
import { Box, Button, Text } from '@chakra-ui/react';
import { Box } from '@chakra-ui/react';
import { unwrap } from '@frinx/shared';
import React, { useRef, VoidFunctionComponent } from 'react';
import { clearGmPathSearch, setSelectedNode, updateMplsNodePosition } from '../../../state.actions';
import { setSelectedNode, updateMplsNodePosition } from '../../../state.actions';
import { useStateContext } from '../../../state.provider';
import Edges from './mpls-edges';
import { height, Position, width, MplsGraphNode } from '../graph.helpers';
import BackgroundSvg from '../img/background.svg';
import MplsNodes from './mpls-nodes';
import MplsInfoPanel from './mpls-info-panel';
import { getGmPathHopsCount } from '../../../helpers/topology-helpers';
import LspCounts from './lsp-counts';

type Props = {
isGrandMasterPathFetching: boolean;
onNodePositionUpdate: (positions: { deviceName: string; position: Position }[]) => Promise<void>;
onGrandMasterPathSearch: (nodeIds: string[]) => void;
};

const MplsTopologyGraph: VoidFunctionComponent<Props> = ({
isGrandMasterPathFetching,
onNodePositionUpdate,
onGrandMasterPathSearch,
}) => {
const MplsTopologyGraph: VoidFunctionComponent<Props> = ({ onNodePositionUpdate }) => {
const { state, dispatch } = useStateContext();
const lastPositionRef = useRef<{ deviceName: string; position: Position } | null>(null);
const positionListRef = useRef<{ deviceName: string; position: Position }[]>([]);
const timeoutRef = useRef<number>();
const {
mplsEdges: edges,
mplsNodes: nodes,
gmPathIds,
selectedNode,
unconfirmedSelectedNodeIds,
unconfirmedSelectedGmPathNodeId,
} = state;
const { mplsEdges: edges, mplsNodes: nodes, selectedNode, lspCounts } = state;

const handleNodePositionUpdate = (deviceName: string, position: Position) => {
if (timeoutRef.current != null) {
Expand Down Expand Up @@ -62,14 +49,6 @@ const MplsTopologyGraph: VoidFunctionComponent<Props> = ({
dispatch(setSelectedNode(null));
};

const handleClearGmPath = () => {
dispatch(clearGmPathSearch());
};

const handleSearchClick = () => {
onGrandMasterPathSearch(unconfirmedSelectedNodeIds);
};

return (
<Box background="white" borderRadius="md" position="relative" backgroundImage={`url(${BackgroundSvg})`}>
<svg width={width} height={height} viewBox={`0 0 ${width} ${height}`}>
Expand All @@ -79,23 +58,9 @@ const MplsTopologyGraph: VoidFunctionComponent<Props> = ({
onNodePositionUpdate={handleNodePositionUpdate}
onNodePositionUpdateFinish={handleNodePositionUpdateFinish}
/>
{selectedNode && <LspCounts edges={edges} lspCounts={lspCounts} />}
</svg>
{selectedNode != null && <MplsInfoPanel node={selectedNode as MplsGraphNode} onClose={handleInfoPanelClose} />}
{unconfirmedSelectedGmPathNodeId && (
<Box position="absolute" top={2} left="2" background="transparent">
<Box display="flex" alignItems="center">
<Button onClick={handleClearGmPath} marginRight={2}>
Clear GM path
</Button>
<Button onClick={handleSearchClick} isDisabled={isGrandMasterPathFetching} marginRight={2}>
Find GM path
</Button>
{gmPathIds.length > 0 && (
<Text fontWeight="600">Number of hops: {getGmPathHopsCount(gmPathIds, 'SynceDevice')}</Text>
)}
</Box>
</Box>
)}
</Box>
);
};
Expand Down
Loading