ATC_SIMPLE/BACKEND/providers/socket_io_provider.ts

184 lines
4.8 KiB
TypeScript

import { Server as SocketIOServer } from 'socket.io'
import http from 'node:http'
import LineConnection from '../app/services/line_connection.js'
import { ApplicationService } from '@adonisjs/core/types'
import env from '#start/env'
import { CustomServer, CustomSocket } from '../app/ultils/types.js'
import Line from '#models/line'
interface Station {
id: number
name: string
ip: string
lines: any[]
}
export default class SocketIoProvider {
private static _io: CustomServer
constructor(protected app: ApplicationService) {}
/**
* Register bindings to the container
*/
register() {}
/**
* The container bindings have booted
*/
async boot() {}
/**
* The application has been booted
*/
async start() {}
/**
* The process has been started
*/
async ready() {
if (process.argv[1].includes('server.js')) {
const webSocket = new WebSocketIo(this.app)
SocketIoProvider._io = await webSocket.boot()
}
}
/**
* Preparing to shutdown the app
*/
async shutdown() {}
public static get io() {
return this._io
}
}
export class WebSocketIo {
intervalMap: { [key: string]: NodeJS.Timeout } = {}
stationMap: Map<number, Station> = new Map()
lineMap: Map<number, LineConnection> = new Map() // key = lineId
constructor(protected app: ApplicationService) {}
async boot() {
const SOCKET_IO_PORT = env.get('SOCKET_PORT') || 8989
const FRONTEND_URL = env.get('FRONTEND_URL') || 'http://localhost:5173'
const socketServer = http.createServer()
const io = new SocketIOServer(socketServer, {
pingInterval: 25000, // 25s server gửi ping
pingTimeout: 20000, // chờ 20s không có pong thì disconnect
cors: {
origin: [FRONTEND_URL],
methods: ['GET', 'POST'],
credentials: true,
},
})
io.on('connection', (socket: CustomSocket) => {
console.log('Socket connected:', socket.id)
socket.connectionTime = new Date()
const lineConnectionArray: LineConnection[] = Array.from(this.lineMap.values())
io.to(socket.id).emit(
'init',
lineConnectionArray.map((el) => el.config)
)
socket.on('disconnect', () => {
console.log(`FE disconnected: ${socket.id}`)
})
// FE gửi yêu cầu connect lines
socket.on('connect_lines', async (data) => {
const { stationData, linesData } = data
await this.connectLine(io, linesData, stationData)
})
socket.on('write_command_line_from_web', (data) => {
const { lineIds, stationId, command } = data
for (const lineId of lineIds) {
const line = this.lineMap.get(lineId)
if (line) {
this.setTimeoutConnect(lineId, line)
line.writeCommand(command)
} else {
io.emit('line_disconnected', {
stationId,
lineId,
status: 'disconnected',
})
io.emit('line_error', { lineId, error: 'Line not connected\r\n' })
}
}
})
// FE yêu cầu ngắt kết nối 1 station
socket.on('disconnect_station', (stationId) => {
this.disconnectStation(stationId)
})
})
socketServer.listen(SOCKET_IO_PORT, () => {
console.log(`Socket server is running on port ${SOCKET_IO_PORT}`)
})
return io
}
private async connectLine(socket: any, lines: Line[], station: Station) {
try {
this.stationMap.set(station.id, station)
for (const line of lines) {
const lineConn = new LineConnection(
{
id: line.id,
port: line.port,
ip: station.ip,
lineNumber: line.lineNumber,
stationId: station.id,
apcName: line.apcName,
output: '',
status: '',
},
socket
)
await lineConn.connect()
lineConn.writeCommand('\r\n\r\n')
this.lineMap.set(line.id, lineConn)
this.setTimeoutConnect(line.id, lineConn)
}
} catch (error) {
console.log(error)
}
}
private disconnectStation(stationId: number) {
const station = this.stationMap.get(stationId)
if (!station) return
for (const line of station.lines) {
const conn = this.lineMap.get(line.id)
if (conn) {
conn.disconnect()
this.lineMap.delete(line.id)
}
}
this.stationMap.delete(stationId)
console.log(`🔻 Station ${station.name} disconnected`)
}
private setTimeoutConnect = (lineId: number, lineConn: LineConnection) => {
if (this.intervalMap[`${lineId}`]) {
clearInterval(this.intervalMap[`${lineId}`])
delete this.intervalMap[`${lineId}`]
}
const interval = setInterval(() => {
lineConn.disconnect()
this.lineMap.delete(lineId)
}, 120000)
this.intervalMap[`${lineId}`] = interval
}
}