Update physical test and load ios
This commit is contained in:
parent
ef1ba4ac99
commit
e4687f27f9
|
|
@ -124,7 +124,7 @@ export default class LineConnection {
|
|||
private listScenarios: number[]
|
||||
public handleClearLine: () => void
|
||||
private session: TestSession
|
||||
private physicalTest: PhysicalPortTest
|
||||
public physicalTest: PhysicalPortTest
|
||||
private outputPhysicalTest: string
|
||||
|
||||
constructor(config: LineConfig, socketIO: any, handleClearLine: () => void) {
|
||||
|
|
@ -514,24 +514,30 @@ export default class LineConnection {
|
|||
resolve(true)
|
||||
return
|
||||
}
|
||||
const detectLog = await this.detectLogWithAI(logScenarios)
|
||||
const result = mapToLineFormat({
|
||||
lineNumber: this.config.lineNumber,
|
||||
inventory: this.config.inventory,
|
||||
latestScenario: {
|
||||
detectAI: detectLog,
|
||||
},
|
||||
data,
|
||||
})
|
||||
// if (script?.send_result || script?.sendResult) {
|
||||
this.dataDPELP = result
|
||||
console.log(
|
||||
`DPELP DATA line ${this.config.lineNumber} of ${this.config.stationName}:`,
|
||||
this.dataDPELP
|
||||
)
|
||||
// }
|
||||
if (this.config.latestScenario)
|
||||
this.config.latestScenario = { ...this.config.latestScenario, detectAI: detectLog }
|
||||
if (script?.send_result || script?.sendResult) {
|
||||
const detectLog = await this.detectLogWithAI(logScenarios)
|
||||
const result = mapToLineFormat({
|
||||
lineNumber: this.config.lineNumber,
|
||||
inventory: this.config.inventory,
|
||||
latestScenario: {
|
||||
detectAI: detectLog,
|
||||
},
|
||||
data,
|
||||
})
|
||||
// if (script?.send_result || script?.sendResult) {
|
||||
this.dataDPELP = result
|
||||
console.log(
|
||||
`DPELP DATA line ${this.config.lineNumber} of ${this.config.stationName}:`,
|
||||
this.dataDPELP
|
||||
)
|
||||
|
||||
// }
|
||||
if (this.config.latestScenario)
|
||||
this.config.latestScenario = { ...this.config.latestScenario, detectAI: detectLog }
|
||||
if (result.sn) {
|
||||
this.updateNote(result.sn, result)
|
||||
}
|
||||
}
|
||||
this.config.data = data
|
||||
this.socketIO.emit('data_textfsm', {
|
||||
stationId: this.config.stationId,
|
||||
|
|
@ -540,9 +546,6 @@ export default class LineConnection {
|
|||
inventory: this.config.inventory || null,
|
||||
latestScenario: this.config.latestScenario || null,
|
||||
})
|
||||
if (result.sn) {
|
||||
this.updateNote(result.sn, result)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
|
@ -1014,6 +1017,7 @@ export default class LineConnection {
|
|||
|
||||
endTesting() {
|
||||
this.physicalTest.done = true
|
||||
this.physicalTest.resetTestedPorts()
|
||||
this.config.runningPhysical = false
|
||||
this.config.runningScenario = ''
|
||||
this.outputBuffer = ''
|
||||
|
|
|
|||
|
|
@ -76,6 +76,16 @@ export class PhysicalPortTest {
|
|||
.sort()
|
||||
}
|
||||
|
||||
resetTestedPorts() {
|
||||
// this.ports.clear()
|
||||
this.expectedPorts.forEach((p) => {
|
||||
this.ports.set(normalizeInterface(p), {
|
||||
name: normalizeInterface(p),
|
||||
tested: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
private checkDone() {
|
||||
const testedCount = [...this.ports.values()].filter((p) => p.tested).length
|
||||
|
||||
|
|
|
|||
|
|
@ -635,8 +635,28 @@ export class WebSocketIo {
|
|||
stationId,
|
||||
[lineId],
|
||||
async (lineCon) => {
|
||||
lineCon.endTesting()
|
||||
await lineCon.sendReportPhysicalTest()
|
||||
lineCon.endTesting()
|
||||
},
|
||||
{}
|
||||
)
|
||||
})
|
||||
|
||||
socket.on('reset_physical_test', async (data) => {
|
||||
const { stationId, lineId } = data
|
||||
await this.handleLineOperation(
|
||||
io,
|
||||
stationId,
|
||||
[lineId],
|
||||
async (lineCon) => {
|
||||
lineCon.physicalTest.resetTestedPorts()
|
||||
io.emit('running_scenario', {
|
||||
stationId: stationId,
|
||||
lineId: lineId,
|
||||
title: 'Physical Test',
|
||||
physical: true,
|
||||
ports: lineCon.config.ports,
|
||||
})
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
|
@ -684,7 +704,8 @@ export class WebSocketIo {
|
|||
lines: Line[],
|
||||
station: Station,
|
||||
output = '',
|
||||
inventory: string = ''
|
||||
inventory: string = '',
|
||||
latestScenario?: any
|
||||
) {
|
||||
try {
|
||||
for (const line of lines) {
|
||||
|
|
@ -710,6 +731,7 @@ export class WebSocketIo {
|
|||
inventory: inventory,
|
||||
runningPhysical: false,
|
||||
runningScenario: '',
|
||||
latestScenario: latestScenario,
|
||||
},
|
||||
socket,
|
||||
async () => {
|
||||
|
|
@ -793,7 +815,8 @@ export class WebSocketIo {
|
|||
[linesData],
|
||||
stationData,
|
||||
line?.config?.output || '',
|
||||
line?.config?.inventory || ''
|
||||
line?.config?.inventory || '',
|
||||
line?.config?.latestScenario || undefined
|
||||
)
|
||||
this.lineConnecting = this.lineConnecting.filter((el) => el !== lineId)
|
||||
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ function App() {
|
|||
const flushScheduledRef = useRef(false);
|
||||
const [listBrands, setListBrands] = useState<TBrands[]>([]);
|
||||
const [listCategories, setListCategories] = useState<TCategories[]>([]);
|
||||
const [listIos, setListIos] = useState<string[]>([]);
|
||||
|
||||
const connectApcSwitch = (station: TStation) => {
|
||||
if (station?.apc_1_ip && station?.apc_1_port) {
|
||||
|
|
@ -189,12 +190,25 @@ function App() {
|
|||
}
|
||||
};
|
||||
|
||||
// function get list ios
|
||||
const getListIos = async () => {
|
||||
try {
|
||||
const response = await axios.get(apiUrl + "api/ios");
|
||||
if (response.data && Array.isArray(response.data)) {
|
||||
setListIos(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error get ios", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!socket) return;
|
||||
getStation();
|
||||
getScenarios();
|
||||
getBrands();
|
||||
getCategories();
|
||||
getListIos();
|
||||
}, [socket]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -387,6 +401,7 @@ function App() {
|
|||
runningScenario: data?.title || "",
|
||||
runningPhysical: data?.physical || false,
|
||||
ports: data?.ports || [],
|
||||
listPortsPhysical: [],
|
||||
},
|
||||
data?.stationId
|
||||
);
|
||||
|
|
@ -401,6 +416,15 @@ function App() {
|
|||
);
|
||||
});
|
||||
|
||||
socket?.on("test_port_physical", (data) => {
|
||||
if (data?.data && data?.data.length > 0)
|
||||
updateValueLineStation(
|
||||
data?.lineId,
|
||||
{ listPortsPhysical: data?.data },
|
||||
data?.stationId
|
||||
);
|
||||
});
|
||||
|
||||
// ✅ cleanup on unmount or when socket changes
|
||||
return () => {
|
||||
socket.off("init");
|
||||
|
|
@ -418,6 +442,7 @@ function App() {
|
|||
socket.off("line_connecting");
|
||||
socket.off("running_scenario");
|
||||
socket.off("user_clear_terminal");
|
||||
socket.off("test_port_physical");
|
||||
};
|
||||
}, [socket, stations, selectedLine]);
|
||||
|
||||
|
|
@ -462,6 +487,10 @@ function App() {
|
|||
...updates,
|
||||
lineNumber: lineItem.lineNumber,
|
||||
line_number: lineItem.line_number,
|
||||
ports:
|
||||
updates?.ports && updates?.ports?.length > 0
|
||||
? updates?.ports
|
||||
: lineItem.ports || [],
|
||||
...(isNetOutput && {
|
||||
netOutput: updates?.loadingClearTerminal
|
||||
? ""
|
||||
|
|
@ -489,6 +518,10 @@ function App() {
|
|||
return {
|
||||
...prevSelected,
|
||||
...updates,
|
||||
ports:
|
||||
updates?.ports && updates?.ports?.length > 0
|
||||
? updates?.ports
|
||||
: prevSelected.ports || [],
|
||||
...(isNetOutput && {
|
||||
netOutput: updates?.loadingClearTerminal
|
||||
? ""
|
||||
|
|
@ -837,6 +870,7 @@ function App() {
|
|||
socket={socket}
|
||||
stationItem={stations.find((el) => el.id === Number(activeTab))}
|
||||
scenarios={scenarios}
|
||||
listIos={listIos}
|
||||
/>
|
||||
|
||||
{/* <ModalConfirmRunScenario
|
||||
|
|
|
|||
|
|
@ -394,6 +394,23 @@ const BottomToolBar = ({
|
|||
>
|
||||
Scenario
|
||||
</Button>
|
||||
<Button
|
||||
fw={400}
|
||||
disabled={isDisable || selectedLines.length === 0}
|
||||
variant="filled"
|
||||
mr={"5px"}
|
||||
style={{ height: "30px", width: "100px" }}
|
||||
onClick={() => {
|
||||
selectedLines.forEach((line) => {
|
||||
socket?.emit("run_physical_test", {
|
||||
lineId: line?.id,
|
||||
stationId: Number(station.id),
|
||||
});
|
||||
});
|
||||
}}
|
||||
>
|
||||
Physical
|
||||
</Button>
|
||||
<DrawerLogs
|
||||
socket={socket}
|
||||
isLogModalOpen={isLogModalOpen}
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ const CardLine = ({
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
{line?.connecting && (
|
||||
{line?.connecting && line?.status !== "connected" && (
|
||||
<motion.div
|
||||
style={{ fontSize: "11px", color: "red" }}
|
||||
animate={{ opacity: [0.2, 1, 0.2] }}
|
||||
|
|
@ -686,14 +686,28 @@ const CardLine = ({
|
|||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<div className={classes.info_line}>
|
||||
Latest: {line?.latestScenario?.name || ""}
|
||||
<Text style={{ fontStyle: "italic", fontSize: "11px" }}>
|
||||
{line?.latestScenario?.time
|
||||
? "(" + convertTimestampToDate(line?.latestScenario?.time) + ")"
|
||||
: ""}
|
||||
</Text>
|
||||
</div>
|
||||
<Flex justify={"space-between"}>
|
||||
<div className={classes.info_line}>
|
||||
Latest: {line?.latestScenario?.name || ""}
|
||||
<Text style={{ fontStyle: "italic", fontSize: "11px" }}>
|
||||
{line?.latestScenario?.time
|
||||
? "(" +
|
||||
convertTimestampToDate(line?.latestScenario?.time) +
|
||||
")"
|
||||
: ""}
|
||||
</Text>
|
||||
</div>
|
||||
<div style={{ display: line?.runningPhysical ? "" : "none" }}>
|
||||
<Text className={classes.info_line}>
|
||||
Ports Tested{" "}
|
||||
{line?.ports?.length
|
||||
? `(${line?.listPortsPhysical?.length || 0}/${
|
||||
line?.ports?.length || 0
|
||||
})`
|
||||
: ""}
|
||||
</Text>
|
||||
</div>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,271 @@
|
|||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Flex,
|
||||
Modal,
|
||||
ScrollArea,
|
||||
Table,
|
||||
Tabs,
|
||||
Text,
|
||||
TextInput,
|
||||
} from "@mantine/core";
|
||||
import type { Socket } from "socket.io-client";
|
||||
import type { TLine, TStation } from "../../untils/types";
|
||||
import { IconPlayerPlay, IconX } from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
|
||||
const ModalSelectIOS = ({
|
||||
socket,
|
||||
station,
|
||||
listIos,
|
||||
opened,
|
||||
close,
|
||||
line,
|
||||
}: {
|
||||
socket: Socket | null;
|
||||
station: TStation | undefined;
|
||||
listIos: string[];
|
||||
opened: boolean;
|
||||
close: () => void;
|
||||
line: TLine | undefined;
|
||||
}) => {
|
||||
const [isReboot, setIsReboot] = useState<boolean>(false);
|
||||
const [inputSearch, setInputSearch] = useState<string>("");
|
||||
|
||||
const filterIos = (type: string = "") => {
|
||||
// Switch: Ưu tiên các dòng 4 chữ số cụ thể
|
||||
// c2960, c3560, c3750, c3850, c4500, c9xxx
|
||||
const switchRegex = /^(c2960|c3560|c3750|c3850|c4500|c9\d{3})/i;
|
||||
|
||||
// Router: Các dòng ISR đời cũ và mới
|
||||
// c18xx, c19xx, c28xx, c29xx (nhưng không phải 2960), c38xx (nhưng không phải 3850), c39xx, isr, asr
|
||||
const routerRegex =
|
||||
/^(c8\d{2}|c18|c19|c28|c29(?!60)|c38(?!50)|c39|isr|asr)/i;
|
||||
|
||||
return listIos
|
||||
.filter((name) => {
|
||||
if (type === "switch") {
|
||||
return switchRegex.test(name);
|
||||
}
|
||||
if (type === "router") {
|
||||
return routerRegex.test(name);
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.filter((ios) => ios.toLowerCase().includes(inputSearch.toLowerCase()));
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
style={{ position: "absolute", left: 0 }}
|
||||
opened={opened}
|
||||
onClose={() => {
|
||||
close();
|
||||
}}
|
||||
title={
|
||||
<Text fz={"lg"} fw={"bolder"}>
|
||||
Select IOS
|
||||
</Text>
|
||||
}
|
||||
size="xl"
|
||||
>
|
||||
<Tabs defaultValue="router" h={"100%"}>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab w={"50%"} value="router">
|
||||
Router
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab w={"50%"} value="switch">
|
||||
Switch
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel value="router" w={"100%"}>
|
||||
<Flex justify={"space-between"} align={"center"} mt={"xs"}>
|
||||
<TextInput
|
||||
style={{ width: "350px" }}
|
||||
placeholder="Search file name"
|
||||
value={inputSearch}
|
||||
onChange={(event) => setInputSearch(event.currentTarget.value)}
|
||||
rightSection={
|
||||
inputSearch ? (
|
||||
<IconX
|
||||
size={14}
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => setInputSearch("")}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
rightSectionPointerEvents="auto"
|
||||
size="xs"
|
||||
/>
|
||||
<Checkbox
|
||||
label="Reboot and send Break"
|
||||
style={{ color: "red" }}
|
||||
checked={isReboot}
|
||||
onChange={(event) => setIsReboot(event.currentTarget.checked)}
|
||||
/>
|
||||
</Flex>
|
||||
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
||||
<Table
|
||||
stickyHeader
|
||||
striped
|
||||
highlightOnHover
|
||||
withRowBorders={true}
|
||||
withTableBorder={true}
|
||||
withColumnBorders={true}
|
||||
>
|
||||
<Table.Thead
|
||||
style={{
|
||||
top: 1,
|
||||
}}
|
||||
>
|
||||
<Table.Tr>
|
||||
<Table.Th
|
||||
style={{
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
Name
|
||||
</Table.Th>
|
||||
<Table.Th
|
||||
style={{
|
||||
width: "70px",
|
||||
textAlign: "center",
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
Action
|
||||
</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{filterIos("router")?.map((ios, i) => (
|
||||
<Table.Tr key={i}>
|
||||
<Table.Td>{ios || ""}</Table.Td>
|
||||
<Table.Td
|
||||
style={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
style={{ width: "100px" }}
|
||||
variant="light"
|
||||
color="green"
|
||||
size="sm"
|
||||
leftSection={<IconPlayerPlay size={16} />}
|
||||
onClick={() => {
|
||||
if (isReboot)
|
||||
socket?.emit("control_apc", {
|
||||
outletNumbers: [line?.outlet],
|
||||
station: { ...station, lines: [] },
|
||||
action: "restart",
|
||||
apcName: line?.apc_name || line?.apcName,
|
||||
});
|
||||
socket?.emit("load_ios_router", {
|
||||
stationId: Number(station?.id),
|
||||
lineId: Number(line?.id),
|
||||
iosName: ios,
|
||||
});
|
||||
close();
|
||||
}}
|
||||
>
|
||||
Run
|
||||
</Button>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value="switch" w={"100%"}>
|
||||
<Flex justify={"space-between"} align={"center"} mt={"xs"}>
|
||||
<TextInput
|
||||
style={{ width: "350px" }}
|
||||
placeholder="Search file name"
|
||||
value={inputSearch}
|
||||
onChange={(event) => setInputSearch(event.currentTarget.value)}
|
||||
rightSection={
|
||||
inputSearch ? (
|
||||
<IconX
|
||||
size={14}
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => setInputSearch("")}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
rightSectionPointerEvents="auto"
|
||||
size="xs"
|
||||
/>
|
||||
</Flex>
|
||||
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
||||
<Table
|
||||
stickyHeader
|
||||
striped
|
||||
highlightOnHover
|
||||
withRowBorders={true}
|
||||
withTableBorder={true}
|
||||
withColumnBorders={true}
|
||||
>
|
||||
<Table.Thead
|
||||
style={{
|
||||
top: 1,
|
||||
}}
|
||||
>
|
||||
<Table.Tr>
|
||||
<Table.Th
|
||||
style={{
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
Name
|
||||
</Table.Th>
|
||||
<Table.Th
|
||||
style={{
|
||||
width: "70px",
|
||||
textAlign: "center",
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
Action
|
||||
</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{filterIos("switch")?.map((ios, i) => (
|
||||
<Table.Tr key={i}>
|
||||
<Table.Td>{ios || ""}</Table.Td>
|
||||
<Table.Td
|
||||
style={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
style={{ width: "100px" }}
|
||||
variant="light"
|
||||
color="green"
|
||||
size="sm"
|
||||
leftSection={<IconPlayerPlay size={16} />}
|
||||
onClick={() => {
|
||||
socket?.emit("load_ios_switch", {
|
||||
stationId: Number(station?.id),
|
||||
lineId: Number(line?.id),
|
||||
iosName: ios,
|
||||
});
|
||||
close();
|
||||
}}
|
||||
>
|
||||
Run
|
||||
</Button>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalSelectIOS;
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -106,6 +106,7 @@ export type TLine = {
|
|||
scenario?: IScenario;
|
||||
loadingClearTerminal?: boolean;
|
||||
runningPhysical?: boolean;
|
||||
listPortsPhysical?: string[];
|
||||
};
|
||||
|
||||
export type TUser = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue