387 lines
No EOL
24 KiB
Markdown
387 lines
No EOL
24 KiB
Markdown
# Описание работы c сетевыми интерфейсами
|
||
|
||
На данный момент главным рабочим модулем приложения является модуль работы с сетевыми интерфейсами. Данный модуль позволяет отображать сетевые интерфейсы хоста в графическом виде и управлять IP адресами интерфейсов. В основе работы модуля лежит достаточно большая библиотека ReactFlow, которая позволяет строить различные графы по средством блоков и связей между ними. Далее для удобства блоки будут называться нодами (node), связи эджами (edge), а само поле ReactFlow холстом
|
||
|
||
### 1\. Подключение модуля в рабочее пространство
|
||
|
||
Работа модуля начинается с файла /src/components/workSpace/workspace.tsx, здесь в рабочее пространство приложения подключаются сами модули.
|
||
|
||
```
|
||
{workModule === workSpaceNames[3] &&
|
||
<WorkSection>
|
||
<Header />
|
||
<ReactFlowProvider>
|
||
<NetworkInterfaces
|
||
key={currentWs}
|
||
startConnection={true}
|
||
ws={currentWs}
|
||
/>
|
||
</ReactFlowProvider>
|
||
</WorkSection>
|
||
}
|
||
```
|
||
|
||
В данном блоке этого файла подключается модуль сетевых интерфейсов в рабочее пространство. Из localstorage берётся название текущего модуля, который должен быть подключён, в этом случаи это ‘networkInterfaces‘ и по названию определяется необходимый модуль.
|
||
|
||
Здесь
|
||
|
||
<Header/> - это верхняя панель для управления холстом
|
||
|
||
<NetWorkInterfaces/> - Сам компонент работы с сетевыми интерфейсами, в нём пропсы key - ключ компонента, будет равен текущему ip хоста, startConnection - флаг, при передачи которого начинается работа компонента, ws - передача ip текущего хоста для создания WebSocket подключения
|
||
|
||
### 2\. Компонент NetworkInterfaces
|
||
|
||
Рассмотрение компонента логично начать с обзора его переменных состояния, которых в этом компоненте присутствует немало.
|
||
|
||
#### 2.1. Переменные состояния компонента NetworkInterfaces
|
||
|
||
1. initializationAttempt
|
||
- Тип: React.useRef<number>
|
||
- Описание: Счетчик попыток инициализации данных, хранящийся в useRef. Используется для отслеживания статуса начальной загрузки нод и эджей. Начальное значение — 0. После успешной инициализации устанавливается в 1. Позволяет избежать повторных попыток инициализации или перезагрузки страницы при сбоях.
|
||
2. requestError
|
||
- Тип: { status: boolean, message: string }
|
||
- Описание: Состояние для отслеживания ошибок при установлении WebSocket-соединения или загрузке данных. Содержит флаг status (true, если ошибка произошла) и поле message с текстом ошибки (например, "Соединение с сервером разорвано" или "Превышено время ожидания"). Используется для отображения ошибок через модальное окно.
|
||
3. { openModal, closeLoader }
|
||
- Тип: Методы из useModalContext
|
||
- Описание: Функции, полученные из контекста ModalWindowContext, для управления модальными окнами. openModal открывает модальное окно с указанным типом и параметрами (например, для отображения ошибок или уведомлений о загрузке). closeLoader закрывает экран загрузки после завершения инициализации данных.
|
||
4. loader
|
||
- Тип: boolean
|
||
- Описание: Флаг, управляющий отображением экрана загрузки. Изначально установлен в true, что указывает на процесс загрузки данных. После успешной инициализации нод и эджей устанавливается в false, и экран загрузки скрывается с помощью closeLoader.
|
||
5. socket
|
||
- Тип: WebSocket | null
|
||
- Описание: Состояние для хранения объекта WebSocket, используемого для связи с сервером по адресу ws://192.168.1.147:8001/api/v1. Изначально null. После успешного подключения сохраняет экземпляр WebSocket, позволяя отправлять и получать данные о сетевых интерфейсах.
|
||
6. data
|
||
- Тип: ResponseData\[\]
|
||
- Описание: Массив данных, полученных через WebSocket. Содержит два элемента: данные об активных (activation) и неактивных (deactivation) сетевых интерфейсах. Используется как источник для преобразования в ноды и эджи для отображения на холсте ReactFlow.
|
||
7. wifiData
|
||
- Тип: ResponseNetwork | undefined
|
||
- Описание: Состояние для хранения информации о Wi-Fi интерфейсе, извлеченной из активных данных (activation). Обновляется, если в полученных данных присутствует интерфейс с типом WIFI. Используется для специфической обработки и отображения Wi-Fi нод на холсте.
|
||
8. hasInited
|
||
- Тип: boolean
|
||
- Описание: Флаг, указывающий, были ли данные (ноды и эджи) успешно инициализированы. Изначально false. Устанавливается в true после первичной обработки данных и вызова handleInit из useInitCanvas, чтобы предотвратить повторную инициализацию.
|
||
9. { initedNodes, initedEdges, handleInit }
|
||
- Тип: Значения и метод из useInitCanvas
|
||
- Описание: Данные и функция из пользовательского хука useInitCanvas. initedNodes и initedEdges содержат инициализированные ноды и эджи, подготовленные для отображения на холсте. handleInit выполняет начальную обработку edgeData и interfaces для создания этих структур.
|
||
10. interfaces
|
||
- Тип: Interfaces\[\]
|
||
- Описание: Состояние для хранения преобразованных данных о сетевых интерфейсах, полученных из data с помощью функции initNetworkIntData. Содержит структурированные объекты, описывающие интерфейсы, в формате, удобном для построения нод на холсте ReactFlow.
|
||
11. edgeData
|
||
- Тип: IEdge\[\]
|
||
- Описание: Состояние для хранения преобразованных данных о связях (эджи) между сетевыми интерфейсами, полученных из data с помощью initNetworkIntData. Используется как промежуточный формат перед финальной инициализацией эджей для отображения.
|
||
12. nodes
|
||
- Тип: Массив нод для ReactFlow
|
||
- Описание: Состояние, управляемое хуком useNodesState, для хранения и отображения нод (блоков) на холсте ReactFlow. Инициализируется пустым массивом и заполняется данными из initedNodes после обработки. Обновляется при изменении позиций, групп или Wi-Fi данных.
|
||
13. edges
|
||
- Тип: Массив эджей для ReactFlow
|
||
- Описание: Состояние, управляемое хуком useEdgesState, для хранения и отображения связей (эджей) между нодами на холсте ReactFlow. Изначально пустой массив, заполняется из initedEdges после инициализации и обновляется при добавлении новых связей.
|
||
14. isPanDisabled
|
||
- Тип: boolean
|
||
- Описание: Флаг, полученный из хука usePannableStore, определяющий, отключена ли возможность перемещения (прокрутки) холста ReactFlow. Если true, пользователь не может перемещать холст, что полезно для фиксации вида при определенных действиях.
|
||
15. changeArray
|
||
- Тип: any\[\]
|
||
- Описание: Массив для временного хранения изменений позиций нод, полученных через processNodeChanges. Накапливает данные о перемещениях, которые затем отправляются на сервер через autosavePosition с интервалом в 1 секунду для сохранения позиций.
|
||
16. groups
|
||
- Тип: Данные из useGroupStore
|
||
- Описание: Состояние, полученное из хука useGroupStore, содержащее информацию о группах сетевых интерфейсов (например, активные и неактивные). Используется для сортировки и сворачивания/разворачивания групп нод на холсте с помощью groupSort и collapseGroup.
|
||
17. { isNullPos, setNullPos }
|
||
- Тип: Значения и метод из usePosStore
|
||
- Описание: Состояние и функция из хука usePosStore. isNullPos — флаг, указывающий, нужно ли сбросить позиции нод. setNullPos — метод для управления этим флагом. При активации сбрасывает позиции нод через resetPosition, отправляя запрос по WebSocket.
|
||
|
||
#### 2.2. Методы
|
||
|
||
Функции и эффекты
|
||
|
||
1. Первый useEffect — Подключение к WebSocket
|
||
|
||
```jsx
|
||
useEffect(() => {
|
||
if (startConnection) {
|
||
openModal(ModalMap['2'], 'Идет загрузка данных');
|
||
setNodes([]);
|
||
setEdges([]);
|
||
setHasInited(false);
|
||
initializationAttempt.current = 0;
|
||
const newSocket = new WebSocket(`ws://192.168.1.147:8001/api/v1`);
|
||
let connectionTimeout;
|
||
setSocket(newSocket);
|
||
connectionTimeout = setTimeout(() => {
|
||
if (newSocket.readyState !== WebSocket.OPEN) {
|
||
newSocket.close();
|
||
}
|
||
}, 5000);
|
||
newSocket.onopen = () => {
|
||
console.log('WebSocket connection established');
|
||
};
|
||
newSocket.onmessage = (event) => {
|
||
const newData = JSON.parse(event.data);
|
||
setData(() => [newData.activation, newData.deactivation]);
|
||
};
|
||
newSocket.onerror = (error) => {
|
||
console.error('WebSocket error:', error);
|
||
};
|
||
newSocket.onclose = () => {
|
||
console.log('WebSocket connection closed');
|
||
setRequestError({ status: true, message: 'Соединение с сервером разорвано' });
|
||
};
|
||
return () => {
|
||
newSocket.close();
|
||
closeLoader();
|
||
};
|
||
}
|
||
}, [startConnection, ws]);
|
||
```
|
||
|
||
- Условие: Срабатывает при изменении startConnection или ws.
|
||
- Логика: Открывает модальное окно загрузки, сбрасывает ноды, эджи и флаги. Создает WebSocket, устанавливает тайм-аут (5 сек) для проверки соединения. Обрабатывает открытие, получение данных, ошибки и закрытие.
|
||
- Очистка: Закрывает WebSocket и загрузчик.
|
||
- Цель: Установить связь с сервером и получить данные.
|
||
2. Второй useEffect — Обработка данных из WebSocket
|
||
|
||
```jsx
|
||
useEffect(() => {
|
||
if (data.length > 0) {
|
||
const { interfaces: newInterfaces, edgeData: newEdgeData } = initNetworkIntData(data);
|
||
const activationData = data[0].interfaces;
|
||
const wifi = activationData.find((el: any) => el.type === 'WIFI');
|
||
setInterfaces(newInterfaces);
|
||
setEdgeData(newEdgeData);
|
||
if (wifi) { setWifi(wifi); }
|
||
}
|
||
}, [data]);
|
||
```
|
||
|
||
- Условие: Срабатывает при изменении data.
|
||
- Логика: Преобразует data в interfaces и edgeData через initNetworkIntData. Ищет Wi-Fi интерфейс в активных данных, обновляет состояния.
|
||
- Цель: Подготовить данные для холст.
|
||
3. Третий useEffect — Инициализация нод и эджей
|
||
|
||
```jsx
|
||
useEffect(() => {
|
||
if (interfaces.length > 0 && !hasInited) {
|
||
handleInit(edgeData, interfaces);
|
||
setHasInited(true);
|
||
}
|
||
}, [interfaces, edgeData, hasInited]);
|
||
```
|
||
|
||
- Условие: Срабатывает при изменении interfaces, edgeData, hasInited.
|
||
- Логика: Если данные есть и инициализация не выполнена, вызывает handleInit для подготовки нод и эджей, затем устанавливает hasInited в true.
|
||
- Цель: Инициализировать холст.
|
||
4. Четвертый useEffect — Мониторинг инициализации
|
||
|
||
```jsx
|
||
useEffect(() => {
|
||
if (initializationAttempt.current === 0 && !requestError.status) {
|
||
const checkInterval = setInterval(() => {
|
||
setTimeout(() => {
|
||
if (!initedNodes.some(node => node.id === 'g0_nw0_rx0_ip0')) {
|
||
window.location.reload();
|
||
}
|
||
}, 500);
|
||
if (initedNodes.length > 0 && initedEdges.length > 0) {
|
||
setNodes(initedNodes);
|
||
setEdges(initedEdges);
|
||
console.log("Ноды и эджи успешно инициализированы");
|
||
initializationAttempt.current = 1;
|
||
clearInterval(checkInterval);
|
||
clearTimeout(timeoutId);
|
||
} else {
|
||
console.log("Попытка инициализации нод и эджей...");
|
||
}
|
||
}, 1000);
|
||
const timeoutId = setTimeout(() => {
|
||
console.error("Ошибка: слишком долгая загрузка данных");
|
||
clearInterval(checkInterval);
|
||
setRequestError({ status: true, message: 'Превышено время ожидания' });
|
||
}, 30000);
|
||
return () => {
|
||
clearInterval(checkInterval);
|
||
clearTimeout(timeoutId);
|
||
};
|
||
}
|
||
}, [initedNodes, initedEdges, requestError]);
|
||
```
|
||
|
||
- Условие: Срабатывает при изменении initedNodes, initedEdges, requestError.
|
||
- Логика: Проверяет каждую секунду наличие инициализированных данных. Через 500 мс проверяет ключевую ноду; если нет — перезагрузка. При успехе обновляет nodes, edges, логирует, останавливает проверки. Тайм-аут (30 сек) фиксирует ошибку.
|
||
- Очистка: Удаляет интервал и тайм-аут.
|
||
- Цель: Гарантировать успешную инициализацию.
|
||
5. Пятый useEffect — Обновление счетчика попыток
|
||
|
||
```jsx
|
||
useEffect(() => {
|
||
if (nodes.length === 0) {
|
||
initializationAttempt.current = 0;
|
||
} else {
|
||
initializationAttempt.current = 1;
|
||
}
|
||
}, [nodes]);
|
||
```
|
||
|
||
- Условие: Срабатывает при изменении nodes.
|
||
- Логика: Сбрасывает initializationAttempt в 0, если нод нет, иначе устанавливает 1.
|
||
- Цель: Отслеживать состояние инициализации.
|
||
6. Шестой useEffect — Управление загрузчиком
|
||
|
||
```jsx
|
||
useEffect(() => {
|
||
if (nodes.length > 0 && loader == true) {
|
||
setLoader(false);
|
||
closeLoader();
|
||
}
|
||
}, [nodes, loader]);
|
||
```
|
||
|
||
- Условие: Срабатывает при изменении nodes, loader.
|
||
- Логика: Если ноды есть и загрузчик активен, скрывает его.
|
||
- Цель: Убрать экран загрузки после рендера.
|
||
7. Функция onEdgesChange
|
||
|
||
```jsx
|
||
const onEdgesChange = useCallback(
|
||
(changes: any) => setEdges((eds) => applyEdgeChanges(changes, eds)),
|
||
[setEdges]
|
||
);
|
||
```
|
||
|
||
- Тип: useCallback
|
||
- Вход: changes — массив изменений эджей.
|
||
- Логика: Применяет изменения к эджам через applyEdgeChanges, обновляет edges.
|
||
- Цель: Обработать изменения связей.
|
||
8. Функция onConnect
|
||
|
||
```jsx
|
||
const onConnect = useCallback(
|
||
(connection: any) => setEdges((eds) => addEdge(connection, eds)),
|
||
[setEdges]
|
||
);
|
||
```
|
||
|
||
- Тип: useCallback
|
||
- Вход: connection — объект новой связи.
|
||
- Логика: Добавляет связь через addEdge, обновляет edges.
|
||
- Цель: Создавать новые связи на холсте.
|
||
9. Функция handleNodesChange
|
||
|
||
```jsx
|
||
const handleNodesChange = (changes: any) => {
|
||
onNodesChange(changes);
|
||
const newData = processNodeChanges(changes, data);
|
||
if (newData) {
|
||
setChangeArray((prevArray) => [...prevArray, newData]);
|
||
}
|
||
};
|
||
```
|
||
|
||
- Вход: changes — массив изменений нод.
|
||
- Логика: Обновляет ноды через onNodesChange, извлекает изменения позиций через processNodeChanges, добавляет в changeArray.
|
||
- Цель: Отслеживать перемещения нод.
|
||
10. Седьмой useEffect — Автосохранение позиций
|
||
|
||
```jsx
|
||
useEffect(() => {
|
||
const interval = setInterval(() => {
|
||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||
autosavePosition(changeArray, socket);
|
||
}
|
||
setChangeArray([]);
|
||
}, 1000);
|
||
return () => {
|
||
clearInterval(interval);
|
||
};
|
||
}, [changeArray, socket]);
|
||
```
|
||
|
||
- Условие: Срабатывает при изменении changeArray, socket.
|
||
- Логика: Каждую секунду отправляет изменения позиций через autosavePosition, очищает changeArray.
|
||
- Очистка: Удаляет интервал.
|
||
- Цель: Сохранять позиции на сервере.
|
||
11. Восьмой useEffect — Обработка групп
|
||
|
||
```jsx
|
||
useEffect(() => {
|
||
const sortedGroups = groupSort(groups, nodes);
|
||
setNodes(collapseGroup(sortedGroups, nodes));
|
||
}, [groups]);
|
||
```
|
||
|
||
- Условие: Срабатывает при изменении groups.
|
||
- Логика: Сортирует ноды по группам через groupSort, сворачивает/разворачивает через collapseGroup, обновляет nodes.
|
||
- Цель: Организовать ноды по группам.
|
||
12. Девятый useEffect — Обновление Wi-Fi
|
||
|
||
```jsx
|
||
useEffect(() => {
|
||
if (wifiData) {
|
||
setNodes(updateWifiData(wifiData, nodes));
|
||
}
|
||
}, [wifiData]);
|
||
```
|
||
|
||
- Условие: Срабатывает при изменении wifiData.
|
||
- Логика: Обновляет ноды с учетом Wi-Fi через updateWifiData.
|
||
- Цель: Отобразить Wi-Fi данные.
|
||
13. Десятый useEffect — Обработка ошибок
|
||
|
||
```jsx
|
||
useEffect(() => {
|
||
if (requestError.status) {
|
||
setTimeout(() => {
|
||
openModal(ModalMap['3'], { message: requestError.message });
|
||
}, 1000);
|
||
}
|
||
}, [requestError]);
|
||
```
|
||
|
||
- Условие: Срабатывает при изменении requestError.
|
||
- Логика: При ошибке через 1 сек открывает модальное окно с сообщением.
|
||
- Цель: Уведомить об ошибках.
|
||
14. Одиннадцатый useEffect — Сброс позиций
|
||
|
||
```jsx
|
||
useEffect(() => {
|
||
if (isNullPos) {
|
||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||
resetPosition(socket, setNullPos, nodes, setNodes);
|
||
}
|
||
}
|
||
}, [isNullPos]);
|
||
```
|
||
|
||
- Условие: Срабатывает при изменении isNullPos.
|
||
- Логика: При истинном isNullPos сбрасывает позиции через resetPosition, обновляет ноды.
|
||
- Цель: Вернуть исходные позиции.
|
||
|
||
#### 2.3. Рендеринг
|
||
|
||
```jsx
|
||
return (
|
||
<>
|
||
<ReactFlow
|
||
nodes={nodes}
|
||
edges={edges}
|
||
onNodesChange={handleNodesChange}
|
||
onEdgesChange={onEdgesChange}
|
||
onConnect={onConnect}
|
||
style={{ background: '#F7F9FB' }}
|
||
minZoom={0}
|
||
maxZoom={4}
|
||
fitView
|
||
fitViewOptions={{ padding: 0.1 }}
|
||
attributionPosition="bottom-left"
|
||
snapToGrid
|
||
snapGrid={[20, 20]}
|
||
panOnDrag={!isPanDisabled}
|
||
nodeTypes={nodeTypes}
|
||
proOptions={proOptions}
|
||
>
|
||
<Background gap={20} size={1} bgColor={theme.background} />
|
||
<Controls fitViewOptions={{ padding: 0.1 }} />
|
||
</ReactFlow>
|
||
</>
|
||
);
|
||
```
|
||
|
||
- Компонент: ReactFlow рендерит холст.
|
||
- Параметры: Ноды, эджи, обработчики, фон, зум (0–4), сетка (20x20), перетаскивание по isPanDisabled, кастомные ноды.
|
||
- Дочерние: Background (сетка), Controls (кнопки для управления зумом). |