ATC_SIMPLE/BACKEND/app/services/line_connection.ts

135 lines
3.6 KiB
TypeScript

import net from 'node:net'
import { cleanData } from '../ultils/helper.js'
interface LineConfig {
id: number
port: number
lineNumber: number
ip: string
stationId: number
apcName?: string
output: string
status: string
}
export default class LineConnection {
public client: net.Socket
public readonly config: LineConfig
public readonly socketIO: any
constructor(config: LineConfig, socketIO: any) {
this.config = config
this.socketIO = socketIO
this.client = new net.Socket()
}
connect(timeoutMs = 5000) {
return new Promise<void>((resolve, reject) => {
const { ip, port, lineNumber, id, stationId } = this.config
let resolvedOrRejected = false
// Set timeout
this.client.setTimeout(timeoutMs)
this.client.connect(port, ip, () => {
if (resolvedOrRejected) return
resolvedOrRejected = true
console.log(`✅ Connected to line ${lineNumber} (${ip}:${port})`)
this.config.status = 'connected'
this.socketIO.emit('line_connected', {
stationId,
lineId: id,
lineNumber,
status: 'connected',
})
resolve()
})
this.client.on('data', (data) => {
let message = data.toString()
// let output = cleanData(message)
// console.log(`📨 [${this.config.port}] ${message}`)
// Handle netOutput with backspace support
for (const char of message) {
if (char === '\x7F' || char === '\x08') {
this.config.output = this.config.output.slice(0, -1)
// message = message.slice(0, -1)
} else {
this.config.output += cleanData(char)
}
}
this.config.output = this.config.output.slice(-15000)
this.socketIO.emit('line_output', {
stationId,
lineId: id,
data: message,
})
})
this.client.on('error', (err) => {
if (resolvedOrRejected) return
resolvedOrRejected = true
console.error(`❌ Error line ${lineNumber}:`, err.message)
this.config.output += err.message
this.socketIO.emit('line_error', {
stationId,
lineId: id,
error: err.message,
})
reject(err)
})
this.client.on('close', () => {
console.log(`🔌 Line ${lineNumber} disconnected`)
this.config.status = 'disconnected'
this.socketIO.emit('line_disconnected', {
stationId,
lineId: id,
lineNumber,
status: 'disconnected',
})
})
this.client.on('timeout', () => {
if (resolvedOrRejected) return
resolvedOrRejected = true
console.log(`⏳ Connection timeout line ${lineNumber}`)
this.client.destroy()
reject(new Error('Connection timeout'))
})
})
}
sendCommand(cmd: string) {
if (this.client.destroyed) {
console.log(`⚠️ Cannot send, line ${this.config.lineNumber} is closed`)
return
}
// console.log(`➡️ [${this.config.apcName}] SEND:`, cmd)
this.client.write(`${cmd}\r\n`)
}
writeCommand(cmd: string) {
if (this.client.destroyed) {
console.log(`⚠️ Cannot send, line ${this.config.lineNumber} is closed`)
return
}
this.client.write(`${cmd}`)
}
disconnect() {
try {
this.client.destroy()
this.config.status = 'disconnected'
this.socketIO.emit('line_disconnected', {
...this.config,
status: 'disconnected',
})
console.log(`🔻 Closed connection to line ${this.config.lineNumber}`)
} catch (e) {
console.error('Error closing line:', e)
}
}
}