From 05950726bfa8328ac76e0f5b915d385cc628e1ab Mon Sep 17 00:00:00 2001 From: nguyentrungthat <80239428+nguentrungthat@users.noreply.github.com> Date: Tue, 24 Feb 2026 09:36:36 +0700 Subject: [PATCH] Update status is ready for line --- BACKEND/app/services/line_connection.ts | 58 ++++++++++++++----- BACKEND/app/ultils/helper.ts | 29 ++++++++++ BACKEND/providers/socket_io_provider.ts | 4 +- FRONTEND/src/App.tsx | 10 ++-- FRONTEND/src/components/CardLine.tsx | 15 ++++- .../src/components/Modal/ModalTerminal.tsx | 13 +++++ FRONTEND/src/untils/types.ts | 1 + 7 files changed, 108 insertions(+), 22 deletions(-) diff --git a/BACKEND/app/services/line_connection.ts b/BACKEND/app/services/line_connection.ts index 7378809..9deb010 100644 --- a/BACKEND/app/services/line_connection.ts +++ b/BACKEND/app/services/line_connection.ts @@ -4,6 +4,7 @@ import net from 'node:net' import { appendLog, buildBody, + canInputCommand, classifyLog, cleanData, convertFromKilobytesString, @@ -139,6 +140,7 @@ export default class LineConnection { private testingPortPoE: boolean private outputTestingPortPoE: string private debounceSendSummaryReport: NodeJS.Timeout | null = null + public isReady: boolean constructor(config: LineConfig, socketIO: any, handleClearLine: () => void) { this.config = config @@ -172,6 +174,7 @@ export default class LineConnection { this.debounceSendSummaryReport = null this.testingPortPoE = false this.outputTestingPortPoE = '' + this.isReady = false } /** * Connect to line with socket @@ -203,7 +206,7 @@ export default class LineConnection { this.sendFeatureTested() this.checkLog() resolve() - }, 1000) + }, 2000) }) this.client.on('data', (data) => { @@ -248,11 +251,15 @@ export default class LineConnection { } this.config.output += cleanData(rawData) this.config.output = this.config.output.slice(-15000) + if (!this.isReady && canInputCommand(message)) { + this.isReady = true + } this.socketIO.emit('line_output', { stationId, lineId: id, data: message, ports: this.config.ports, + isReady: this.isReady ? true : canInputCommand(message), }) setTimeout(() => { if (!this.config.inventory) { @@ -289,6 +296,7 @@ export default class LineConnection { this.config.listFeatureTested = [] this.config.latestScenario = undefined this.physicalTest = new PhysicalPortTest([]) + this.isReady = false // this.config.inventory = undefined this.socketIO.emit('line_disconnected', { stationId, @@ -342,7 +350,7 @@ export default class LineConnection { /** * Write a command with socket.write */ - async writeCommand(cmd: string | Buffer, userName = '') { + async writeCommand(cmd: string | Buffer) { if (this.client.destroyed) { console.log(`⚠️ Cannot send, line ${this.config.lineNumber} is closed`) this.disconnect() @@ -394,6 +402,10 @@ export default class LineConnection { this.outputBuffer = '' return } + if (!this.isReady) { + console.log('Device is not ready') + return + } if (this.config.runningScenario || this.config.runningPhysical) { console.log('Script already running') return @@ -594,18 +606,8 @@ export default class LineConnection { ] this.sendFeatureTested() - // 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) - }, 600000) // 10p debounce + // Set timeout send report + this.setTimeoutSendSummaryReport(600000) // } if (this.config.latestScenario) @@ -1262,6 +1264,8 @@ export default class LineConnection { async sendReportPhysicalTest() { this.config.listFeatureTested = [...new Set([...this.config.listFeatureTested, 'PHYSICAL'])] this.sendFeatureTested() + // Set timeout send report + this.setTimeoutSendSummaryReport(180000) const formReport = this.physicalTest.getFormReport() await sendMessageToMail( `[ATC] - [${this.config.stationName} - Line: ${this.config.lineNumber}] - Physical Ports Test`, @@ -1836,4 +1840,30 @@ ${log} } 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, + }) + } } diff --git a/BACKEND/app/ultils/helper.ts b/BACKEND/app/ultils/helper.ts index 6600a84..682a746 100644 --- a/BACKEND/app/ultils/helper.ts +++ b/BACKEND/app/ultils/helper.ts @@ -1579,3 +1579,32 @@ export function convertFromKilobytesString(input: string, decimals = 0): string 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 +} diff --git a/BACKEND/providers/socket_io_provider.ts b/BACKEND/providers/socket_io_provider.ts index e221dfb..08decde 100644 --- a/BACKEND/providers/socket_io_provider.ts +++ b/BACKEND/providers/socket_io_provider.ts @@ -195,7 +195,7 @@ export class WebSocketIo { stationId, lineIds, async (line) => - command === 'spam_break' ? line.breakSpam() : line.writeCommand(command, userName), + command === 'spam_break' ? line.breakSpam() : line.writeCommand(command), { command } ) }) @@ -257,7 +257,7 @@ export class WebSocketIo { io, stationId, [lineId], - async (lineCon) => lineCon.writeCommand('\r\n', userName), + async (lineCon) => lineCon.writeCommand('\r\n'), { command: '\r\n' } ) } else { diff --git a/FRONTEND/src/App.tsx b/FRONTEND/src/App.tsx index b84938e..6848cde 100644 --- a/FRONTEND/src/App.tsx +++ b/FRONTEND/src/App.tsx @@ -264,11 +264,11 @@ function App() { socket?.on("line_output", (data) => { const { lineId, data: text } = data; - // updateValueLineStation( - // data?.lineId, - // { netOutput: data.data, commands: data.commands || [] }, - // data?.stationId - // ); + updateValueLineStation( + data?.lineId, + { isReady: data.isReady }, + data?.stationId + ); const buf = lineBuffersRef.current.get(lineId) || ""; lineBuffersRef.current.set(lineId, buf + text); diff --git a/FRONTEND/src/components/CardLine.tsx b/FRONTEND/src/components/CardLine.tsx index 4d75a14..6e03e62 100644 --- a/FRONTEND/src/components/CardLine.tsx +++ b/FRONTEND/src/components/CardLine.tsx @@ -130,7 +130,7 @@ const CardLine = ({ Array.isArray(line?.latestScenario?.detectAI?.issue) ? "- " + 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); }, [line?.latestScenario]); @@ -336,6 +336,19 @@ const CardLine = ({ connecting... )} + {!line?.isReady && line?.status === "connected" && ( + + booting... + + )} {line?.runningScenario && ( )} + {!line?.isReady && line?.status === "connected" && ( + + booting... + + )} {line?.runningScenario && (