Refactor log handling and improve output buffering
Updated log file naming to include station name and IP, and refactored appendLog to use new format. Enhanced frontend output buffering for lines to reduce UI update frequency. Improved connection retry logic for switch connections and adjusted socket.io provider timeouts. Updated DrawerLogs format description and removed unnecessary state changes in TerminalXTerm.
This commit is contained in:
parent
dc47636c96
commit
36d7438055
|
|
@ -17,6 +17,8 @@ interface LineConfig {
|
|||
lineNumber: number
|
||||
ip: string
|
||||
stationId: number
|
||||
stationName: string
|
||||
stationIp: string
|
||||
apcName?: string
|
||||
outlet: number
|
||||
output: string
|
||||
|
|
@ -36,8 +38,17 @@ interface LineConfig {
|
|||
textfsm: string
|
||||
}[]
|
||||
commands: string[]
|
||||
// history: string
|
||||
}
|
||||
|
||||
/** HISTORY
|
||||
* PID
|
||||
* SN
|
||||
* VID
|
||||
* Timestamp
|
||||
* Scenario
|
||||
*/
|
||||
|
||||
interface User {
|
||||
userEmail: string
|
||||
userName: string
|
||||
|
|
@ -81,7 +92,7 @@ export default class LineConnection {
|
|||
if (resolvedOrRejected) return
|
||||
resolvedOrRejected = true
|
||||
|
||||
console.log(`✅ Connected to line ${lineNumber} (${ip}:${port})`)
|
||||
console.log(`[${Date.now()}] ✅ Connected to line ${lineNumber} (${ip}:${port})`)
|
||||
this.connecting = true
|
||||
setTimeout(() => {
|
||||
this.config.status = 'connected'
|
||||
|
|
@ -135,8 +146,9 @@ export default class LineConnection {
|
|||
appendLog(
|
||||
cleanData(message),
|
||||
this.config.stationId,
|
||||
this.config.lineNumber,
|
||||
this.config.port
|
||||
this.config.stationName,
|
||||
this.config.stationIp,
|
||||
this.config.lineNumber
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -154,7 +166,7 @@ export default class LineConnection {
|
|||
})
|
||||
|
||||
this.client.on('close', () => {
|
||||
console.log(`🔌 Line ${lineNumber} disconnected`)
|
||||
console.log(`[${Date.now()}] 🔌 Line ${lineNumber} disconnected`)
|
||||
this.config.status = 'disconnected'
|
||||
// this.config.inventory = undefined
|
||||
this.socketIO.emit('line_disconnected', {
|
||||
|
|
@ -178,8 +190,9 @@ export default class LineConnection {
|
|||
appendLog(
|
||||
cleanData(message),
|
||||
this.config.stationId,
|
||||
this.config.lineNumber,
|
||||
this.config.port
|
||||
this.config.stationName,
|
||||
this.config.stationIp,
|
||||
this.config.lineNumber
|
||||
)
|
||||
console.log(`⏳ Connection timeout line ${lineNumber}`)
|
||||
this.client.destroy()
|
||||
|
|
@ -257,8 +270,9 @@ export default class LineConnection {
|
|||
appendLog(
|
||||
`\n\n---start-scenarios---${now}---${userName}---\n---scenario---${script?.title}---${now}---\n`,
|
||||
this.config.stationId,
|
||||
this.config.lineNumber,
|
||||
this.config.port
|
||||
this.config.stationName,
|
||||
this.config.stationIp,
|
||||
this.config.lineNumber
|
||||
)
|
||||
this.config.latestScenario = {
|
||||
name: script?.title,
|
||||
|
|
@ -282,8 +296,9 @@ export default class LineConnection {
|
|||
appendLog(
|
||||
`\n---end-scenarios---${now}---${userName}---\n`,
|
||||
this.config.stationId,
|
||||
this.config.lineNumber,
|
||||
this.config.port
|
||||
this.config.stationName,
|
||||
this.config.stationIp,
|
||||
this.config.lineNumber
|
||||
)
|
||||
// reject(new Error('Script timeout'))
|
||||
}, script.timeout || 300000)
|
||||
|
|
@ -303,8 +318,9 @@ export default class LineConnection {
|
|||
appendLog(
|
||||
`\n---end-scenarios---${now}---${userName}---\n`,
|
||||
this.config.stationId,
|
||||
this.config.lineNumber,
|
||||
this.config.port
|
||||
this.config.stationName,
|
||||
this.config.stationIp,
|
||||
this.config.lineNumber
|
||||
)
|
||||
|
||||
const logScenarios = getLogWithTimeScenario(this.outputScenario, now) || ''
|
||||
|
|
@ -342,8 +358,9 @@ export default class LineConnection {
|
|||
appendLog(
|
||||
`\n---send-command---"${step?.send ?? ''}"---${now}---\n`,
|
||||
this.config.stationId,
|
||||
this.config.lineNumber,
|
||||
this.config.port
|
||||
this.config.stationName,
|
||||
this.config.stationIp,
|
||||
this.config.lineNumber
|
||||
)
|
||||
let repeatCount = Number(step.repeat) || 1
|
||||
const sendCommand = async () => {
|
||||
|
|
@ -396,8 +413,9 @@ export default class LineConnection {
|
|||
appendLog(
|
||||
`\n-------${user.userName}-------\n`,
|
||||
this.config.stationId,
|
||||
this.config.lineNumber,
|
||||
this.config.port
|
||||
this.config.stationName,
|
||||
this.config.stationIp,
|
||||
this.config.lineNumber
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -457,8 +475,9 @@ export default class LineConnection {
|
|||
appendLog(
|
||||
cleanData(data),
|
||||
this.config.stationId,
|
||||
this.config.lineNumber,
|
||||
this.config.port
|
||||
this.config.stationName,
|
||||
this.config.stationIp,
|
||||
this.config.lineNumber
|
||||
)
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ export default class SwitchController {
|
|||
public ports: PortInfo[]
|
||||
public portGroups: PortInfo[][]
|
||||
private isEnable: boolean
|
||||
private retryConnect: number
|
||||
|
||||
constructor({ host, port = 23, username, password, onData }: SwitchControllerOptions) {
|
||||
this.host = host
|
||||
|
|
@ -47,6 +48,7 @@ export default class SwitchController {
|
|||
this.ports = []
|
||||
this.portGroups = []
|
||||
this.isEnable = false
|
||||
this.retryConnect = 0
|
||||
}
|
||||
|
||||
private sleep(ms: number) {
|
||||
|
|
@ -65,10 +67,17 @@ export default class SwitchController {
|
|||
}
|
||||
}
|
||||
|
||||
private _handleClose() {
|
||||
private async _handleClose(err: boolean) {
|
||||
console.log('[SWITCH CONNECTION CLOSE]', err)
|
||||
this.status = 'DISCONNECTED'
|
||||
this.isEnable = false
|
||||
this.onData(this.portGroups, this.status)
|
||||
if (this.retryConnect <= 5) {
|
||||
await this.sleep(15000)
|
||||
console.log('Retry connect times', this.retryConnect)
|
||||
this.retryConnect += 1
|
||||
await this.reconnect()
|
||||
}
|
||||
}
|
||||
|
||||
private _handleError(err: Error & { code?: string }) {
|
||||
|
|
@ -113,8 +122,8 @@ export default class SwitchController {
|
|||
this.socket.on('data', (data) => this._handleData(data.toString()))
|
||||
resolve()
|
||||
})
|
||||
this.socket.on('close', () => {
|
||||
this._handleClose()
|
||||
this.socket.on('close', (e) => {
|
||||
this._handleClose(e)
|
||||
resolve()
|
||||
})
|
||||
this.socket.on('error', (err) => {
|
||||
|
|
|
|||
|
|
@ -31,10 +31,20 @@ export function sleep(ms: number) {
|
|||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
export function appendLog(output: string, stationId: number, lineNumber: number, port: number) {
|
||||
// 20250527-AUTO-Session.Station_1-13-192.168.171.9-2.log
|
||||
// {DATE}-AUTO-Session.{Station name}-{Station ID}-{Station IP}-{Line number}.log
|
||||
export function appendLog(
|
||||
output: string,
|
||||
stationId: number,
|
||||
stationName: string,
|
||||
stationIP: string,
|
||||
lineNumber: number
|
||||
) {
|
||||
const date = new Date().toISOString().slice(0, 10).replace(/-/g, '') // YYYYMMDD
|
||||
const logDir = path.join('storage', 'system_logs')
|
||||
const logFile = path.join(logDir, `${date}-Station_${stationId}-Line_${lineNumber}_${port}.log`)
|
||||
const logFile = path
|
||||
.join(logDir, `${date}-AUTO-Session.${stationName}-${stationId}-${stationIP}-${lineNumber}.log`)
|
||||
.replaceAll(' ', '_')
|
||||
|
||||
// Ensure folder exists
|
||||
if (!fs.existsSync(logDir)) {
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ export class WebSocketIo {
|
|||
lineIds,
|
||||
async (line) =>
|
||||
command === 'spam_break' ? line.breakSpam() : line.writeCommand(command, userName),
|
||||
{ command, timeout: 120000 }
|
||||
{ command }
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -162,7 +162,6 @@ export class WebSocketIo {
|
|||
async (line) => line.runScript(scenario, userName),
|
||||
{
|
||||
scenario,
|
||||
timeout: scenario?.timeout ? Number(scenario.timeout) + 120000 : 300000,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
|
@ -203,7 +202,7 @@ export class WebSocketIo {
|
|||
stationId,
|
||||
[lineId],
|
||||
async (lineCon) => lineCon.writeCommand('\r\n', userName),
|
||||
{ command: '\r\n', timeout: 120000 }
|
||||
{ command: '\r\n' }
|
||||
)
|
||||
} else {
|
||||
if (this.lineConnecting.includes(lineId)) return
|
||||
|
|
@ -322,7 +321,7 @@ export class WebSocketIo {
|
|||
.andWhere('outlet', outletNumber)
|
||||
if (lines.length > 0) {
|
||||
const line = this.lineMap.get(lines[0].id)
|
||||
if (line) this.setTimeoutConnect(lines[0].id, line, 300000)
|
||||
if (line) this.setTimeoutConnect(lines[0].id, line)
|
||||
}
|
||||
|
||||
const apcIp = (station as any)[`${apcName}_ip`] as string
|
||||
|
|
@ -508,6 +507,8 @@ export class WebSocketIo {
|
|||
ip: station.ip,
|
||||
lineNumber: line.lineNumber,
|
||||
stationId: station.id,
|
||||
stationName: station.name,
|
||||
stationIp: station.ip,
|
||||
apcName: line.apcName,
|
||||
outlet: line.outlet,
|
||||
baud: line.baud,
|
||||
|
|
@ -542,7 +543,7 @@ export class WebSocketIo {
|
|||
private setTimeoutConnect = (
|
||||
lineId: number,
|
||||
lineConn: LineConnection | SwitchController,
|
||||
timeout = 120000
|
||||
timeout = 28800000 // 8h = 8*60*60*1000
|
||||
) => {
|
||||
if (this.intervalMap[`${lineId}`]) {
|
||||
clearInterval(this.intervalMap[`${lineId}`])
|
||||
|
|
@ -576,7 +577,7 @@ export class WebSocketIo {
|
|||
// console.log(line?.config)
|
||||
if (line && line.config.status === 'connected') {
|
||||
this.lineConnecting = this.lineConnecting.filter((el) => el !== lineId)
|
||||
this.setTimeoutConnect(lineId, line, options.timeout)
|
||||
this.setTimeoutConnect(lineId, line)
|
||||
// await sleep(500)
|
||||
await action(line, options)
|
||||
} else {
|
||||
|
|
@ -598,7 +599,7 @@ export class WebSocketIo {
|
|||
|
||||
const lineReconnect = this.lineMap.get(lineId)
|
||||
if (lineReconnect) {
|
||||
this.setTimeoutConnect(lineId, lineReconnect, options.timeout)
|
||||
this.setTimeoutConnect(lineId, lineReconnect)
|
||||
await sleep(100)
|
||||
await action(lineReconnect, options)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,14 @@ import "@mantine/notifications/styles.css";
|
|||
import "./App.css";
|
||||
import classes from "./App.module.css";
|
||||
|
||||
import { Suspense, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
Suspense,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
Tabs,
|
||||
Text,
|
||||
|
|
@ -79,6 +86,8 @@ function App() {
|
|||
const [isLogModalOpen, setIsLogModalOpen] = useState(false);
|
||||
const [expandedBottomBar, setExpandedBottomBar] = useState(true);
|
||||
const [activeTabBottom, setActiveTabBottom] = useState<string>("command");
|
||||
const lineBuffersRef = useRef(new Map<number, string>());
|
||||
const flushScheduledRef = useRef(false);
|
||||
|
||||
const connectApcSwitch = (station: TStation) => {
|
||||
if (station?.apc_1_ip && station?.apc_1_port) {
|
||||
|
|
@ -165,11 +174,20 @@ function App() {
|
|||
);
|
||||
|
||||
socket?.on("line_output", (data) => {
|
||||
updateValueLineStation(
|
||||
data?.lineId,
|
||||
{ netOutput: data.data, commands: data.commands || [] },
|
||||
data?.stationId
|
||||
);
|
||||
const { lineId, data: text } = data;
|
||||
// updateValueLineStation(
|
||||
// data?.lineId,
|
||||
// { netOutput: data.data, commands: data.commands || [] },
|
||||
// data?.stationId
|
||||
// );
|
||||
|
||||
const buf = lineBuffersRef.current.get(lineId) || "";
|
||||
lineBuffersRef.current.set(lineId, buf + text);
|
||||
|
||||
if (!flushScheduledRef.current) {
|
||||
flushScheduledRef.current = true;
|
||||
setTimeout(() => flushBuffers(), 50);
|
||||
}
|
||||
});
|
||||
|
||||
socket?.on("line_error", (data) => {
|
||||
|
|
@ -323,6 +341,28 @@ function App() {
|
|||
};
|
||||
}, [socket, stations, selectedLine]);
|
||||
|
||||
const flushBuffers = useCallback(() => {
|
||||
setStations((prev) =>
|
||||
prev.map((station) => ({
|
||||
...station,
|
||||
lines: station.lines.map((line) => {
|
||||
const buffered = lineBuffersRef.current.get(line.id || 0);
|
||||
if (!buffered) return line; // không có update
|
||||
updateValueSelectedLine(line?.id || 0, { netOutput: buffered });
|
||||
return {
|
||||
...line,
|
||||
netOutput: (line.netOutput || "") + buffered,
|
||||
output: buffered,
|
||||
loadingOutput: line.loadingOutput ? false : true,
|
||||
};
|
||||
}),
|
||||
}))
|
||||
);
|
||||
// clear
|
||||
lineBuffersRef.current.clear();
|
||||
flushScheduledRef.current = false;
|
||||
}, []);
|
||||
|
||||
const updateValueLineStation = useCallback(
|
||||
(lineId: number, updates: Partial<TLine>, stationId?: number) => {
|
||||
setStations((prevStations) =>
|
||||
|
|
@ -374,6 +414,29 @@ function App() {
|
|||
[]
|
||||
);
|
||||
|
||||
const updateValueSelectedLine = useCallback(
|
||||
(lineId: number, updates: Partial<TLine>) => {
|
||||
// Update selectedLine nếu nó đang được chọn
|
||||
setSelectedLine((prevSelected) => {
|
||||
if (!prevSelected || prevSelected.id !== lineId) return prevSelected;
|
||||
|
||||
const isNetOutput = typeof updates?.netOutput !== "undefined";
|
||||
|
||||
return {
|
||||
...prevSelected,
|
||||
...updates,
|
||||
...(isNetOutput && {
|
||||
netOutput:
|
||||
(prevSelected.netOutput || "") + (updates.netOutput || ""),
|
||||
output: updates.netOutput,
|
||||
loadingOutput: prevSelected.loadingOutput ? false : true,
|
||||
}),
|
||||
};
|
||||
});
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
// const getLine = (lineId: number, stationId: number) => {
|
||||
// const station = stations?.find((sta) => sta.id === stationId);
|
||||
// if (station) {
|
||||
|
|
@ -530,7 +593,6 @@ function App() {
|
|||
onChange={(id) => {
|
||||
if (selectedLines.length > 0) {
|
||||
selectedLines.forEach((el) => {
|
||||
console.log(el?.userOpenCLI, user?.userName);
|
||||
if (el?.userOpenCLI === user?.userName)
|
||||
socket?.emit("close_cli", {
|
||||
lineId: el?.id,
|
||||
|
|
|
|||
|
|
@ -98,7 +98,8 @@ function DrawerLogs({
|
|||
<div>
|
||||
Format:
|
||||
<i style={{ marginLeft: "4px" }}>
|
||||
YYYYMMDD-Station_{`{id}`}-Line_{`{number}`}_{`{port}`}
|
||||
YYYYMMDD-AUTO-Session.{`{Station name}`}-{`{Station ID}`}-
|
||||
{`{Station IP}`}-{`{Line number}`}
|
||||
.log
|
||||
</i>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -146,8 +146,6 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
|||
setLoading(false);
|
||||
}, 500);
|
||||
if (fitRef.current) fitRef.current?.fit();
|
||||
} else {
|
||||
setIsInit(false);
|
||||
}
|
||||
}, [cliOpened]);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue