Update
This commit is contained in:
parent
3054874568
commit
34d0f082f5
|
|
@ -29,3 +29,6 @@ SEND_ZULIP=1
|
|||
ZULIP_REALM="https://zulip.ipsupply.com.au"
|
||||
ZULIP_USERNAME="networktool-bot@zulip.ipsupply.com.au"
|
||||
ZULIP_API_KEY="0jMAmOuhfLvBqKJikv5oAkyNM4RIEoAM"
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=
|
||||
|
|
@ -47,7 +47,8 @@ export default defineConfig({
|
|||
() => import('@adonisjs/cors/cors_provider'),
|
||||
() => import('@adonisjs/lucid/database_provider'),
|
||||
() => import('@adonisjs/auth/auth_provider'),
|
||||
() => import('#providers/socket_io_provider')
|
||||
() => import('#providers/socket_io_provider'),
|
||||
() => import('@adonisjs/redis/redis_provider'),
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { textfsmResults } from './../ultils/templates/index.js'
|
||||
import fs from 'node:fs'
|
||||
import net from 'node:net'
|
||||
import {
|
||||
appendLog,
|
||||
|
|
@ -51,6 +50,7 @@ export default class LineConnection {
|
|||
private isRunningScript: boolean
|
||||
private connecting: boolean
|
||||
private waitingScenario: boolean
|
||||
private outputInventory: string
|
||||
private outputScenario: string
|
||||
|
||||
constructor(config: LineConfig, socketIO: any) {
|
||||
|
|
@ -61,6 +61,7 @@ export default class LineConnection {
|
|||
this.isRunningScript = false
|
||||
this.connecting = false
|
||||
this.waitingScenario = false
|
||||
this.outputInventory = ''
|
||||
this.outputScenario = ''
|
||||
}
|
||||
|
||||
|
|
@ -96,8 +97,9 @@ export default class LineConnection {
|
|||
if (this.isRunningScript) {
|
||||
this.waitingScenario = true
|
||||
this.outputBuffer += message
|
||||
this.outputScenario += message
|
||||
if (!this.config.inventory)
|
||||
this.outputScenario = this.outputScenario.slice(-3000) + message
|
||||
this.outputInventory = this.outputInventory.slice(-3000) + message
|
||||
}
|
||||
if (message.includes('--More--')) this.writeCommand(' ')
|
||||
|
||||
|
|
@ -212,6 +214,7 @@ export default class LineConnection {
|
|||
|
||||
this.isRunningScript = true
|
||||
const now = Date.now()
|
||||
this.outputScenario += `\n\n---start-scenarios---${now}---\n---scenario---${script?.title}---${now}---\n`
|
||||
appendLog(
|
||||
`\n\n---start-scenarios---${now}---\n---scenario---${script?.title}---${now}---\n`,
|
||||
this.config.stationId,
|
||||
|
|
@ -236,6 +239,7 @@ export default class LineConnection {
|
|||
lineId: this.config.id,
|
||||
data: 'Timeout run scenario',
|
||||
})
|
||||
this.outputScenario += `\n---end-scenarios---${now}---\n`
|
||||
appendLog(
|
||||
`\n---end-scenarios---${now}---\n`,
|
||||
this.config.stationId,
|
||||
|
|
@ -256,35 +260,21 @@ export default class LineConnection {
|
|||
} else clearTimeout(timeoutTimer)
|
||||
this.isRunningScript = false
|
||||
this.outputBuffer = ''
|
||||
this.outputScenario = ''
|
||||
this.outputScenario += `\n---end-scenarios---${now}---\n`
|
||||
appendLog(
|
||||
`\n---end-scenarios---${now}---\n`,
|
||||
this.config.stationId,
|
||||
this.config.lineNumber,
|
||||
this.config.port
|
||||
)
|
||||
const pathLog = getPathLog(
|
||||
this.config.stationId,
|
||||
this.config.lineNumber,
|
||||
this.config.port
|
||||
)
|
||||
|
||||
if (pathLog)
|
||||
fs.readFile(pathLog, 'utf8', async (err, content) => {
|
||||
if (err) return
|
||||
|
||||
const logScenarios = getLogWithTimeScenario(content, now) || ''
|
||||
this.socketIO.emit('output_test_scenario', {
|
||||
data: logScenarios,
|
||||
})
|
||||
const logScenarios = getLogWithTimeScenario(this.outputScenario, now) || ''
|
||||
const data = textfsmResults(logScenarios, '')
|
||||
try {
|
||||
data.forEach((item) => {
|
||||
if (item?.textfsm && isValidJson(item?.textfsm)) {
|
||||
if (
|
||||
['show inventory', 'sh inventory', 'show inv', 'sh inv'].includes(
|
||||
item.command
|
||||
)
|
||||
['show inventory', 'sh inventory', 'show inv', 'sh inv'].includes(item.command)
|
||||
) {
|
||||
this.config.inventory = JSON.parse(item.textfsm)[0]
|
||||
}
|
||||
|
|
@ -302,13 +292,14 @@ export default class LineConnection {
|
|||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
})
|
||||
|
||||
this.outputScenario = ''
|
||||
resolve(true)
|
||||
return
|
||||
}
|
||||
|
||||
const step = steps[index]
|
||||
this.outputScenario += `\n---send-command---"${step?.send ?? ''}"---${now}---\n`
|
||||
appendLog(
|
||||
`\n---send-command---"${step?.send ?? ''}"---${now}---\n`,
|
||||
this.config.stationId,
|
||||
|
|
@ -474,7 +465,7 @@ export default class LineConnection {
|
|||
}
|
||||
|
||||
getInventory = () => {
|
||||
const data = textfsmResults(this.outputScenario, 'show inventory')
|
||||
const data = textfsmResults(this.outputInventory, 'show inventory')
|
||||
try {
|
||||
data.forEach((item) => {
|
||||
if (item?.textfsm && isValidJson(item?.textfsm)) {
|
||||
|
|
@ -493,7 +484,7 @@ export default class LineConnection {
|
|||
inventory: this.config.inventory || null,
|
||||
latestScenario: this.config.latestScenario || null,
|
||||
})
|
||||
this.outputScenario = ''
|
||||
this.outputInventory = ''
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
import env from '#start/env'
|
||||
import { defineConfig } from '@adonisjs/redis'
|
||||
import { InferConnections } from '@adonisjs/redis/types'
|
||||
|
||||
const redisConfig = defineConfig({
|
||||
connection: 'main',
|
||||
|
||||
connections: {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| The default connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The main connection you want to use to execute redis commands. The same
|
||||
| connection will be used by the session provider, if you rely on the
|
||||
| redis driver.
|
||||
|
|
||||
*/
|
||||
main: {
|
||||
host: env.get('REDIS_HOST'),
|
||||
port: env.get('REDIS_PORT'),
|
||||
password: env.get('REDIS_PASSWORD', ''),
|
||||
db: 0,
|
||||
keyPrefix: '',
|
||||
retryStrategy(times) {
|
||||
return times > 10 ? null : times * 50
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default redisConfig
|
||||
|
||||
declare module '@adonisjs/redis/types' {
|
||||
export interface RedisConnections extends InferConnections<typeof redisConfig> {}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
"@adonisjs/core": "^6.18.0",
|
||||
"@adonisjs/cors": "^2.2.1",
|
||||
"@adonisjs/lucid": "^21.6.1",
|
||||
"@adonisjs/redis": "^9.2.0",
|
||||
"@vinejs/vine": "^3.0.1",
|
||||
"axios": "^1.13.2",
|
||||
"luxon": "^3.7.2",
|
||||
|
|
@ -582,6 +583,23 @@
|
|||
"prettier-plugin-edgejs": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@adonisjs/redis": {
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@adonisjs/redis/-/redis-9.2.0.tgz",
|
||||
"integrity": "sha512-DUI9NrHDLZ2ISNjMlqWbKJT99ZYj1ZmvhNFTfhVs9lc7K2KJmNKZfK8Y85a8eN7q+ZYMBYSu1uRemxGs6xRaYw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@poppinss/utils": "^6.9.2",
|
||||
"emittery": "^1.1.0",
|
||||
"ioredis": "^5.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@adonisjs/core": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@adonisjs/repl": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@adonisjs/repl/-/repl-4.1.2.tgz",
|
||||
|
|
@ -999,6 +1017,12 @@
|
|||
"url": "https://github.com/sponsors/nzakas"
|
||||
}
|
||||
},
|
||||
"node_modules/@ioredis/commands": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz",
|
||||
"integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@japa/api-client": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@japa/api-client/-/api-client-3.1.0.tgz",
|
||||
|
|
@ -2971,6 +2995,15 @@
|
|||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/cluster-key-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
|
||||
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/code-block-writer": {
|
||||
"version": "13.0.3",
|
||||
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz",
|
||||
|
|
@ -4719,6 +4752,30 @@
|
|||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/ioredis": {
|
||||
"version": "5.8.2",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.8.2.tgz",
|
||||
"integrity": "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ioredis/commands": "1.4.0",
|
||||
"cluster-key-slot": "^1.1.0",
|
||||
"debug": "^4.3.4",
|
||||
"denque": "^2.1.0",
|
||||
"lodash.defaults": "^4.2.0",
|
||||
"lodash.isarguments": "^3.1.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0",
|
||||
"standard-as-callback": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.22.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ioredis"
|
||||
}
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
|
|
@ -5117,6 +5174,18 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.defaults": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isarguments": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
|
|
@ -6299,6 +6368,27 @@
|
|||
"node": ">= 10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/redis-errors": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/redis-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
|
||||
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"redis-errors": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect-metadata": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
|
||||
|
|
@ -6878,6 +6968,12 @@
|
|||
"get-source": "^2.0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/standard-as-callback": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
|
||||
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
"@adonisjs/core": "^6.18.0",
|
||||
"@adonisjs/cors": "^2.2.1",
|
||||
"@adonisjs/lucid": "^21.6.1",
|
||||
"@adonisjs/redis": "^9.2.0",
|
||||
"@vinejs/vine": "^3.0.1",
|
||||
"axios": "^1.13.2",
|
||||
"luxon": "^3.7.2",
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import Station from '#models/station'
|
|||
import APCController from '#services/apc_connection'
|
||||
import { sleep } from '../app/ultils/helper.js'
|
||||
import SwitchController from '#services/switch_connection'
|
||||
import redis from '@adonisjs/redis/services/main'
|
||||
|
||||
interface HandleOptions {
|
||||
command?: string
|
||||
|
|
@ -64,17 +65,17 @@ export class WebSocketIo {
|
|||
intervalMap: { [key: string]: NodeJS.Timeout } = {}
|
||||
stationMap: Map<number, Station> = new Map()
|
||||
lineMap: Map<number, LineConnection> = new Map() // key = lineId
|
||||
lineConnecting: number[] = [] // key = lineId
|
||||
userConnecting: Map<number, { userId: number; userName: string }> = new Map()
|
||||
apcsControl: Map<string, APCController> = new Map()
|
||||
switchControl: Map<string, SwitchController> = new Map()
|
||||
lineConnecting: number[] = [] // key = lineId
|
||||
|
||||
constructor(protected app: ApplicationService) {}
|
||||
|
||||
async boot() {
|
||||
const SOCKET_IO_PORT = env.get('SOCKET_PORT') || 8989
|
||||
const FRONTEND_URL = env.get('FRONTEND_URL') || 'http://localhost:5173'
|
||||
|
||||
await this.restoreState()
|
||||
const socketServer = http.createServer()
|
||||
const io = new SocketIOServer(socketServer, {
|
||||
pingInterval: 25000, // 25s server gửi ping
|
||||
|
|
@ -103,16 +104,16 @@ export class WebSocketIo {
|
|||
setTimeout(() => {
|
||||
io.to(socket.id).emit(
|
||||
'init',
|
||||
Array.from(this.lineMap.values()).map((el) => el.config)
|
||||
Array.from(this.lineMap.values()).map((el) => el?.config || {})
|
||||
)
|
||||
}, 500)
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
console.log(`FE disconnected: ${socket.id}`)
|
||||
this.userConnecting.delete(userId)
|
||||
const listLineS = Array.from(this.lineMap.values()).map((el) => el.config)
|
||||
const listLineS = Array.from(this.lineMap.values()).map((el) => el?.config || {})
|
||||
listLineS.forEach((el) => {
|
||||
if (el.userOpenCLI === userName) {
|
||||
if (el?.userOpenCLI === userName) {
|
||||
const line = this.lineMap.get(el.id)
|
||||
if (line) line.userCloseCLI()
|
||||
}
|
||||
|
|
@ -415,6 +416,9 @@ export class WebSocketIo {
|
|||
console.log(`Socket server is running on port ${SOCKET_IO_PORT}`)
|
||||
})
|
||||
|
||||
// 🔹 Tự động lưu dữ liệu định kỳ mỗi 10 giây
|
||||
setInterval(async () => await this.saveState(), 10000)
|
||||
|
||||
return io
|
||||
}
|
||||
|
||||
|
|
@ -644,4 +648,25 @@ export class WebSocketIo {
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
async saveState() {
|
||||
const newMap = new Map<number, LineConnection>()
|
||||
this.lineMap.forEach((line, id) => {
|
||||
if (line && line.config) {
|
||||
newMap.set(id, { config: { ...line.config, status: 'disconnected' } } as LineConnection)
|
||||
}
|
||||
})
|
||||
|
||||
const data = {
|
||||
lineMap: newMap ? [...newMap.entries()] : [],
|
||||
}
|
||||
await redis.set('socket_state', JSON.stringify(data))
|
||||
}
|
||||
|
||||
async restoreState() {
|
||||
const raw = await redis.get('socket_state')
|
||||
if (!raw) return
|
||||
const parsed = JSON.parse(raw)
|
||||
this.lineMap = new Map(parsed.lineMap)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,5 +27,9 @@ export default await Env.create(new URL('../', import.meta.url), {
|
|||
DB_PORT: Env.schema.number(),
|
||||
DB_USER: Env.schema.string(),
|
||||
DB_PASSWORD: Env.schema.string.optional(),
|
||||
DB_DATABASE: Env.schema.string()
|
||||
DB_DATABASE: Env.schema.string(),
|
||||
|
||||
REDIS_HOST: Env.schema.string({ format: 'host' }),
|
||||
REDIS_PORT: Env.schema.number(),
|
||||
REDIS_PASSWORD: Env.schema.string.optional()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -86,13 +86,22 @@ const CardLine = ({
|
|||
</Flex>
|
||||
<Flex justify={"space-between"}>
|
||||
<div className={classes.info_line}>
|
||||
PID: {line?.inventory?.pid || ""}
|
||||
PID:{" "}
|
||||
<Text className={classes.info_line} fs={"italic"}>
|
||||
{line?.inventory?.pid || ""}
|
||||
</Text>
|
||||
</div>
|
||||
<div className={classes.info_line}>
|
||||
SN: {line?.inventory?.sn || ""}
|
||||
SN:{" "}
|
||||
<Text className={classes.info_line} fs={"italic"}>
|
||||
{line?.inventory?.sn || ""}
|
||||
</Text>
|
||||
</div>
|
||||
<div className={classes.info_line} style={{ minWidth: "50px" }}>
|
||||
VID: {line?.inventory?.vid || ""}
|
||||
VID:{" "}
|
||||
<Text className={classes.info_line} fs={"italic"}>
|
||||
{line?.inventory?.vid || ""}
|
||||
</Text>
|
||||
</div>
|
||||
</Flex>
|
||||
<Box
|
||||
|
|
|
|||
Loading…
Reference in New Issue