2025-06-09 10:17:12 +05:00
|
|
|
|
import styled from "styled-components"
|
|
|
|
|
|
import { useState, useEffect } from "react"
|
|
|
|
|
|
import { useModalContext } from "../../../../store"
|
|
|
|
|
|
import { ModalMap } from "../../../modalwindows/windowsType"
|
|
|
|
|
|
import { NetworkService } from "../../../../api/request"
|
|
|
|
|
|
import { AnimDot } from "../../../UI/animDot"
|
|
|
|
|
|
import { useMenu } from "../../../../hooks/useMenu"
|
|
|
|
|
|
import { useIpStore, useDevicesStore } from "../../../../store"
|
|
|
|
|
|
import { menusNames } from "../../../../const/menus/othersMenus/names"
|
|
|
|
|
|
import { useWorkSpaceStore } from "../../../../store"
|
|
|
|
|
|
import { workSpaceNames } from "../../../../const/workspace/names"
|
|
|
|
|
|
import { IconComponent } from "../../../../const/buttons/icons"
|
|
|
|
|
|
import { buttonNames } from "../../../../const/buttons/names"
|
|
|
|
|
|
|
|
|
|
|
|
const Container = styled.div`
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 15px;
|
|
|
|
|
|
padding: 20px;
|
2025-06-09 10:31:14 +05:00
|
|
|
|
width: 210px;
|
2025-06-09 10:17:12 +05:00
|
|
|
|
p{
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
|
|
const Title = styled.div`
|
|
|
|
|
|
padding: 5px 0;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
border-bottom: 1px solid ${props => props.theme.text};
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
|
|
const DeviceBlock = styled.div`
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
border: 2px solid ${props => props.theme.accent};
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
|
|
|
|
.paddingP{
|
|
|
|
|
|
padding: 7px 5px;
|
|
|
|
|
|
}
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
|
|
const DeviceControl = styled.div`
|
|
|
|
|
|
padding: 5px 8px;
|
|
|
|
|
|
background: ${props => props.theme.accent};
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
|
|
const PlusBlock = styled.div`
|
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
border: 2px dotted ${props => props.theme.accent};
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: 5px 0;
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
|
|
const Text = styled.div`
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 3px;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
|
|
const DevicesContainer = styled.div`
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 15px;
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
|
|
const DevicesHeader = styled.div`
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
|
|
|
|
.hControl{
|
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
|
|
const DevicesContent = styled.div<{ $isactive: string }>`
|
|
|
|
|
|
${props => props.$isactive === 'true' && `display: flex;`}
|
|
|
|
|
|
${props => props.$isactive === 'false' && `display: none;`}
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 15px;
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
|
|
interface CurrentDevice {
|
|
|
|
|
|
hostname: string
|
|
|
|
|
|
address: string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export const DevicesBar = () => {
|
|
|
|
|
|
const { openModal } = useModalContext();
|
|
|
|
|
|
const { setIpAddress, ipAddress } = useIpStore();
|
|
|
|
|
|
const { devices, addDevice, deleteDevice } = useDevicesStore()
|
|
|
|
|
|
const { isOpen, collapseMenu } = useMenu(menusNames[1]);
|
|
|
|
|
|
const { setWorkModule } = useWorkSpaceStore()
|
|
|
|
|
|
|
|
|
|
|
|
const [currentDevice, setCurrentDevice] = useState<CurrentDevice>({
|
|
|
|
|
|
address: 'localhost',
|
|
|
|
|
|
hostname: ''
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const checkWebSocketAvailability = async (ip: string): Promise<boolean> => {
|
|
|
|
|
|
return new Promise((resolve) => {
|
2025-07-04 15:07:41 +05:00
|
|
|
|
const testSocket = new WebSocket(`ws://${ip}:8001/api/v1`);
|
2025-06-09 10:17:12 +05:00
|
|
|
|
const timeout = setTimeout(() => {
|
2025-06-09 10:31:14 +05:00
|
|
|
|
testSocket.close();
|
|
|
|
|
|
resolve(false);
|
|
|
|
|
|
}, 500
|
|
|
|
|
|
);
|
2025-06-09 10:17:12 +05:00
|
|
|
|
|
|
|
|
|
|
testSocket.onopen = () => {
|
|
|
|
|
|
clearTimeout(timeout);
|
|
|
|
|
|
testSocket.close();
|
|
|
|
|
|
resolve(true);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
testSocket.onerror = () => {
|
|
|
|
|
|
clearTimeout(timeout);
|
|
|
|
|
|
testSocket.close();
|
|
|
|
|
|
resolve(false);
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
const fetchHostame = async () => {
|
|
|
|
|
|
let response = await NetworkService.getHostname();
|
|
|
|
|
|
if (response.status === 200) {
|
|
|
|
|
|
setCurrentDevice(prevData => ({
|
|
|
|
|
|
...prevData,
|
|
|
|
|
|
hostname: response.data.hostname
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
fetchHostame();
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
|
|
|
|
const handleAdd = () => {
|
|
|
|
|
|
openModal(ModalMap['4'], {
|
|
|
|
|
|
onSave: handleAddDeviceComplete,
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleAddDeviceComplete = (data: string) => {
|
2025-06-09 10:31:14 +05:00
|
|
|
|
addDevice(data);
|
|
|
|
|
|
switchWs(data);
|
2025-06-09 10:17:12 +05:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleDelete = (number: number, adress: string) => {
|
|
|
|
|
|
deleteDevice(number)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(adress == ipAddress){
|
|
|
|
|
|
setIpAddress('localhost')
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const switchWs = async (ip: string) => {
|
|
|
|
|
|
const isAvailable = await checkWebSocketAvailability(ip);
|
|
|
|
|
|
if (!isAvailable) {
|
|
|
|
|
|
openModal(ModalMap['3'], {
|
|
|
|
|
|
message: `Не удалось подключиться к серверу по адресу ${ip}`,
|
|
|
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setIpAddress(ip);
|
|
|
|
|
|
setWorkModule(workSpaceNames[3]);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (devices.length === 0) {
|
|
|
|
|
|
setIpAddress('localhost');
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [devices]);
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Container>
|
|
|
|
|
|
<Title>
|
|
|
|
|
|
<p className="unselectable">Доступные узлы</p>
|
|
|
|
|
|
</Title>
|
|
|
|
|
|
|
|
|
|
|
|
<DeviceBlock>
|
|
|
|
|
|
<DeviceControl className="unselectable" onClick={() => switchWs(currentDevice.address)}>
|
2025-06-09 10:31:14 +05:00
|
|
|
|
<p>Корневой адрес</p>
|
2025-06-09 10:17:12 +05:00
|
|
|
|
</DeviceControl>
|
|
|
|
|
|
{currentDevice.hostname == '' ? (
|
2025-06-09 10:31:14 +05:00
|
|
|
|
<Text className="paddingP">Название: Загрузка<AnimDot /></Text>
|
2025-06-09 10:17:12 +05:00
|
|
|
|
) : (
|
2025-06-09 10:31:14 +05:00
|
|
|
|
<p className="paddingP">Название: {currentDevice.hostname}</p>
|
2025-06-09 10:17:12 +05:00
|
|
|
|
)}
|
|
|
|
|
|
</DeviceBlock>
|
|
|
|
|
|
|
|
|
|
|
|
<DevicesContainer>
|
|
|
|
|
|
<DevicesHeader className="unselectable" onClick={collapseMenu}>
|
|
|
|
|
|
<p>Дополнительные узлы</p>
|
|
|
|
|
|
<IconComponent name={isOpen ? buttonNames[5] : buttonNames[6]} />
|
|
|
|
|
|
</DevicesHeader>
|
|
|
|
|
|
|
|
|
|
|
|
<DevicesContent $isactive={isOpen.toString()} >
|
|
|
|
|
|
{devices.length > 0 &&
|
|
|
|
|
|
devices.map((device, index) => (
|
|
|
|
|
|
<DeviceBlock key={index}>
|
|
|
|
|
|
<DeviceControl className="unselectable" onClick={() => switchWs(device.address)}>
|
|
|
|
|
|
<p>Узел {device.number}</p>
|
|
|
|
|
|
<IconComponent
|
|
|
|
|
|
width="20"
|
|
|
|
|
|
height="20"
|
|
|
|
|
|
name={buttonNames[2]}
|
|
|
|
|
|
setColor="white"
|
|
|
|
|
|
onClick={(e: any) => {
|
|
|
|
|
|
e.stopPropagation(); // Предотвращаем всплытие события
|
|
|
|
|
|
handleDelete(device.number, device.address);
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</DeviceControl>
|
|
|
|
|
|
<p className="paddingP">Адрес: {device.address}</p>
|
|
|
|
|
|
</DeviceBlock>
|
|
|
|
|
|
))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
<PlusBlock className="unselectable" onClick={handleAdd}>
|
|
|
|
|
|
+
|
|
|
|
|
|
</PlusBlock>
|
|
|
|
|
|
</DevicesContent>
|
|
|
|
|
|
|
|
|
|
|
|
</DevicesContainer>
|
|
|
|
|
|
</Container>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|