Add baud rate management for line connections #3

Merged
andrew.ng merged 1 commits from that into main 2025-11-24 20:42:50 +11:00
4 changed files with 218 additions and 17 deletions

View File

@ -21,6 +21,7 @@ interface LineConfig {
outlet: number
output: string
status: string
baud: number
openCLI: boolean
userEmailOpenCLI: string
userOpenCLI: string

View File

@ -9,7 +9,7 @@ import { CustomServer, CustomSocket } from '../app/ultils/types.js'
import Line from '#models/line'
import Station from '#models/station'
import APCController from '#services/apc_connection'
import { appendLog, sleep } from '../app/ultils/helper.js'
import { appendLog, cleanData, sleep } from '../app/ultils/helper.js'
import SwitchController from '#services/switch_connection'
import redis from '@adonisjs/redis/services/main'
@ -178,7 +178,7 @@ export class WebSocketIo {
}
Object.assign(line, { baud })
line?.save()
await this.setBaudByLineNumber(data.stationId, line?.lineNumber, baud)
await this.setBaudByClearLine(data.stationId, line?.lineClear, baud, lineId, io)
})
socket.on('open_cli', async (data) => {
@ -510,6 +510,7 @@ export class WebSocketIo {
stationId: station.id,
apcName: line.apcName,
outlet: line.outlet,
baud: line.baud,
output: output,
status: '',
openCLI: false,
@ -529,6 +530,9 @@ export class WebSocketIo {
await lineConn.connect()
lineConn.writeCommand('\r\n\r\n')
this.setTimeoutConnect(line.id, lineConn)
if (line.lineClear && line.lineClear > 0)
await this.checkBaudByClearLine(station.id, line.lineClear, line.id, socket)
}
} catch (error) {
console.log(error)
@ -781,7 +785,12 @@ export class WebSocketIo {
this.intervalKeepConnect[`${ip}`] = interval
}
private async setBaudByLineNumber(stationId: number, lineNumber: number, baud: number) {
private async checkBaudByClearLine(
stationId: number,
lineClear: number,
lineId: number,
io: any
) {
const station = await Station.find(stationId)
if (!station) {
console.log('[ERROR connect station] Not found!')
@ -790,43 +799,146 @@ export class WebSocketIo {
// Kết nối tới station qua Telnet / Socket
const client = new net.Socket()
let buffer = ''
return new Promise<void>((resolve, reject) => {
client.setTimeout(5000)
client.setTimeout(8000)
client.connect(station.port, station.ip, async () => {
console.log(`Connected to station ${station.name} (${station.ip})`)
// Gửi lệnh clear line
client.write(`conf t\r\n`)
await sleep(500)
client.write(`line ${lineNumber}\r\n`)
await sleep(500)
client.write(`speed ${baud.toString()}\r\n`)
await sleep(500)
client.write(`end`)
await sleep(500)
client.write(`\r\n`)
await sleep(500)
client.write(`show line\r\n`)
await sleep(2000)
client.destroy()
resolve()
})
client.on('data', (data) => {
appendLog(data.toString(), 0, 0, lineNumber)
const text = data.toString()
buffer += cleanData(text)
})
client.on('error', (err) => {
console.error(`Error clearing line ${lineNumber}:`, err)
console.error(`Error clearing line ${lineClear}:`, err)
resolve()
})
client.on('close', () => {
console.log(`Station connection closed (line ${lineNumber})`)
console.log(`Station connection closed (line ${lineClear})`)
const result = this.detectBaudFromShowLine(buffer)
const found = result.find((x) => x.clearLine === lineClear)
if (found) {
const line = this.lineMap.get(lineId)
if (line) {
line.config.baud = found.baud
this.lineMap.set(lineId, line)
io.emit('update_baud', {
stationId,
lineId,
data: found.baud,
})
}
}
resolve()
})
client.on('timeout', () => {
console.log(`Station connection timeout (line ${lineNumber})`)
console.log(`Station connection timeout (line ${lineClear})`)
client.destroy()
resolve()
})
})
}
private async setBaudByClearLine(
stationId: number,
lineClear: number,
baud: number,
lineId: number,
io: any
) {
const station = await Station.find(stationId)
if (!station) {
console.log('[ERROR connect station] Not found!')
return
}
// Kết nối tới station qua Telnet / Socket
const client = new net.Socket()
let buffer = ''
return new Promise<void>((resolve, reject) => {
client.setTimeout(8000)
client.connect(station.port, station.ip, async () => {
console.log(`Connected to station ${station.name} (${station.ip})`)
// Gửi lệnh clear line
client.write(`conf t\r\n`)
await sleep(500)
client.write(`line ${lineClear}\r\n`)
await sleep(500)
client.write(`speed ${baud.toString()}\r\n`)
await sleep(500)
client.write(`end\r\n`)
await sleep(500)
client.write(`\r\n`)
await sleep(500)
client.write(`show line\r\n`)
await sleep(2000)
client.destroy()
resolve()
})
client.on('data', (data) => {
const text = data.toString()
buffer += text
})
client.on('error', (err) => {
console.error(`Error clearing line ${lineClear}:`, err)
resolve()
})
client.on('close', () => {
console.log(`Station connection closed (line ${lineClear})`)
const result = this.detectBaudFromShowLine(buffer)
const found = result.find((x) => x.clearLine === lineClear)
if (found) {
const line = this.lineMap.get(lineId)
if (line) {
line.config.baud = found.baud
io.emit('update_baud', {
stationId,
lineId,
data: found.baud,
})
}
}
resolve()
})
client.on('timeout', () => {
console.log(`Station connection timeout (line ${lineClear})`)
client.destroy()
resolve()
})
})
}
private detectBaudFromShowLine(output: string) {
const lines = output.split(/\r?\n/)
const result: { clearLine: number; baud: number }[] = []
const regex = /^\s*\S+\s+(\d+)\s+\S+\s+(\d+)\/(\d+)/
for (const line of lines) {
const match = line.replace('*', '').match(regex)
if (match) {
const clearLine = Number.parseInt(match[1], 10)
const baud = Number.parseInt(match[2], 10)
result.push({ clearLine, baud })
}
}
return result
}
}

View File

@ -295,6 +295,18 @@ function App() {
}, 100);
});
socket?.on("update_baud", (data) => {
setTimeout(() => {
updateValueLineStation(
data.lineId,
{
baud: data.data,
},
data?.stationId
);
}, 100);
});
// ✅ cleanup on unmount or when socket changes
return () => {
socket.off("init");

View File

@ -32,6 +32,7 @@ import moment from "moment";
import axios from "axios";
import { notifications } from "@mantine/notifications";
import classes from "./Component.module.css";
import { listBaudDefault } from "../untils/constanst";
const apiUrl = import.meta.env.VITE_BACKEND_URL;
const INIT_TICKET = {
@ -71,6 +72,7 @@ const ModalTerminal = ({
const [listPorts, setListPorts] = useState<SwitchPortsProps[]>([]);
const [latestTicket, setLatestTicket] = useState<TDataTicket>(INIT_TICKET);
const [dataTicket, setDataTicket] = useState<TDataTicket>(INIT_TICKET);
const [valueBaud, setValueBaud] = useState<string>("");
useEffect(() => {
if (opened && line?.tickets && line?.tickets?.length > 0) {
@ -80,6 +82,7 @@ const ModalTerminal = ({
} else {
setLatestTicket(INIT_TICKET);
setDataTicket(INIT_TICKET);
setValueBaud("");
}
}, [opened, line?.tickets]);
@ -664,6 +667,79 @@ const ModalTerminal = ({
</Box>
</Menu.Dropdown>
</Menu>
<Menu
closeOnItemClick={false}
closeOnClickOutside={false}
trigger="hover"
shadow="md"
position="top"
>
<Menu.Target>
<Button
fw={400}
disabled={isDisable}
variant="filled"
size="xs"
style={{ height: "30px", width: "100px" }}
onClick={() => {}}
>
BAUD
</Button>
</Menu.Target>
<Menu.Dropdown style={{ width: "110px" }}>
<Flex
justify={"space-between"}
direction={"column"}
style={{
gap: "8px",
}}
>
{listBaudDefault.map((el, i) => (
<Button
key={i}
disabled={isDisable}
variant="outline"
size="xs"
fw={400}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
socket?.emit("set_baud", {
lineId: line?.id,
baud: el,
stationId: Number(stationItem?.id),
});
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 5000);
}}
>
{el}
</Button>
))}
<Input
placeholder="Custom"
value={valueBaud}
onChange={(e) => setValueBaud(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
socket?.emit("set_baud", {
lineId: line?.id,
baud: Number(valueBaud),
stationId: Number(stationItem?.id),
});
setValueBaud("");
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 5000);
}
}}
/>
</Flex>
</Menu.Dropdown>
</Menu>
<Button
disabled={true}
fw={400}