Add baud rate management for line connections

Introduces baud rate configuration and detection for line connections in both backend and frontend. Backend now supports setting and checking baud rates via socket events, and frontend provides UI controls for users to select or input baud rates. Real-time updates are handled through new socket events, improving line management and visibility.
This commit is contained in:
nguyentrungthat 2025-11-24 16:42:20 +07:00
parent 9d1be9e260
commit e8f5a9f67a
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}