Update status is ready for line

This commit is contained in:
nguyentrungthat 2026-02-24 09:36:36 +07:00
parent 669c30ca11
commit 05950726bf
7 changed files with 108 additions and 22 deletions

View File

@ -4,6 +4,7 @@ import net from 'node:net'
import { import {
appendLog, appendLog,
buildBody, buildBody,
canInputCommand,
classifyLog, classifyLog,
cleanData, cleanData,
convertFromKilobytesString, convertFromKilobytesString,
@ -139,6 +140,7 @@ export default class LineConnection {
private testingPortPoE: boolean private testingPortPoE: boolean
private outputTestingPortPoE: string private outputTestingPortPoE: string
private debounceSendSummaryReport: NodeJS.Timeout | null = null private debounceSendSummaryReport: NodeJS.Timeout | null = null
public isReady: boolean
constructor(config: LineConfig, socketIO: any, handleClearLine: () => void) { constructor(config: LineConfig, socketIO: any, handleClearLine: () => void) {
this.config = config this.config = config
@ -172,6 +174,7 @@ export default class LineConnection {
this.debounceSendSummaryReport = null this.debounceSendSummaryReport = null
this.testingPortPoE = false this.testingPortPoE = false
this.outputTestingPortPoE = '' this.outputTestingPortPoE = ''
this.isReady = false
} }
/** /**
* Connect to line with socket * Connect to line with socket
@ -203,7 +206,7 @@ export default class LineConnection {
this.sendFeatureTested() this.sendFeatureTested()
this.checkLog() this.checkLog()
resolve() resolve()
}, 1000) }, 2000)
}) })
this.client.on('data', (data) => { this.client.on('data', (data) => {
@ -248,11 +251,15 @@ export default class LineConnection {
} }
this.config.output += cleanData(rawData) this.config.output += cleanData(rawData)
this.config.output = this.config.output.slice(-15000) this.config.output = this.config.output.slice(-15000)
if (!this.isReady && canInputCommand(message)) {
this.isReady = true
}
this.socketIO.emit('line_output', { this.socketIO.emit('line_output', {
stationId, stationId,
lineId: id, lineId: id,
data: message, data: message,
ports: this.config.ports, ports: this.config.ports,
isReady: this.isReady ? true : canInputCommand(message),
}) })
setTimeout(() => { setTimeout(() => {
if (!this.config.inventory) { if (!this.config.inventory) {
@ -289,6 +296,7 @@ export default class LineConnection {
this.config.listFeatureTested = [] this.config.listFeatureTested = []
this.config.latestScenario = undefined this.config.latestScenario = undefined
this.physicalTest = new PhysicalPortTest([]) this.physicalTest = new PhysicalPortTest([])
this.isReady = false
// this.config.inventory = undefined // this.config.inventory = undefined
this.socketIO.emit('line_disconnected', { this.socketIO.emit('line_disconnected', {
stationId, stationId,
@ -342,7 +350,7 @@ export default class LineConnection {
/** /**
* Write a command with socket.write * Write a command with socket.write
*/ */
async writeCommand(cmd: string | Buffer<ArrayBuffer>, userName = '') { async writeCommand(cmd: string | Buffer<ArrayBuffer>) {
if (this.client.destroyed) { if (this.client.destroyed) {
console.log(`⚠️ Cannot send, line ${this.config.lineNumber} is closed`) console.log(`⚠️ Cannot send, line ${this.config.lineNumber} is closed`)
this.disconnect() this.disconnect()
@ -394,6 +402,10 @@ export default class LineConnection {
this.outputBuffer = '' this.outputBuffer = ''
return return
} }
if (!this.isReady) {
console.log('Device is not ready')
return
}
if (this.config.runningScenario || this.config.runningPhysical) { if (this.config.runningScenario || this.config.runningPhysical) {
console.log('Script already running') console.log('Script already running')
return return
@ -594,18 +606,8 @@ export default class LineConnection {
] ]
this.sendFeatureTested() this.sendFeatureTested()
// Debounce send summary report // Set timeout send report
if (this.debounceSendSummaryReport) clearTimeout(this.debounceSendSummaryReport) this.setTimeoutSendSummaryReport(600000)
// Snapshot toàn bộ data tại thời điểm này
const snapshot = {
snapConfig: this.config,
snapPhysical: this.physicalTest,
}
this.debounceSendSummaryReport = setTimeout(() => {
this.config.listFeatureTested = ['DPELP', 'PHYSICAL']
this.sendFeatureTested()
this.sendReportSummary(snapshot)
}, 600000) // 10p debounce
// } // }
if (this.config.latestScenario) if (this.config.latestScenario)
@ -1262,6 +1264,8 @@ export default class LineConnection {
async sendReportPhysicalTest() { async sendReportPhysicalTest() {
this.config.listFeatureTested = [...new Set([...this.config.listFeatureTested, 'PHYSICAL'])] this.config.listFeatureTested = [...new Set([...this.config.listFeatureTested, 'PHYSICAL'])]
this.sendFeatureTested() this.sendFeatureTested()
// Set timeout send report
this.setTimeoutSendSummaryReport(180000)
const formReport = this.physicalTest.getFormReport() const formReport = this.physicalTest.getFormReport()
await sendMessageToMail( await sendMessageToMail(
`[ATC] - [${this.config.stationName} - Line: ${this.config.lineNumber}] - Physical Ports Test`, `[ATC] - [${this.config.stationName} - Line: ${this.config.lineNumber}] - Physical Ports Test`,
@ -1836,4 +1840,30 @@ ${log}
} }
this.physicalTest = new PhysicalPortTest([]) this.physicalTest = new PhysicalPortTest([])
} }
setTimeoutSendSummaryReport(timeout: number) {
// Debounce send summary report
if (this.debounceSendSummaryReport) clearTimeout(this.debounceSendSummaryReport)
// Snapshot toàn bộ data tại thời điểm này
const snapshot = {
snapConfig: this.config,
snapPhysical: this.physicalTest,
}
this.debounceSendSummaryReport = setTimeout(() => {
this.config.listFeatureTested = ['DPELP', 'PHYSICAL']
this.sendFeatureTested()
this.sendReportSummary(snapshot)
}, timeout)
}
/**
* Send is ready
*/
sendIsReady = async () => {
this.socketIO.emit('line_is_ready', {
stationId: this.config.stationId,
lineId: this.config.id,
isReady: this.isReady,
})
}
} }

View File

@ -1579,3 +1579,32 @@ export function convertFromKilobytesString(input: string, decimals = 0): string
return `${displayValue.toFixed(decimals)} ${units[unitIndex]}` return `${displayValue.toFixed(decimals)} ${units[unitIndex]}`
} }
export function canInputCommand(buffer: string): boolean {
if (!buffer) return false
const data = buffer.toString()
// IOS prompt (hostname> hoặc hostname#)
if (/[\r\n]?[\w.-]+[>#]\s?$/.test(data)) return true
// Username / Password
if (data.includes('Username:')) return true
if (data.includes('Password:')) return true
// ROMMON
if (/rommon\s+\d+\s+>/i.test(data)) return true
// Switch loader
if (data.includes('switch:')) return true
// Press RETURN
if (data.includes('Press RETURN to get started!')) return true
// yes/no cases
if (/\[(yes\/no|confirm)\]/i.test(data)) return true
if (/\((yes\/no|y\/n)\)/i.test(data)) return true
if (/yes\/no/i.test(data)) return true
return false
}

View File

@ -195,7 +195,7 @@ export class WebSocketIo {
stationId, stationId,
lineIds, lineIds,
async (line) => async (line) =>
command === 'spam_break' ? line.breakSpam() : line.writeCommand(command, userName), command === 'spam_break' ? line.breakSpam() : line.writeCommand(command),
{ command } { command }
) )
}) })
@ -257,7 +257,7 @@ export class WebSocketIo {
io, io,
stationId, stationId,
[lineId], [lineId],
async (lineCon) => lineCon.writeCommand('\r\n', userName), async (lineCon) => lineCon.writeCommand('\r\n'),
{ command: '\r\n' } { command: '\r\n' }
) )
} else { } else {

View File

@ -264,11 +264,11 @@ function App() {
socket?.on("line_output", (data) => { socket?.on("line_output", (data) => {
const { lineId, data: text } = data; const { lineId, data: text } = data;
// updateValueLineStation( updateValueLineStation(
// data?.lineId, data?.lineId,
// { netOutput: data.data, commands: data.commands || [] }, { isReady: data.isReady },
// data?.stationId data?.stationId
// ); );
const buf = lineBuffersRef.current.get(lineId) || ""; const buf = lineBuffersRef.current.get(lineId) || "";
lineBuffersRef.current.set(lineId, buf + text); lineBuffersRef.current.set(lineId, buf + text);

View File

@ -130,7 +130,7 @@ const CardLine = ({
Array.isArray(line?.latestScenario?.detectAI?.issue) Array.isArray(line?.latestScenario?.detectAI?.issue)
? "- " + line?.latestScenario?.detectAI?.issue?.join("\n- ") ? "- " + line?.latestScenario?.detectAI?.issue?.join("\n- ")
: ""; : "";
if (data) setIsShowIssue(true); if (data && !data.includes("No issues detected")) setIsShowIssue(true);
else setIsShowIssue(false); else setIsShowIssue(false);
} else setIsShowIssue(false); } else setIsShowIssue(false);
}, [line?.latestScenario]); }, [line?.latestScenario]);
@ -336,6 +336,19 @@ const CardLine = ({
connecting... connecting...
</motion.div> </motion.div>
)} )}
{!line?.isReady && line?.status === "connected" && (
<motion.div
style={{ fontSize: "11px", color: "red" }}
animate={{ opacity: [0.2, 1, 0.2] }}
transition={{
duration: 1.2,
repeat: Infinity,
ease: "easeInOut",
}}
>
booting...
</motion.div>
)}
{line?.runningScenario && ( {line?.runningScenario && (
<motion.div <motion.div
style={{ fontSize: "11px", color: "red" }} style={{ fontSize: "11px", color: "red" }}

View File

@ -590,6 +590,19 @@ const ModalTerminal = ({
connecting... connecting...
</motion.div> </motion.div>
)} )}
{!line?.isReady && line?.status === "connected" && (
<motion.div
style={{ fontSize: "12px", color: "red" }}
animate={{ opacity: [0.2, 1, 0.2] }}
transition={{
duration: 1.2,
repeat: Infinity,
ease: "easeInOut",
}}
>
booting...
</motion.div>
)}
{line?.runningScenario && ( {line?.runningScenario && (
<motion.div <motion.div
style={{ fontSize: "12px", color: "red" }} style={{ fontSize: "12px", color: "red" }}

View File

@ -108,6 +108,7 @@ export type TLine = {
runningPhysical?: boolean; runningPhysical?: boolean;
listPortsPhysical?: string[]; listPortsPhysical?: string[];
listFeatureTested?: string[]; listFeatureTested?: string[];
isReady?: boolean;
}; };
export type TUser = { export type TUser = {