Update station connect
This commit is contained in:
parent
4d9c6abc89
commit
8ddccf6586
|
|
@ -0,0 +1,108 @@
|
|||
import net from 'node:net'
|
||||
import { appendLog, cleanData } from '../ultils/helper.js'
|
||||
|
||||
interface LineConfig {
|
||||
id: number
|
||||
port: number
|
||||
ip: string
|
||||
name: string
|
||||
output: string
|
||||
status: string
|
||||
}
|
||||
|
||||
export default class StationConnection {
|
||||
public client: net.Socket
|
||||
public config: LineConfig
|
||||
|
||||
constructor(config: LineConfig) {
|
||||
this.config = config
|
||||
this.client = new net.Socket()
|
||||
}
|
||||
|
||||
connect(timeoutMs = 5000) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const { ip, port, name } = this.config
|
||||
let resolvedOrRejected = false
|
||||
// Set timeout
|
||||
this.client.setTimeout(timeoutMs)
|
||||
console.log(`🔌 Connecting to station ${name} (${ip}:${port})...`)
|
||||
this.client.connect(port, ip, () => {
|
||||
if (resolvedOrRejected) return
|
||||
resolvedOrRejected = true
|
||||
|
||||
console.log(`[${Date.now()}] ✅ Connected to line ${name} (${ip}:${port})`)
|
||||
setTimeout(() => {
|
||||
this.config.status = 'connected'
|
||||
resolve()
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
this.client.on('data', (data) => {
|
||||
let message = data.toString()
|
||||
if (message.includes('--More--')) this.writeCommand(' ')
|
||||
this.config.output += cleanData(message)
|
||||
this.config.output = this.config.output.slice(-5000)
|
||||
// appendLog(cleanData(message), this.config.id, this.config.name, this.config.ip, 0)
|
||||
})
|
||||
|
||||
this.client.on('error', (err) => {
|
||||
if (resolvedOrRejected) return
|
||||
resolvedOrRejected = true
|
||||
console.error(`❌ Error station ${name}:`, err.message)
|
||||
this.config.output += '\r\n' + err.message + '\r\n'
|
||||
resolve()
|
||||
})
|
||||
|
||||
this.client.on('close', async () => {
|
||||
console.log(`[${Date.now()}] 🔌 station ${name} disconnected`)
|
||||
this.config.status = 'disconnected'
|
||||
})
|
||||
|
||||
this.client.on('timeout', () => {
|
||||
if (resolvedOrRejected) return
|
||||
resolvedOrRejected = true
|
||||
const message = '\r\nConnection timeout!!\r\n'
|
||||
this.config.output += message
|
||||
console.log(`⏳ Connection timeout station ${name}`)
|
||||
this.client.destroy()
|
||||
resolve()
|
||||
// reject(new Error('Connection timeout'))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
private sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
async writeCommand(cmd: string | Buffer<ArrayBuffer>) {
|
||||
if (this.client.destroyed) {
|
||||
console.log(`⚠️ Cannot send, station ${this.config.name} is closed`)
|
||||
return
|
||||
}
|
||||
this.client.write(cmd)
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
try {
|
||||
console.log('[DISCONNECT] station', this.config.name)
|
||||
this.client.destroy()
|
||||
this.config.status = 'disconnected'
|
||||
console.log(`🔻 Closed connection to station ${this.config.name}`)
|
||||
} catch (e) {
|
||||
console.error('Error closing line:', e)
|
||||
}
|
||||
}
|
||||
|
||||
public async reconnect(): Promise<boolean> {
|
||||
try {
|
||||
this.disconnect()
|
||||
this.client = new net.Socket()
|
||||
await this.sleep(1000)
|
||||
await this.connect()
|
||||
return true
|
||||
} catch (err: any) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import { appendLog, cleanData, sendMessageToMail, sleep } from '../app/ultils/he
|
|||
import SwitchController from '#services/switch_connection'
|
||||
import redis from '@adonisjs/redis/services/main'
|
||||
import axios from 'axios'
|
||||
import StationConnection from '#services/station_connection'
|
||||
|
||||
interface HandleOptions {
|
||||
command?: string
|
||||
|
|
@ -37,6 +38,11 @@ interface HistoryItem {
|
|||
|
||||
type LineAction = (line: LineConnection, options?: HandleOptions) => Promise<void | unknown> | void
|
||||
|
||||
type StationAction = (
|
||||
line: StationConnection,
|
||||
options?: HandleOptions
|
||||
) => Promise<void | unknown> | void
|
||||
|
||||
export default class SocketIoProvider {
|
||||
private static _io: CustomServer
|
||||
constructor(protected app: ApplicationService) {}
|
||||
|
|
@ -78,6 +84,7 @@ export default class SocketIoProvider {
|
|||
|
||||
export class WebSocketIo {
|
||||
intervalMap: { [key: string]: NodeJS.Timeout } = {}
|
||||
stationMap: Map<number, StationConnection> = new Map() // key = stationId
|
||||
lineMap: Map<number, LineConnection> = new Map() // key = lineId
|
||||
userConnecting: Map<number, { userId: number; userName: string }> = new Map()
|
||||
apcsControl: Map<string, APCController> = new Map()
|
||||
|
|
@ -629,7 +636,7 @@ export class WebSocketIo {
|
|||
|
||||
private setTimeoutConnect = (
|
||||
lineId: number,
|
||||
lineConn: LineConnection | SwitchController,
|
||||
lineConn: LineConnection | SwitchController | StationConnection,
|
||||
timeout = 28800000 // 8h = 8*60*60*1000
|
||||
) => {
|
||||
if (this.intervalMap[`${lineId}`]) {
|
||||
|
|
@ -798,43 +805,10 @@ export class WebSocketIo {
|
|||
return
|
||||
}
|
||||
|
||||
// Kết nối tới station qua Telnet / Socket
|
||||
const client = new net.Socket()
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
client.setTimeout(5000)
|
||||
client.connect(station.port, station.ip, async () => {
|
||||
console.log(
|
||||
`Connected to station ${station.name} (${station.ip}) to clear line ${clearLine}`
|
||||
)
|
||||
// Gửi lệnh clear line
|
||||
client.write(`clear line ${clearLine}\r\n`)
|
||||
await sleep(500)
|
||||
client.write(`\r\n\r\n`)
|
||||
})
|
||||
|
||||
client.on('data', (data) => {
|
||||
const output = data.toString()
|
||||
if (output.includes('Clear completed') || output.includes('OK')) {
|
||||
console.log(`Line ${clearLine} cleared successfully.`)
|
||||
client.destroy()
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
|
||||
client.on('error', (err) => {
|
||||
console.error(`Error clearing line ${clearLine}:`, err)
|
||||
resolve()
|
||||
})
|
||||
|
||||
client.on('close', () => {
|
||||
console.log(`Station connection closed (line ${clearLine})`)
|
||||
resolve()
|
||||
})
|
||||
client.on('timeout', () => {
|
||||
console.log(`Station connection timeout (line ${clearLine})`)
|
||||
client.destroy()
|
||||
resolve()
|
||||
})
|
||||
await this.handleStationOperation(stationId, async (stationCon) => {
|
||||
stationCon.writeCommand(`\r\nclear line ${clearLine}\r\n`)
|
||||
await sleep(500)
|
||||
stationCon.writeCommand(`\r\n\r\n`)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -888,57 +862,32 @@ export class WebSocketIo {
|
|||
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})`)
|
||||
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 += cleanData(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
|
||||
this.lineMap.set(lineId, line)
|
||||
io.emit('update_baud', {
|
||||
stationId,
|
||||
lineId,
|
||||
data: found.baud,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
resolve()
|
||||
})
|
||||
client.on('timeout', () => {
|
||||
console.log(`Station connection timeout (line ${lineClear})`)
|
||||
client.destroy()
|
||||
resolve()
|
||||
})
|
||||
await this.handleStationOperation(stationId, async (stationCon) => {
|
||||
stationCon.writeCommand(`\r\n`)
|
||||
await sleep(500)
|
||||
stationCon.writeCommand(`show line\r\n`)
|
||||
await sleep(2000)
|
||||
})
|
||||
|
||||
const stationConn = this.stationMap.get(stationId)
|
||||
|
||||
if (stationConn) {
|
||||
const buffer = stationConn?.config?.output || ''
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async setBaudByClearLine(
|
||||
|
|
@ -1112,4 +1061,58 @@ export class WebSocketIo {
|
|||
html += `</table>\n\n`
|
||||
return html
|
||||
}
|
||||
|
||||
private async connectStation(station: Station) {
|
||||
try {
|
||||
const stationConn = new StationConnection({
|
||||
id: station.id,
|
||||
port: station.port,
|
||||
ip: station.ip,
|
||||
name: station.name,
|
||||
output: '',
|
||||
status: '',
|
||||
})
|
||||
this.stationMap.set(station.id, stationConn)
|
||||
await stationConn.connect()
|
||||
stationConn.writeCommand('\r\n')
|
||||
this.setTimeoutConnect(station.id, stationConn)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hàm xử lý chung cho mọi action (write command, runScript, v.v.)
|
||||
*/
|
||||
async handleStationOperation(
|
||||
stationId: number,
|
||||
action: StationAction,
|
||||
options: HandleOptions = {}
|
||||
): Promise<void> {
|
||||
try {
|
||||
const station = this.stationMap.get(stationId)
|
||||
// console.log(line?.config)
|
||||
if (station && station.config.status === 'connected') {
|
||||
this.setTimeoutConnect(stationId, station)
|
||||
// await sleep(500)
|
||||
await action(station, options)
|
||||
} else {
|
||||
const stationData = await Station.findBy('id', stationId)
|
||||
|
||||
if (stationData) {
|
||||
await this.connectStation(stationData)
|
||||
const stationReconnect = this.stationMap.get(stationId)
|
||||
if (stationReconnect) {
|
||||
this.setTimeoutConnect(stationId, stationReconnect)
|
||||
await sleep(100)
|
||||
await action(stationReconnect, options)
|
||||
}
|
||||
} else {
|
||||
console.log('Station not found')
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.log('Station connect error:', err.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -523,7 +523,13 @@ function App() {
|
|||
}}
|
||||
>
|
||||
<ScrollArea
|
||||
h={expandedBottomBar ? "68vh" : "85vh"}
|
||||
h={
|
||||
expandedBottomBar
|
||||
? activeTabBottom !== "switch"
|
||||
? "75vh"
|
||||
: "70vh"
|
||||
: "85vh"
|
||||
}
|
||||
type="scroll"
|
||||
scrollbars="y"
|
||||
style={{ overflowX: "hidden" }}
|
||||
|
|
@ -569,8 +575,8 @@ function App() {
|
|||
// >= 9 lines: chia làm 2 cột, mỗi cột chứa 1/2 số line,
|
||||
// mỗi cột hiển thị 2 item trên một "hàng" như ví dụ yêu cầu
|
||||
(() => {
|
||||
const total = station.lines.length;
|
||||
const half = Math.ceil(total / 2);
|
||||
// const total = station.lines.length;
|
||||
const half = 8;
|
||||
const leftLines = station.lines.slice(0, half);
|
||||
const rightLines = station.lines.slice(half);
|
||||
|
||||
|
|
|
|||
|
|
@ -501,6 +501,7 @@ const BottomToolBar = ({
|
|||
translate: "-19px 0",
|
||||
backgroundColor: "#e3e0e0",
|
||||
width: "55px",
|
||||
zIndex: 10,
|
||||
}}
|
||||
variant="light"
|
||||
onClick={() => {
|
||||
|
|
@ -631,7 +632,8 @@ const BottomToolBar = ({
|
|||
selectedLines.forEach((line) => {
|
||||
socket?.emit("close_cli", {
|
||||
lineId: line?.id,
|
||||
stationId: line.stationId || line.station_id,
|
||||
stationId:
|
||||
line.stationId || line.station_id,
|
||||
});
|
||||
});
|
||||
setSelectedLines([]);
|
||||
|
|
@ -646,7 +648,8 @@ const BottomToolBar = ({
|
|||
</ScrollArea>
|
||||
<Flex justify={"space-between"} align={"center"} mt={4}>
|
||||
<Text fz={"11px"} c="dimmed">
|
||||
Selected: {selectedLines.length} / {station.lines.length}
|
||||
Selected: {selectedLines.length} /{" "}
|
||||
{station.lines.length}
|
||||
</Text>
|
||||
<ButtonSelect
|
||||
selectedLines={selectedLines}
|
||||
|
|
@ -761,7 +764,12 @@ const BottomToolBar = ({
|
|||
</Box>
|
||||
</Box>
|
||||
<Box style={{ width: "260px" }}>
|
||||
<Flex align={"center"} justify={"flex-end"} gap={"xs"} wrap={"wrap"}>
|
||||
<Flex
|
||||
align={"center"}
|
||||
justify={"flex-end"}
|
||||
gap={"xs"}
|
||||
wrap={"wrap"}
|
||||
>
|
||||
<ButtonDPELP
|
||||
socket={socket}
|
||||
selectedLines={selectedLines}
|
||||
|
|
|
|||
|
|
@ -437,7 +437,7 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
|
|||
style={{
|
||||
paddingLeft: 0,
|
||||
paddingRight: 0,
|
||||
width: "65px",
|
||||
width: "55px",
|
||||
position: "relative",
|
||||
cursor: "pointer",
|
||||
textAlign: "center",
|
||||
|
|
@ -668,7 +668,7 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
|
|||
style={{
|
||||
paddingLeft: 0,
|
||||
paddingRight: 0,
|
||||
width: "65px",
|
||||
width: "55px",
|
||||
position: "relative",
|
||||
cursor: "pointer",
|
||||
textAlign: "center",
|
||||
|
|
|
|||
|
|
@ -269,6 +269,8 @@ const StationSetting = ({
|
|||
if (response.data.status) {
|
||||
if (response.data.data) {
|
||||
const station = response.data.data[0];
|
||||
const lines = station?.lines;
|
||||
const dataStationLines = dataStation?.lines;
|
||||
setStations((pre) =>
|
||||
isEdit
|
||||
? pre.map((el) =>
|
||||
|
|
@ -276,13 +278,13 @@ const StationSetting = ({
|
|||
? {
|
||||
...el,
|
||||
...station,
|
||||
lines: dataStation?.lines?.map((el) =>
|
||||
lineUpdate?.find((value) => value?.id === el.id)
|
||||
lines: lines?.map((el: TLine) =>
|
||||
dataStationLines?.find((value) => value?.id === el.id)
|
||||
? {
|
||||
...el,
|
||||
...lineUpdate?.find(
|
||||
(value) => value?.id === el.id
|
||||
...dataStationLines?.find(
|
||||
(value: TLine) => value?.id === el.id
|
||||
),
|
||||
...el,
|
||||
}
|
||||
: el
|
||||
),
|
||||
|
|
|
|||
Loading…
Reference in New Issue