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((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) } } }