From ef1ba4ac9945737ab8aba7b435fc73fb37d03f05 Mon Sep 17 00:00:00 2001 From: nguyentrungthat <80239428+nguentrungthat@users.noreply.github.com> Date: Tue, 30 Dec 2025 16:04:24 +0700 Subject: [PATCH] Update scenario load ios --- BACKEND/app/services/line_connection.ts | 386 ++++++++++++++++++++--- BACKEND/app/ultils/helper.ts | 4 +- BACKEND/providers/socket_io_provider.ts | 117 ++++--- FRONTEND/src/components/ButtonAction.tsx | 30 +- 4 files changed, 428 insertions(+), 109 deletions(-) diff --git a/BACKEND/app/services/line_connection.ts b/BACKEND/app/services/line_connection.ts index 2e64816..0b49800 100644 --- a/BACKEND/app/services/line_connection.ts +++ b/BACKEND/app/services/line_connection.ts @@ -26,6 +26,7 @@ import Line from '#models/line' import { ErrorRow, TestResult } from '../ultils/types.js' import momentTZ from 'moment-timezone' import { PhysicalPortTest } from './physical_test_service.js' +import Station from '#models/station' type Inventory = { pid: string @@ -377,6 +378,7 @@ export default class LineConnection { this.breakSpam() } } + const now = Date.now() this.outputScenario += `\n\n---start-scenarios---${now}---${userName}---${script?.title}---\n---scenario---${script?.title}---${now}---\n` appendLog( @@ -393,46 +395,54 @@ export default class LineConnection { const steps = typeof script?.body === 'string' ? JSON.parse(script?.body) : [] let stepIndex = 0 - return new Promise((resolve, reject) => { - const timeoutTimer = setTimeout( - () => { - this.config.runningScenario = '' - this.socketIO.emit('running_scenario', { - stationId: this.config.stationId, - lineId: this.config.id, - title: '', - }) - this.outputBuffer = '' - this.outputScenario = '' - this.config.output += 'Timeout run scenario' - this.dataDPELP = { - line: this.config.lineNumber, - pid: '', - vid: '', - sn: '', - ios: '', - mac: '', - license: [], - issues: ['No data'], - summary: '', - } - this.socketIO.emit('line_output', { - stationId: this.config.stationId, - lineId: this.config.id, - data: 'Timeout run scenario', - }) - this.outputScenario += `\n---end-scenarios---${now}---${userName}---\n` - appendLog( - `\n---end-scenarios---${now}---${userName}---\n`, - this.config.stationId, - this.config.stationName, - this.config.stationIp, - this.config.lineNumber - ) - // reject(new Error('Script timeout')) - }, - script.timeout ? Number(script.timeout) * 1000 : 300000 + // Create a timeout + let timeoutTimer: NodeJS.Timeout | null = null + const timeoutNumber = script.timeout ? Number(script.timeout) * 1000 : 300000 + const onTimeout = () => { + this.config.runningScenario = '' + this.socketIO.emit('running_scenario', { + stationId: this.config.stationId, + lineId: this.config.id, + title: '', + }) + this.outputBuffer = '' + this.outputScenario = '' + this.config.output += 'Timeout run scenario' + this.dataDPELP = { + line: this.config.lineNumber, + pid: '', + vid: '', + sn: '', + ios: '', + mac: '', + license: [], + issues: ['No data'], + summary: '', + } + this.socketIO.emit('line_output', { + stationId: this.config.stationId, + lineId: this.config.id, + data: 'Timeout run scenario', + }) + this.outputScenario += `\n---end-scenarios---${now}---${userName}---\n` + appendLog( + `\n---end-scenarios---${now}---${userName}---\n`, + this.config.stationId, + this.config.stationName, + this.config.stationIp, + this.config.lineNumber ) + // reject(new Error('Script timeout')) + } + const resetTimeout = () => { + // console.log('resetTimeout', timeoutNumber) + // this.outputBuffer = '' + if (timeoutTimer) clearTimeout(timeoutTimer) + timeoutTimer = setTimeout(onTimeout, timeoutNumber) + } + + return new Promise((resolve, reject) => { + timeoutTimer = setTimeout(onTimeout, timeoutNumber) const runStep = async (index: number) => { if (index >= steps.length) { @@ -442,7 +452,7 @@ export default class LineConnection { runStep(index) }, 5000) return - } else clearTimeout(timeoutTimer) + } else if (timeoutTimer) clearTimeout(timeoutTimer) this.outputScenario += `\n---end-scenarios---${now}---${userName}---\n` this.outputBuffer = '' this.config.runningScenario = '' @@ -536,8 +546,6 @@ export default class LineConnection { } catch (error) { console.log(error) } - // this.outputBuffer = '' - // this.outputScenario += `\n---end-scenarios---${now}---${userName}---\n` appendLog( `\n---end-scenarios---${now}---${userName}---\n`, this.config.stationId, @@ -548,19 +556,20 @@ export default class LineConnection { this.listScenarios = [] resolve(true) return - } + } else resetTimeout() const step = steps[index] let repeatCount = Number(step.repeat) || 1 const delay = step?.delay ? Number(step?.delay) * 1000 : 1000 const sendCommand = async () => { - if (repeatCount <= 0) { - // Done → next step - stepIndex++ - return runStep(stepIndex) - } + // if (repeatCount <= 0) { + // // Done → next step + // stepIndex++ + // return runStep(stepIndex) + // } if (typeof step.send !== 'undefined') { + console.log(Date.now() - now, (step?.send ?? '[ENTER]').toString()) this.outputScenario += `\n---send-command---"${(step?.send ?? '[ENTER]').toString().replace(/\r/g, '\\r').replace(/\n/g, '\\n')}"---${now}---\n` appendLog( `\n---send-command---"${(step?.send ?? '[ENTER]').toString().replace(/\r/g, '\\r').replace(/\n/g, '\\n')}"---${now}---\n`, @@ -573,7 +582,11 @@ export default class LineConnection { } repeatCount-- - setTimeout(() => sendCommand(), delay) + if (repeatCount <= 0) { + // Done → next step + stepIndex++ + return runStep(stepIndex) + } else setTimeout(() => sendCommand(), delay) } // Nếu expect rỗng → gửi ngay @@ -1043,4 +1056,279 @@ export default class LineConnection { formReport ) } + + async loadIosRouter(nameIos: string, userName: string) { + const station = await Station.find(this.config.stationId) + if (!station) return + const network = station?.gateway || '172.25.1.1' + const [a, b] = network.split('.').map(Number) + + const body = [ + { + expect: '', + send: `IP_ADDRESS=${a}.${b}.100.${this.config.id < 254 ? this.config.id : 254 - this.config.id}`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'rommon', + send: `IP_SUBNET_MASK=255.255.0.0`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'rommon', + send: `DEFAULT_GATEWAY=${station?.gateway ? station?.gateway : '0.0.0.0'}`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'rommon', + send: `TFTP_SERVER=${station?.tftp_ip}`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'rommon', + send: `TFTP_FILE=ios/${nameIos}`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'rommon', + send: `tftpdnld`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'y/n', + send: `y`, + delay: '2', + repeat: '1', + note: '', + }, + { + expect: 'rommon', + send: `boot usbflash0:${nameIos}`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'Press RETURN to get started', + send: ``, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '', + send: ``, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '', + send: `enable`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '#', + send: `configure terminal`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '', + send: `boot system usbflash0:${nameIos}`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '', + send: `end`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '', + send: `write memory`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '', + send: ``, + delay: '1', + repeat: '1', + note: '', + }, + ] + + const script = { + id: 0, + isReboot: true, + sendResult: false, + send_result: false, + title: 'Load IOS Router', + timeout: 1000000, + body: JSON.stringify(body), + } + + this.runScript(script as any, userName) + } + + async loadIosSwitch(nameIos: string, userName: string) { + const station = await Station.find(this.config.stationId) + if (!station) return + const network = station?.gateway || '172.25.1.1' + const [a, b] = network.split('.').map(Number) + + const body = [ + { + expect: '', + send: ``, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '', + send: `IP_ADDRESS=${a}.${b}.100.${this.config.id < 254 ? this.config.id : 254 - this.config.id}`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'switch:', + send: `IP_SUBNET_MASK=255.255.0.0`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'switch:', + send: `DEFAULT_GATEWAY=${station?.gateway ? station?.gateway : '0.0.0.0'}`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'switch:', + send: `TFTP_SERVER=${station?.tftp_ip}`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'switch:', + send: `TFTP_FILE=ios/${nameIos}`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'switch:', + send: `tftpdnld`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'y/n', + send: `y`, + delay: '2', + repeat: '1', + note: '', + }, + { + expect: 'switch:', + send: `boot flash:${nameIos}`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: 'Press RETURN to get started', + send: ``, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '', + send: ``, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '', + send: `enable`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '#', + send: `configure terminal`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '', + send: `boot system usbflash0:${nameIos}`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '', + send: `end`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '', + send: `write memory`, + delay: '1', + repeat: '1', + note: '', + }, + { + expect: '', + send: ``, + delay: '1', + repeat: '1', + note: '', + }, + ] + + const script = { + id: 0, + isReboot: false, + sendResult: false, + send_result: false, + title: 'Load IOS Switch', + timeout: 1000000, + body: JSON.stringify(body), + } + + this.runScript(script as any, userName) + } } diff --git a/BACKEND/app/ultils/helper.ts b/BACKEND/app/ultils/helper.ts index 2e2ac56..7c8ce81 100644 --- a/BACKEND/app/ultils/helper.ts +++ b/BACKEND/app/ultils/helper.ts @@ -8,8 +8,8 @@ import axios from 'axios' import moment from 'moment' const mailTo = 'andrew.ng@apactech.io' -// const mailCC = ['ips@ipsupply.com.au', 'kay@ipsupply.com.au', 'joseph@apactech.io'] -const mailCC = '' +const mailCC = ['ips@ipsupply.com.au', 'kay@ipsupply.com.au', 'joseph@apactech.io'] +// const mailCC = '' type DetectAI = { status: string[] diff --git a/BACKEND/providers/socket_io_provider.ts b/BACKEND/providers/socket_io_provider.ts index 37f0f6a..7406c22 100644 --- a/BACKEND/providers/socket_io_provider.ts +++ b/BACKEND/providers/socket_io_provider.ts @@ -339,57 +339,62 @@ export class WebSocketIo { }) socket.on('control_apc', async (data) => { - const { outletNumbers, action, station, apcName } = data - if (action === 'reconnect') { - if (!station) return - const apcIp = (station as any)[`${apcName}_ip`] as string - const apc = this.apcsControl.get(apcIp) - if (apc) { - await apc.reconnect() - this.keepConnectAPC(apcIp, io) - } else await this.connectApc(io, apcName, station) - } else { - for (const outletNumber of outletNumbers) { - if (!outletNumber || outletNumber < 0) return + try { + const { outletNumbers, action, station, apcName } = data + if (action === 'reconnect') { if (!station) return - // find line from station by apcName and outletNumber - const lines = await Line.query() - .where('station_id', station.id) - .andWhere('apc_name', apcName) - .andWhere('outlet', outletNumber) - if (lines.length > 0) { - const line = this.lineMap.get(lines[0].id) - if (line) this.setTimeoutConnect(lines[0].id, line) - } - const apcIp = (station as any)[`${apcName}_ip`] as string const apc = this.apcsControl.get(apcIp) - if (apc && apc.status !== 'CONNECTED') { + if (apc) { await apc.reconnect() this.keepConnectAPC(apcIp, io) - } - if (apc) { - switch (action) { - case 'on': - await apc?.turnOnOutlet(outletNumber) - break - case 'off': - await apc?.turnOffOutlet(outletNumber) - break - case 'restart': - await apc?.restartOutlet(outletNumber) - break - case 'reconnect': - await apc?.reconnect() - break - default: - break + } else await this.connectApc(io, apcName, station) + } else { + for (const outletNumber of outletNumbers) { + if (!outletNumber || outletNumber < 0) return + if (!station) return + // find line from station by apcName and outletNumber + const lines = await Line.query() + .where('station_id', station.id) + .andWhere('apc_name', apcName) + .andWhere('outlet', outletNumber) + if (lines.length > 0) { + const line = this.lineMap.get(lines[0].id) + if (line) this.setTimeoutConnect(lines[0].id, line) + } + + const apcIp = (station as any)[`${apcName}_ip`] as string + if (!this.apcsControl.get(apcIp)) await this.connectApc(io, apcName, station) + const apc = this.apcsControl.get(apcIp) + if (apc && apc.status !== 'CONNECTED') { + await apc.reconnect() + this.keepConnectAPC(apcIp, io) + } + if (apc) { + switch (action) { + case 'on': + await apc?.turnOnOutlet(outletNumber) + break + case 'off': + await apc?.turnOffOutlet(outletNumber) + break + case 'restart': + await apc?.restartOutlet(outletNumber) + break + case 'reconnect': + await apc?.reconnect() + break + default: + break + } + setTimeout(() => { + apc?.navigateToOutlets() + }, 10000) } - setTimeout(() => { - apc?.navigateToOutlets() - }, 10000) } } + } catch (e) { + console.log('control_apc', e) } }) @@ -636,6 +641,32 @@ export class WebSocketIo { {} ) }) + + socket.on('load_ios_router', async (data) => { + const { stationId, lineId, iosName } = data + await this.handleLineOperation( + io, + stationId, + [lineId], + async (lineCon) => { + lineCon.loadIosRouter(iosName, userName) + }, + {} + ) + }) + + socket.on('load_ios_switch', async (data) => { + const { stationId, lineId, iosName } = data + await this.handleLineOperation( + io, + stationId, + [lineId], + async (lineCon) => { + lineCon.loadIosSwitch(iosName, userName) + }, + {} + ) + }) }) socketServer.listen(SOCKET_IO_PORT, () => { diff --git a/FRONTEND/src/components/ButtonAction.tsx b/FRONTEND/src/components/ButtonAction.tsx index d50d56f..f71f060 100644 --- a/FRONTEND/src/components/ButtonAction.tsx +++ b/FRONTEND/src/components/ButtonAction.tsx @@ -42,105 +42,105 @@ export const ButtonDPELP = ({ { expect: "", send: "", - delay: "1000", + delay: "1", repeat: "1", note: "", }, { expect: "", send: " no", - delay: "1000", + delay: "1", repeat: "1", note: "", }, { expect: "", send: "\r\n", - delay: "1000", + delay: "1", repeat: "1", note: "", }, { expect: "", send: "\r\n", - delay: "2000", + delay: "2", repeat: "1", note: "", }, // { // expect: "", // send: " terminal length 0", - // delay: "3000", + // delay: "3", // repeat: "1", // note: "", // }, { expect: "", send: "enable", - delay: "3000", + delay: "3", repeat: "1", note: "", }, { expect: "", send: "show inventory", - delay: "3000", + delay: "3", repeat: "1", note: "", }, { expect: "", send: "show version | include License", - delay: "1000", + delay: "1", repeat: "1", note: "", }, { expect: "", send: "show version", - delay: "1000", + delay: "1", repeat: "1", note: "", }, { expect: "", send: "show diag", - delay: "3000", + delay: "3", repeat: "1", note: "", }, { expect: "", send: "show post", - delay: "3000", + delay: "3", repeat: "1", note: "", }, { expect: "", send: "show env all", - delay: "3000", + delay: "3", repeat: "1", note: "", }, { expect: "", send: "show license", - delay: "3000", + delay: "3", repeat: "1", note: "", }, { expect: "", send: "show log", - delay: "3000", + delay: "3", repeat: "1", note: "", }, { expect: "", send: "show platform", - delay: "3000", + delay: "3", repeat: "1", note: "", },