Update flow check physical ports test
This commit is contained in:
parent
d33878c112
commit
63b264304e
|
|
@ -216,12 +216,6 @@ export default class LineConnection {
|
|||
}
|
||||
if (this.config.runningPhysical) {
|
||||
this.outputPhysicalTest += message
|
||||
if (this.debounceTimer) clearTimeout(this.debounceTimer)
|
||||
|
||||
if (this.testingPortPoE)
|
||||
this.debounceTimer = setTimeout(() => {
|
||||
this.flushLogBuffer()
|
||||
}, 1000) // 1s debounce
|
||||
}
|
||||
if (data.toString().includes('More') || data.toString().includes('MORE'))
|
||||
this.writeCommand(' ')
|
||||
|
|
@ -1108,35 +1102,39 @@ export default class LineConnection {
|
|||
return
|
||||
}
|
||||
|
||||
this.physicalTest.start(listPorts, this.config.inventory)
|
||||
// const interval = setInterval(async () => {
|
||||
// if (!this.physicalTest.done) {
|
||||
// // const result = this.physicalTest.getResult()
|
||||
// // console.warn('⚠️ Missing ports:', result.missingPorts)
|
||||
// } else {
|
||||
// clearInterval(interval)
|
||||
// await this.sendReportPhysicalTest()
|
||||
// this.endTesting()
|
||||
// }
|
||||
// }, 10000)
|
||||
this.physicalTest.start(
|
||||
listPorts.map((el) => el),
|
||||
this.config.inventory
|
||||
)
|
||||
const interval = setInterval(async () => {
|
||||
if (!this.config.runningPhysical) {
|
||||
clearInterval(interval)
|
||||
} else {
|
||||
this.flushLogBuffer()
|
||||
}
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
flushLogBuffer() {
|
||||
const lines = this.outputPhysicalTest.split(/\r?\n/)
|
||||
|
||||
// giữ lại dòng cuối nếu chưa kết thúc hoàn chỉnh
|
||||
this.outputPhysicalTest = lines.pop() || ''
|
||||
|
||||
const completeLines = lines.join('\n')
|
||||
|
||||
if (completeLines.trim()) {
|
||||
const ports = this.physicalTest.handleLog(completeLines)
|
||||
if (ports?.length)
|
||||
async flushLogBuffer() {
|
||||
try {
|
||||
this.writeCommand('show power inline | include on\r\n')
|
||||
this.writeCommand('\r\n')
|
||||
await this.sleep(1000)
|
||||
this.writeCommand('show interfaces status | include SFP\r\n')
|
||||
this.writeCommand('\r\n')
|
||||
await this.sleep(2000)
|
||||
const output = this.outputPhysicalTest
|
||||
this.outputPhysicalTest = ''
|
||||
if (output) {
|
||||
const ports = this.physicalTest.detectPorts(output)
|
||||
this.socketIO.emit('test_port_physical', {
|
||||
stationId: this.config.stationId,
|
||||
lineId: this.config.id,
|
||||
data: ports,
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('flushLogBuffer', error)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1164,22 +1162,31 @@ export default class LineConnection {
|
|||
* Get list PoE ports
|
||||
*/
|
||||
async getPorts(): Promise<string[]> {
|
||||
this.writeCommand(' terminal length 0\r\n')
|
||||
this.writeCommand(' show power inline\r\n')
|
||||
this.writeCommand(' \r\n')
|
||||
await this.sleep(5000)
|
||||
await this.sleep(3000)
|
||||
this.writeCommand(' show interfaces status\r\n')
|
||||
this.writeCommand(' \r\n')
|
||||
await this.sleep(4000)
|
||||
const statusOutput = this.outputPhysicalTest
|
||||
this.outputPhysicalTest = ''
|
||||
|
||||
const lines = statusOutput.split('\n')
|
||||
const ports = []
|
||||
for (const line of lines) {
|
||||
// Match: "Gi0/1 is up, line protocol is up"
|
||||
const match = line.match(/^(\S+)\s+\S+\s+(on|off)/i)
|
||||
|
||||
if (match) {
|
||||
const name = match[1]
|
||||
const matchPoE = line.match(/^(\S+)\s+\S+\s+(on|off)/i)
|
||||
if (matchPoE) {
|
||||
const name = matchPoE[1]
|
||||
ports.push(normalizeInterface(name))
|
||||
}
|
||||
// Match: "Gi0/15 notconnect 1 auto auto 1000BaseSX SFP"
|
||||
// Match: "Gi0/16 notconnect 1 auto auto Not Present"
|
||||
const matchSFP = line.match(/^([A-Za-z0-9\/]+).*\b(SFP|Not Present)\b/i)
|
||||
if (matchSFP) {
|
||||
const name = matchSFP[1]
|
||||
ports.push(normalizeInterface(name) + ' (SFP)')
|
||||
}
|
||||
}
|
||||
this.config.ports = [...new Set(ports)]
|
||||
return [...new Set(ports)]
|
||||
|
|
@ -1191,7 +1198,7 @@ export default class LineConnection {
|
|||
async sendReportPhysicalTest() {
|
||||
const formReport = this.physicalTest.getFormReport()
|
||||
await sendMessageToMail(
|
||||
`[ATC] - [${this.config.stationName} - Line: ${this.config.lineNumber}] - Physical Port Test`,
|
||||
`[ATC] - [${this.config.stationName} - Line: ${this.config.lineNumber}] - Physical Ports Test`,
|
||||
formReport
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import { normalizeInterface } from '../ultils/helper.js'
|
|||
import { PhysicalTestReport, PhysicalTestResult, PortState } from '../ultils/types.js'
|
||||
const LINK_REGEX =
|
||||
/Interface\s+((?:FastEthernet|GigabitEthernet|TenGigabitEthernet|TwentyFiveGigE|FortyGigabitEthernet|HundredGigE|Ethernet|Port-channel|Fa|Gi|Te|Hu|Eth)[\w\/.-]+),\s+changed state to\s+(up|down)/i
|
||||
const POE_GRANTED_REGEX = /%ILPOWER-\d+-POWER_GRANTED:\s+Interface\s+([\w\/.-]+):\s+Power granted/i
|
||||
const POE_GRANTED_REGEX =
|
||||
/.*%ILPOWER-\d+-POWER_GRANTED:\s+Interface\s+([\w\/.-]+):\s+Power granted/i
|
||||
const POE_DISCONNECT_REGEX =
|
||||
/%ILPOWER-\d+-IEEE_DISCONNECT:\s+Interface\s+([\w\/.-]+):\s+PD removed/i
|
||||
|
||||
|
|
@ -64,6 +65,7 @@ export class PhysicalPortTest {
|
|||
match = line.match(POE_GRANTED_REGEX)
|
||||
if (match) {
|
||||
iface = normalizeInterface(match[1])
|
||||
state = 'up'
|
||||
markTested = true
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +161,7 @@ export class PhysicalPortTest {
|
|||
const status = missing.length === 0 ? 'PASS' : 'WARNING'
|
||||
|
||||
return `
|
||||
<b>Physical Port Test Report</b><br/>
|
||||
<b>Physical Ports Test Report</b><br/>
|
||||
<table cellpadding="6" cellspacing="0" border="1" style="margin-top: 10px; border-collapse: collapse; width: 100%">
|
||||
<tr>
|
||||
<td style="width: 50%;">
|
||||
|
|
@ -169,26 +171,25 @@ export class PhysicalPortTest {
|
|||
</td>
|
||||
<td>
|
||||
Total Ports : ${report.ports.length}<br/>
|
||||
Ports Tested (UP) : ${tested.length}<br/>
|
||||
Ports Missing : ${missing.length}<br/>
|
||||
Ports Tested (UP) : <b style="color: #008000;">${tested.length}</b><br/>
|
||||
Ports Missing : <b style="color: #ff0000;">${missing.length}</b><br/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
────────────────────────────────<br/>
|
||||
<b>Passed Ports</b><br/>
|
||||
<b style="color: #008000;">Passed Ports</b><br/>
|
||||
${
|
||||
tested.length
|
||||
? `<div style="margin-top: 10px; border: 1px solid #ccc; padding: 5px; column-count: 12;">${tested.map((p) => this.normalizePortName(p.name)).join('<br/>')}</div><br/>
|
||||
`
|
||||
: ''
|
||||
}
|
||||
<br/>
|
||||
${
|
||||
missing.length
|
||||
? `
|
||||
────────────────────────────────<br/>
|
||||
<b>Missing Ports</b><br/>
|
||||
<b style="color: #ff0000;">Missing Ports</b><br/>
|
||||
<div style="margin-top: 10px; border: 1px solid #ccc; padding: 5px; column-count: 12;">${missing.map((p) => this.normalizePortName(p.name)).join('<br/>')}</div><br/>
|
||||
`
|
||||
: ''
|
||||
|
|
@ -209,7 +210,11 @@ export class PhysicalPortTest {
|
|||
if (!port) return ''
|
||||
|
||||
// Example inputs: "Fa0/1", "Gi0/0/1", "Fa0/0/2"
|
||||
const match = port.match(/^([A-Za-z]+)([\d/]+)$/)
|
||||
const isSFP = port.includes('SFP')
|
||||
const match = port
|
||||
.replace('(SFP)', '')
|
||||
.trim()
|
||||
.match(/^([A-Za-z]+)([\d/]+)$/)
|
||||
|
||||
if (!match) return port
|
||||
|
||||
|
|
@ -221,6 +226,44 @@ export class PhysicalPortTest {
|
|||
const last = parts[parts.length - 1]
|
||||
const preLast = parts?.length > 1 ? parts[parts.length - 2] : ''
|
||||
|
||||
return `${type?.slice(0, 2)}${preLast ? preLast + '/' : ''}${last}`
|
||||
return `${type?.slice(0, 2)}${preLast ? preLast + '/' : ''}${last}${isSFP ? ' (SFP)' : ''}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Function 1: Lấy danh sách các cổng có module SFP từ lệnh 'show interfaces status'
|
||||
* Logic: Tìm dòng bắt đầu bằng Tên Port và có chứa từ khóa "SFP" ở cuối.
|
||||
* Function 2: Lấy danh sách các cổng đang cấp nguồn (PoE on) từ lệnh 'show power inline'
|
||||
* Logic: Tìm dòng bắt đầu bằng Tên Port, cột tiếp theo là Admin status, cột tiếp theo là 'on'.
|
||||
*/
|
||||
detectPorts(output: string): string[] {
|
||||
for (const line of output.split('\n')) {
|
||||
if (line?.includes('include')) continue
|
||||
const ports: string[] = []
|
||||
const regexPoE = /^([A-Za-z0-9\/]+).*\on\b/i
|
||||
const regexSFP = /^([A-Za-z0-9\/]+).*\bSFP\b/i
|
||||
|
||||
let matchPoE = line.match(regexPoE)
|
||||
if (matchPoE) {
|
||||
ports.push(matchPoE[1])
|
||||
}
|
||||
let matchSFP = line.match(regexSFP)
|
||||
if (matchSFP) {
|
||||
ports.push(matchSFP[1] + ' (SFP)')
|
||||
}
|
||||
if (ports.length > 0) {
|
||||
ports
|
||||
.filter((el) => el)
|
||||
.forEach((el) => {
|
||||
const iface = normalizeInterface(el)
|
||||
const port = this.ports.get(iface)
|
||||
if (port) {
|
||||
port.lastState = 'up'
|
||||
port.tested = true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return this.getTestedPorts()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
import { Progress } from "@mantine/core";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
interface AutoProgressProps {
|
||||
ms: number; // thời gian chạy từ 0 -> 100
|
||||
start: boolean;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export default function AutoProgress({ ms, start, style }: AutoProgressProps) {
|
||||
const [value, setValue] = useState(0);
|
||||
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// cleanup cũ
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
|
||||
if (!start) {
|
||||
setValue(0);
|
||||
return;
|
||||
}
|
||||
|
||||
const stepTime = 50; // update mỗi 50ms
|
||||
const stepValue = 100 / (ms / stepTime);
|
||||
|
||||
intervalRef.current = setInterval(() => {
|
||||
setValue((prev) => {
|
||||
if (prev + stepValue >= 100) {
|
||||
return 0; // reset và chạy vòng mới
|
||||
}
|
||||
return prev + stepValue;
|
||||
});
|
||||
}, stepTime);
|
||||
|
||||
return () => {
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [start, ms]);
|
||||
|
||||
return <Progress style={style} value={value} animated />;
|
||||
}
|
||||
|
|
@ -52,6 +52,7 @@ import ModalSelectIOS from "./ModalSelectIOS";
|
|||
import ModalSelectLicense from "./ModalSelectLicense";
|
||||
import ModalRunScenario from "./ModalRunScenario";
|
||||
import DrawerScenario from "./ModalScenario";
|
||||
import AutoProgress from "../Components/AutoProgress";
|
||||
const apiUrl = import.meta.env.VITE_BACKEND_URL;
|
||||
|
||||
const INIT_TICKET = {
|
||||
|
|
@ -115,6 +116,11 @@ const ModalTerminal = ({
|
|||
const [openSelectLicense, setOpenSelectLicense] = useState<boolean>(false);
|
||||
const [openScenarioModal, setOpenScenarioModal] = useState<boolean>(false);
|
||||
const [openDrawerScenario, setOpenDrawerScenario] = useState<boolean>(false);
|
||||
const [isPhysicalTest, setIsPhysicalTest] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsPhysicalTest(line?.runningPhysical || false);
|
||||
}, [line?.runningPhysical]);
|
||||
|
||||
useEffect(() => {
|
||||
if (opened && line?.tickets && line?.tickets?.length > 0) {
|
||||
|
|
@ -437,7 +443,10 @@ const ModalTerminal = ({
|
|||
if (!port) return "";
|
||||
|
||||
// Example inputs: "Fa0/1", "Gi0/0/1", "Fa0/0/2"
|
||||
const match = port.match(/^([A-Za-z]+)([\d/]+)$/);
|
||||
const match = port
|
||||
.replace("(SFP)", "")
|
||||
.trim()
|
||||
.match(/^([A-Za-z]+)([\d/]+)$/);
|
||||
|
||||
if (!match) return port;
|
||||
|
||||
|
|
@ -1034,16 +1043,17 @@ const ModalTerminal = ({
|
|||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="physical">
|
||||
<fieldset
|
||||
style={{
|
||||
marginTop: "12px",
|
||||
}}
|
||||
>
|
||||
<AutoProgress
|
||||
start={isPhysicalTest || false}
|
||||
ms={5000}
|
||||
style={{ marginTop: "8px" }}
|
||||
/>
|
||||
<fieldset>
|
||||
<legend>
|
||||
<Flex>
|
||||
<Text>
|
||||
List ports{" "}
|
||||
{line?.runningPhysical
|
||||
{isPhysicalTest
|
||||
? `(${line?.listPortsPhysical?.length || 0}/${
|
||||
line?.ports?.length || 0
|
||||
})`
|
||||
|
|
@ -1053,10 +1063,10 @@ const ModalTerminal = ({
|
|||
</legend>
|
||||
<ScrollArea h={"45vh"} p={"4px"}>
|
||||
<SimpleGrid cols={5} spacing={"xs"}>
|
||||
{line?.runningPhysical && line?.ports
|
||||
{isPhysicalTest && line?.ports
|
||||
? line.ports.map((port, i) => (
|
||||
<Text
|
||||
fz={"16px"}
|
||||
fz={"15px"}
|
||||
key={i}
|
||||
c={
|
||||
line?.listPortsPhysical?.includes(port)
|
||||
|
|
@ -1065,6 +1075,22 @@ const ModalTerminal = ({
|
|||
}
|
||||
>
|
||||
{normalizePortName(port)}
|
||||
{port?.includes("SFP") ? (
|
||||
<Text
|
||||
mt={-4}
|
||||
fz={"11px"}
|
||||
key={i}
|
||||
c={
|
||||
line?.listPortsPhysical?.includes(port)
|
||||
? "#014a1a"
|
||||
: "#dedede"
|
||||
}
|
||||
>
|
||||
SFP
|
||||
</Text>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</Text>
|
||||
))
|
||||
: null}
|
||||
|
|
@ -1074,7 +1100,7 @@ const ModalTerminal = ({
|
|||
<Flex justify={"space-between"}>
|
||||
<Button
|
||||
fw={400}
|
||||
disabled={isDisable || !line?.runningPhysical}
|
||||
disabled={isDisable || !isPhysicalTest}
|
||||
variant="outline"
|
||||
color="yellow"
|
||||
size="xs"
|
||||
|
|
@ -1094,7 +1120,7 @@ const ModalTerminal = ({
|
|||
>
|
||||
Reset
|
||||
</Button>
|
||||
{line?.runningPhysical ? (
|
||||
{isPhysicalTest ? (
|
||||
<Button
|
||||
fw={400}
|
||||
disabled={isDisable}
|
||||
|
|
@ -1133,7 +1159,7 @@ const ModalTerminal = ({
|
|||
setIsDisable(true);
|
||||
setTimeout(() => {
|
||||
setIsDisable(false);
|
||||
}, 10000);
|
||||
}, 15000);
|
||||
}}
|
||||
>
|
||||
Start Physical Test
|
||||
|
|
|
|||
Loading…
Reference in New Issue