Update UI Bottom Tool Bar
This commit is contained in:
parent
f04bc0b4c1
commit
31036ff7da
|
|
@ -17,8 +17,8 @@ interface PromptCallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
class APCController {
|
class APCController {
|
||||||
private apc_number?: number
|
public apc_number?: number
|
||||||
private apc_ip: string
|
public apc_ip: string
|
||||||
private apc_port: number
|
private apc_port: number
|
||||||
private apc_username: string
|
private apc_username: string
|
||||||
private apc_password: string
|
private apc_password: string
|
||||||
|
|
@ -100,7 +100,7 @@ class APCController {
|
||||||
this.buffer = ''
|
this.buffer = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// appendLog(data, 0, 0, this.apc_number || 0)
|
appendLog(data, 0, 0, this.apc_number || 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleClose(): void {
|
private _handleClose(): void {
|
||||||
|
|
@ -130,7 +130,7 @@ class APCController {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log('[ECONNRESET] Trying reconnect apc:', this.apc_ip)
|
console.log('[ECONNRESET] Trying reconnect apc:', this.apc_ip)
|
||||||
this.reconnect()
|
this.reconnect()
|
||||||
}, 10000)
|
}, 15000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,7 @@ export default class LineConnection {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
writeCommand(cmd: string, isWrite = false) {
|
writeCommand(cmd: string | Buffer<ArrayBuffer>, isWrite = false) {
|
||||||
if (this.client.destroyed) {
|
if (this.client.destroyed) {
|
||||||
console.log(`⚠️ Cannot send, line ${this.config.lineNumber} is closed`)
|
console.log(`⚠️ Cannot send, line ${this.config.lineNumber} is closed`)
|
||||||
return
|
return
|
||||||
|
|
@ -513,4 +513,17 @@ export default class LineConnection {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gửi nhiều ký tự ESC để vào ROMMON
|
||||||
|
breakSpam() {
|
||||||
|
let count = 0
|
||||||
|
const escInterval = setInterval(() => {
|
||||||
|
if (count >= 100) {
|
||||||
|
clearInterval(escInterval)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.writeCommand(Buffer.from([0xff, 0xf3])) // Ctrl + Break
|
||||||
|
count++
|
||||||
|
}, 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ export default class SwitchController {
|
||||||
private _waitFor(prompt: string, timeout = 5000): Promise<string> {
|
private _waitFor(prompt: string, timeout = 5000): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
reject(new Error(`Timeout waiting for: ${prompt}`))
|
resolve(`Timeout waiting for: ${prompt}`)
|
||||||
}, timeout)
|
}, timeout)
|
||||||
|
|
||||||
this.promptCallbacks.push({
|
this.promptCallbacks.push({
|
||||||
|
|
@ -188,58 +188,74 @@ export default class SwitchController {
|
||||||
public async turnPortOff(port: string) {
|
public async turnPortOff(port: string) {
|
||||||
await this.enterEnableMode()
|
await this.enterEnableMode()
|
||||||
this._send(`configure terminal`)
|
this._send(`configure terminal`)
|
||||||
await this._waitFor('(config)#')
|
// await this._waitFor('(config)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`interface ${port}`)
|
this._send(`interface ${port}`)
|
||||||
await this._waitFor('(config-if)#')
|
// await this._waitFor('(config-if)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`shutdown`)
|
this._send(`shutdown`)
|
||||||
await this._waitFor('(config-if)#')
|
// await this._waitFor('(config-if)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`end`)
|
this._send(`end`)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async turnPortOn(port: string) {
|
public async turnPortOn(port: string) {
|
||||||
await this.enterEnableMode()
|
await this.enterEnableMode()
|
||||||
this._send(`configure terminal`)
|
this._send(`configure terminal`)
|
||||||
await this._waitFor('(config)#')
|
// await this._waitFor('(config)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`interface ${port}`)
|
this._send(`interface ${port}`)
|
||||||
await this._waitFor('(config-if)#')
|
// await this._waitFor('(config-if)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`no shutdown`)
|
this._send(`no shutdown`)
|
||||||
await this._waitFor('(config-if)#')
|
// await this._waitFor('(config-if)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`end`)
|
this._send(`end`)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async restartPort(port: string) {
|
public async restartPort(port: string) {
|
||||||
await this.enterEnableMode()
|
await this.enterEnableMode()
|
||||||
this._send(`configure terminal`)
|
this._send(`configure terminal`)
|
||||||
await this._waitFor('(config)#')
|
// await this._waitFor('(config)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`interface ${port}`)
|
this._send(`interface ${port}`)
|
||||||
await this._waitFor('(config-if)#')
|
// await this._waitFor('(config-if)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`shutdown`)
|
this._send(`shutdown`)
|
||||||
await this._waitFor('(config-if)#')
|
// await this._waitFor('(config-if)#')
|
||||||
|
await this.sleep(500)
|
||||||
await this.sleep(2000)
|
await this.sleep(2000)
|
||||||
this._send(`no shutdown`)
|
this._send(`no shutdown`)
|
||||||
await this._waitFor('(config-if)#')
|
// await this._waitFor('(config-if)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`end`)
|
this._send(`end`)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async disablePoE(port: string) {
|
public async disablePoE(port: string) {
|
||||||
await this.enterEnableMode()
|
await this.enterEnableMode()
|
||||||
this._send(`configure terminal`)
|
this._send(`configure terminal`)
|
||||||
await this._waitFor('(config)#')
|
// await this._waitFor('(config)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`interface ${port}`)
|
this._send(`interface ${port}`)
|
||||||
await this._waitFor('(config-if)#')
|
// await this._waitFor('(config-if)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`power inline never`)
|
this._send(`power inline never`)
|
||||||
await this._waitFor('(config-if)#')
|
// await this._waitFor('(config-if)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`end`)
|
this._send(`end`)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async enablePoE(port: string) {
|
public async enablePoE(port: string) {
|
||||||
await this.enterEnableMode()
|
await this.enterEnableMode()
|
||||||
this._send(`configure terminal`)
|
this._send(`configure terminal`)
|
||||||
await this._waitFor('(config)#')
|
// await this._waitFor('(config)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`interface ${port}`)
|
this._send(`interface ${port}`)
|
||||||
await this._waitFor('(config-if)#')
|
// await this._waitFor('(config-if)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`power inline auto`)
|
this._send(`power inline auto`)
|
||||||
await this._waitFor('(config-if)#')
|
// await this._waitFor('(config-if)#')
|
||||||
|
await this.sleep(500)
|
||||||
this._send(`end`)
|
this._send(`end`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,12 +63,12 @@ export default class SocketIoProvider {
|
||||||
|
|
||||||
export class WebSocketIo {
|
export class WebSocketIo {
|
||||||
intervalMap: { [key: string]: NodeJS.Timeout } = {}
|
intervalMap: { [key: string]: NodeJS.Timeout } = {}
|
||||||
stationMap: Map<number, Station> = new Map()
|
|
||||||
lineMap: Map<number, LineConnection> = new Map() // key = lineId
|
lineMap: Map<number, LineConnection> = new Map() // key = lineId
|
||||||
userConnecting: Map<number, { userId: number; userName: string }> = new Map()
|
userConnecting: Map<number, { userId: number; userName: string }> = new Map()
|
||||||
apcsControl: Map<string, APCController> = new Map()
|
apcsControl: Map<string, APCController> = new Map()
|
||||||
switchControl: Map<string, SwitchController> = new Map()
|
switchControl: Map<string, SwitchController> = new Map()
|
||||||
lineConnecting: number[] = [] // key = lineId
|
lineConnecting: number[] = [] // key = lineId
|
||||||
|
intervalKeepConnect: { [key: string]: NodeJS.Timeout } = {}
|
||||||
|
|
||||||
constructor(protected app: ApplicationService) {}
|
constructor(protected app: ApplicationService) {}
|
||||||
|
|
||||||
|
|
@ -115,7 +115,16 @@ export class WebSocketIo {
|
||||||
listLineS.forEach((el) => {
|
listLineS.forEach((el) => {
|
||||||
if (el?.userOpenCLI === userName) {
|
if (el?.userOpenCLI === userName) {
|
||||||
const line = this.lineMap.get(el.id)
|
const line = this.lineMap.get(el.id)
|
||||||
if (line && line?.userCloseCLI()) line?.userCloseCLI()
|
if (line) {
|
||||||
|
line.config.openCLI = false
|
||||||
|
line.config.userEmailOpenCLI = ''
|
||||||
|
line.config.userOpenCLI = ''
|
||||||
|
io.emit('user_close_cli', {
|
||||||
|
stationId: line.config.stationId,
|
||||||
|
lineId: line.config.id,
|
||||||
|
userEmailOpenCLI: '',
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
@ -136,7 +145,8 @@ export class WebSocketIo {
|
||||||
io,
|
io,
|
||||||
stationId,
|
stationId,
|
||||||
lineIds,
|
lineIds,
|
||||||
async (line) => line.writeCommand(command, true),
|
async (line) =>
|
||||||
|
command === 'spam_break' ? line.breakSpam() : line.writeCommand(command, true),
|
||||||
{ command, timeout: 120000 }
|
{ command, timeout: 120000 }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
@ -159,21 +169,26 @@ export class WebSocketIo {
|
||||||
socket.on('open_cli', async (data) => {
|
socket.on('open_cli', async (data) => {
|
||||||
const { lineId, userEmail, userName: name, stationId } = data
|
const { lineId, userEmail, userName: name, stationId } = data
|
||||||
const line = this.lineMap.get(lineId)
|
const line = this.lineMap.get(lineId)
|
||||||
if (line && line?.userOpenCLI) {
|
if (line) {
|
||||||
line?.userOpenCLI({ userEmail, userName: name })
|
if (line?.userOpenCLI) line?.userOpenCLI({ userEmail, userName: name })
|
||||||
|
else {
|
||||||
|
line.config.openCLI = true
|
||||||
|
line.config.userEmailOpenCLI = userEmail
|
||||||
|
line.config.userOpenCLI = userName
|
||||||
|
io.emit('user_open_cli', {
|
||||||
|
stationId: line.config.stationId,
|
||||||
|
lineId: line.config.id,
|
||||||
|
userEmailOpenCLI: userEmail,
|
||||||
|
userOpenCLI: userName,
|
||||||
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.lineConnecting.includes(lineId)) return
|
if (this.lineConnecting.includes(lineId)) return
|
||||||
const linesData = await Line.findBy('id', lineId)
|
const linesData = await Line.findBy('id', lineId)
|
||||||
const stationData = await Station.findBy('id', stationId)
|
const stationData = await Station.findBy('id', stationId)
|
||||||
if (linesData && stationData) {
|
if (linesData && stationData) {
|
||||||
this.lineConnecting.push(lineId)
|
this.lineConnecting.push(lineId)
|
||||||
await this.connectLine(
|
await this.connectLine(io, [linesData], stationData)
|
||||||
io,
|
|
||||||
[linesData],
|
|
||||||
stationData,
|
|
||||||
line?.config?.output || '',
|
|
||||||
line?.config?.commands || []
|
|
||||||
)
|
|
||||||
const lineReconnect = this.lineMap.get(lineId)
|
const lineReconnect = this.lineMap.get(lineId)
|
||||||
if (lineReconnect) {
|
if (lineReconnect) {
|
||||||
lineReconnect.userOpenCLI({ userEmail, userName: name })
|
lineReconnect.userOpenCLI({ userEmail, userName: name })
|
||||||
|
|
@ -185,21 +200,25 @@ export class WebSocketIo {
|
||||||
socket.on('close_cli', async (data) => {
|
socket.on('close_cli', async (data) => {
|
||||||
const { lineId, stationId } = data
|
const { lineId, stationId } = data
|
||||||
const line = this.lineMap.get(lineId)
|
const line = this.lineMap.get(lineId)
|
||||||
if (line && line?.userCloseCLI) {
|
if (line) {
|
||||||
line?.userCloseCLI()
|
if (line?.userCloseCLI) line?.userCloseCLI()
|
||||||
|
else {
|
||||||
|
line.config.openCLI = false
|
||||||
|
line.config.userEmailOpenCLI = ''
|
||||||
|
line.config.userOpenCLI = ''
|
||||||
|
io.emit('user_close_cli', {
|
||||||
|
stationId: line.config.stationId,
|
||||||
|
lineId: line.config.id,
|
||||||
|
userEmailOpenCLI: '',
|
||||||
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.lineConnecting.includes(lineId)) return
|
if (this.lineConnecting.includes(lineId)) return
|
||||||
const linesData = await Line.findBy('id', lineId)
|
const linesData = await Line.findBy('id', lineId)
|
||||||
const stationData = await Station.findBy('id', stationId)
|
const stationData = await Station.findBy('id', stationId)
|
||||||
if (linesData && stationData) {
|
if (linesData && stationData) {
|
||||||
this.lineConnecting.push(lineId)
|
this.lineConnecting.push(lineId)
|
||||||
await this.connectLine(
|
await this.connectLine(io, [linesData], stationData)
|
||||||
io,
|
|
||||||
[linesData],
|
|
||||||
stationData,
|
|
||||||
line?.config?.output || '',
|
|
||||||
line?.config?.commands || []
|
|
||||||
)
|
|
||||||
const lineReconnect = this.lineMap.get(lineId)
|
const lineReconnect = this.lineMap.get(lineId)
|
||||||
if (lineReconnect) {
|
if (lineReconnect) {
|
||||||
lineReconnect.userCloseCLI()
|
lineReconnect.userCloseCLI()
|
||||||
|
|
@ -265,7 +284,10 @@ export class WebSocketIo {
|
||||||
if (!station) return
|
if (!station) return
|
||||||
const apcIp = (station as any)[`${apcName}_ip`] as string
|
const apcIp = (station as any)[`${apcName}_ip`] as string
|
||||||
const apc = this.apcsControl.get(apcIp)
|
const apc = this.apcsControl.get(apcIp)
|
||||||
if (apc) await apc.reconnect()
|
if (apc) {
|
||||||
|
await apc.reconnect()
|
||||||
|
this.keepConnectAPC(apcIp, io)
|
||||||
|
} else await this.connectApc(io, apcName, station)
|
||||||
} else {
|
} else {
|
||||||
for (const outletNumber of outletNumbers) {
|
for (const outletNumber of outletNumbers) {
|
||||||
if (!outletNumber || outletNumber < 0) return
|
if (!outletNumber || outletNumber < 0) return
|
||||||
|
|
@ -282,7 +304,11 @@ export class WebSocketIo {
|
||||||
|
|
||||||
const apcIp = (station as any)[`${apcName}_ip`] as string
|
const apcIp = (station as any)[`${apcName}_ip`] as string
|
||||||
const apc = this.apcsControl.get(apcIp)
|
const apc = this.apcsControl.get(apcIp)
|
||||||
if (apc && apc.status === 'CONNECTED') {
|
if (apc && apc.status !== 'CONNECTED') {
|
||||||
|
await apc.reconnect()
|
||||||
|
this.keepConnectAPC(apcIp, io)
|
||||||
|
}
|
||||||
|
if (apc) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'on':
|
case 'on':
|
||||||
await apc?.turnOnOutlet(outletNumber)
|
await apc?.turnOnOutlet(outletNumber)
|
||||||
|
|
@ -302,24 +328,6 @@ export class WebSocketIo {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
apc?.navigateToOutlets()
|
apc?.navigateToOutlets()
|
||||||
}, 10000)
|
}, 10000)
|
||||||
} else if (apc && apc.status !== 'CONNECTED') {
|
|
||||||
await apc.reconnect()
|
|
||||||
switch (action) {
|
|
||||||
case 'on':
|
|
||||||
await apc?.turnOnOutlet(outletNumber)
|
|
||||||
break
|
|
||||||
case 'off':
|
|
||||||
await apc?.turnOffOutlet(outletNumber)
|
|
||||||
break
|
|
||||||
case 'restart':
|
|
||||||
await apc?.restartOutlet(outletNumber)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
setTimeout(() => {
|
|
||||||
apc?.navigateToOutlets()
|
|
||||||
}, 10000)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -336,8 +344,11 @@ export class WebSocketIo {
|
||||||
data: apc.output,
|
data: apc.output,
|
||||||
status: apc.status,
|
status: apc.status,
|
||||||
})
|
})
|
||||||
|
this.keepConnectAPC(apcIp, io)
|
||||||
} else if (apc && apc.status !== 'CONNECTED') {
|
} else if (apc && apc.status !== 'CONNECTED') {
|
||||||
await apc.reconnect()
|
await apc.reconnect()
|
||||||
|
this.apcsControl.set(apcIp, apc)
|
||||||
|
this.keepConnectAPC(apcIp, io)
|
||||||
} else await this.connectApc(io, apcName, station)
|
} else await this.connectApc(io, apcName, station)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
|
|
@ -442,7 +453,6 @@ export class WebSocketIo {
|
||||||
commands: string[] = []
|
commands: string[] = []
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
this.stationMap.set(station.id, station)
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const lineConn = new LineConnection(
|
const lineConn = new LineConnection(
|
||||||
{
|
{
|
||||||
|
|
@ -584,6 +594,7 @@ export class WebSocketIo {
|
||||||
await apc.connect()
|
await apc.connect()
|
||||||
await apc.login()
|
await apc.login()
|
||||||
this.apcsControl.set(ip, apc)
|
this.apcsControl.set(ip, apc)
|
||||||
|
this.keepConnectAPC(ip, socket)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
}
|
}
|
||||||
|
|
@ -603,7 +614,7 @@ export class WebSocketIo {
|
||||||
status: 'DISCONNECTED',
|
status: 'DISCONNECTED',
|
||||||
message: `Missing Switch configuration`,
|
message: `Missing Switch configuration`,
|
||||||
})
|
})
|
||||||
throw new Error(`Missing Switch configuration`)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tạo APC Controller instance
|
// Tạo APC Controller instance
|
||||||
|
|
@ -633,7 +644,10 @@ export class WebSocketIo {
|
||||||
|
|
||||||
private async clearLineBeforeConnect(stationId: number, clearLine: number) {
|
private async clearLineBeforeConnect(stationId: number, clearLine: number) {
|
||||||
const station = await Station.find(stationId)
|
const station = await Station.find(stationId)
|
||||||
if (!station) throw new Error(`Station ${stationId} not found`)
|
if (!station) {
|
||||||
|
console.log('[ERROR connect station] Not found!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Kết nối tới station qua Telnet / Socket
|
// Kết nối tới station qua Telnet / Socket
|
||||||
const client = new net.Socket()
|
const client = new net.Socket()
|
||||||
|
|
@ -679,7 +693,9 @@ export class WebSocketIo {
|
||||||
const newMap = new Map<number, LineConnection>()
|
const newMap = new Map<number, LineConnection>()
|
||||||
this.lineMap.forEach((line, id) => {
|
this.lineMap.forEach((line, id) => {
|
||||||
if (line && line.config) {
|
if (line && line.config) {
|
||||||
newMap.set(id, { config: { ...line.config, status: 'disconnected' } } as LineConnection)
|
newMap.set(id, {
|
||||||
|
config: { ...line.config, status: 'disconnected' },
|
||||||
|
} as LineConnection)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -695,4 +711,19 @@ export class WebSocketIo {
|
||||||
const parsed = JSON.parse(raw)
|
const parsed = JSON.parse(raw)
|
||||||
this.lineMap = new Map(parsed.lineMap)
|
this.lineMap = new Map(parsed.lineMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private keepConnectAPC = (ip: string, io: any) => {
|
||||||
|
if (this.intervalKeepConnect[`${ip}`]) {
|
||||||
|
clearInterval(this.intervalKeepConnect[`${ip}`])
|
||||||
|
delete this.intervalKeepConnect[`${ip}`]
|
||||||
|
}
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
const apcConnect = this.apcsControl.get(ip)
|
||||||
|
if (apcConnect && apcConnect.status === 'CONNECTED') {
|
||||||
|
apcConnect._send('ENTER')
|
||||||
|
}
|
||||||
|
}, 40000)
|
||||||
|
|
||||||
|
this.intervalKeepConnect[`${ip}`] = interval
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,3 +70,10 @@ body {
|
||||||
margin: 0.1rem auto 0;
|
margin: 0.1rem auto 0;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.containerMain {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 88vh;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,22 +27,22 @@ import type {
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import CardLine from "./components/CardLine";
|
import CardLine from "./components/CardLine";
|
||||||
import { SocketProvider, useSocket } from "./context/SocketContext";
|
import { SocketProvider, useSocket } from "./context/SocketContext";
|
||||||
import {
|
import // ButtonConnect,
|
||||||
// ButtonConnect,
|
// ButtonControlApc,
|
||||||
ButtonControlApc,
|
// ButtonCopy,
|
||||||
ButtonCopy,
|
// ButtonDPELP,
|
||||||
ButtonDPELP,
|
// ButtonScenario,
|
||||||
ButtonScenario,
|
// ButtonSelect,
|
||||||
ButtonSelect,
|
"./components/ButtonAction";
|
||||||
} from "./components/ButtonAction";
|
|
||||||
import StationSetting from "./components/FormAddEdit";
|
import StationSetting from "./components/FormAddEdit";
|
||||||
import DrawerScenario from "./components/DrawerScenario";
|
// import DrawerScenario from "./components/DrawerScenario";
|
||||||
import { Notifications } from "@mantine/notifications";
|
import { Notifications } from "@mantine/notifications";
|
||||||
import ModalTerminal from "./components/ModalTerminal";
|
import ModalTerminal from "./components/ModalTerminal";
|
||||||
import PageLogin from "./components/Authentication/LoginPage";
|
import PageLogin from "./components/Authentication/LoginPage";
|
||||||
import DrawerLogs from "./components/DrawerLogs";
|
// import DrawerLogs from "./components/DrawerLogs";
|
||||||
import DraggableTabs from "./components/DragTabs";
|
import DraggableTabs from "./components/DragTabs";
|
||||||
import { isJsonString } from "./untils/helper";
|
import { isJsonString } from "./untils/helper";
|
||||||
|
import BottomToolBar from "./components/BottomToolBar";
|
||||||
|
|
||||||
const apiUrl = import.meta.env.VITE_BACKEND_URL;
|
const apiUrl = import.meta.env.VITE_BACKEND_URL;
|
||||||
|
|
||||||
|
|
@ -67,7 +67,6 @@ function App() {
|
||||||
const [stations, setStations] = useState<TStation[]>([]);
|
const [stations, setStations] = useState<TStation[]>([]);
|
||||||
const [selectedLines, setSelectedLines] = useState<TLine[]>([]);
|
const [selectedLines, setSelectedLines] = useState<TLine[]>([]);
|
||||||
const [activeTab, setActiveTab] = useState("0");
|
const [activeTab, setActiveTab] = useState("0");
|
||||||
const [showBottomShadow, setShowBottomShadow] = useState(false);
|
|
||||||
const [isDisable, setIsDisable] = useState(false);
|
const [isDisable, setIsDisable] = useState(false);
|
||||||
const [isOpenAddStation, setIsOpenAddStation] = useState(false);
|
const [isOpenAddStation, setIsOpenAddStation] = useState(false);
|
||||||
const [isEditStation, setIsEditStation] = useState(false);
|
const [isEditStation, setIsEditStation] = useState(false);
|
||||||
|
|
@ -84,6 +83,29 @@ function App() {
|
||||||
const [testLogContent, setTestLogContent] = useState("");
|
const [testLogContent, setTestLogContent] = useState("");
|
||||||
const [isLogModalOpen, setIsLogModalOpen] = useState(false);
|
const [isLogModalOpen, setIsLogModalOpen] = useState(false);
|
||||||
|
|
||||||
|
const connectApcSwitch = (station: TStation) => {
|
||||||
|
if (station?.apc_1_ip && station?.apc_1_port) {
|
||||||
|
socket?.emit("connect_apc", {
|
||||||
|
station: station,
|
||||||
|
apcIp: station?.apc_1_ip,
|
||||||
|
apcName: "apc_1",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (station?.apc_2_ip && station?.apc_2_port) {
|
||||||
|
socket?.emit("connect_apc", {
|
||||||
|
station: station,
|
||||||
|
apcIp: station?.apc_2_ip,
|
||||||
|
apcName: "apc_2",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (station?.switch_control_ip && station?.switch_control_port) {
|
||||||
|
socket?.emit("connect_switch", {
|
||||||
|
station: station,
|
||||||
|
ip: station?.switch_control_ip,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// function get list station
|
// function get list station
|
||||||
const getStation = async () => {
|
const getStation = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -91,6 +113,9 @@ function App() {
|
||||||
if (response.status) {
|
if (response.status) {
|
||||||
if (Array.isArray(response.data)) {
|
if (Array.isArray(response.data)) {
|
||||||
setStations(response.data);
|
setStations(response.data);
|
||||||
|
response.data.forEach((station) => {
|
||||||
|
connectApcSwitch(station);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -113,9 +138,10 @@ function App() {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!socket) return;
|
||||||
getStation();
|
getStation();
|
||||||
getScenarios();
|
getScenarios();
|
||||||
}, []);
|
}, [socket]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!socket || !stations?.length) return;
|
if (!socket || !stations?.length) return;
|
||||||
|
|
@ -363,16 +389,6 @@ function App() {
|
||||||
setSelectedLine(data);
|
setSelectedLine(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
if (selectedLine)
|
|
||||||
socket?.emit("close_cli", {
|
|
||||||
lineId: selectedLine?.id,
|
|
||||||
stationId: selectedLine?.station_id,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container w={"100%"} style={{ maxWidth: "100%" }}>
|
<Container w={"100%"} style={{ maxWidth: "100%" }}>
|
||||||
<DraggableTabs
|
<DraggableTabs
|
||||||
|
|
@ -389,60 +405,30 @@ function App() {
|
||||||
value={station.id.toString()}
|
value={station.id.toString()}
|
||||||
pt="md"
|
pt="md"
|
||||||
>
|
>
|
||||||
<Grid>
|
<Flex className={classes.containerMain}>
|
||||||
<Grid.Col
|
<Grid>
|
||||||
span={11}
|
<Grid.Col
|
||||||
style={{
|
span={12}
|
||||||
boxShadow: showBottomShadow
|
style={{
|
||||||
? "inset 0 -12px 10px -10px rgba(0, 0, 0, 0.2)"
|
borderRadius: 8,
|
||||||
: "none",
|
|
||||||
borderRadius: 8,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ScrollArea
|
|
||||||
h={"84vh"}
|
|
||||||
onScrollPositionChange={({ y }) => {
|
|
||||||
const el = document.querySelector(
|
|
||||||
".mantine-ScrollArea-viewport"
|
|
||||||
);
|
|
||||||
if (!el) return;
|
|
||||||
const maxScroll = el.scrollHeight - el.clientHeight;
|
|
||||||
setShowBottomShadow(y < maxScroll - 2);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{station.lines.length > 8 ? (
|
<ScrollArea h={"63vh"}>
|
||||||
<Grid
|
{station.lines.length > 8 ? (
|
||||||
style={{
|
<Grid
|
||||||
marginLeft: "3%",
|
style={{
|
||||||
width: "90%",
|
marginLeft: "3%",
|
||||||
display: "flex",
|
width: "95%",
|
||||||
justifyContent: "center",
|
display: "flex",
|
||||||
}}
|
justifyContent: "center",
|
||||||
>
|
}}
|
||||||
<Grid.Col span={6}>
|
>
|
||||||
<Flex wrap="wrap" gap="sm" justify={"center"}>
|
<Grid.Col
|
||||||
{station.lines.slice(0, 8).map((line, i) => (
|
span={6}
|
||||||
<CardLine
|
style={{ borderRight: "1px solid #ccc" }}
|
||||||
key={i}
|
>
|
||||||
socket={socket}
|
<Flex wrap="wrap" gap="sm" justify={"center"}>
|
||||||
stationItem={station}
|
{station.lines.slice(0, 8).map((line, i) => (
|
||||||
line={line}
|
|
||||||
selectedLines={selectedLines}
|
|
||||||
setSelectedLines={setSelectedLines}
|
|
||||||
openTerminal={openTerminal}
|
|
||||||
loadTerminal={
|
|
||||||
loadingTerminal &&
|
|
||||||
Number(station.id) === Number(activeTab)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={6}>
|
|
||||||
<Flex wrap="wrap" gap="sm" justify={"center"}>
|
|
||||||
{station.lines
|
|
||||||
.slice(8, station.lines.length)
|
|
||||||
.map((line, i) => (
|
|
||||||
<CardLine
|
<CardLine
|
||||||
key={i}
|
key={i}
|
||||||
socket={socket}
|
socket={socket}
|
||||||
|
|
@ -457,142 +443,70 @@ function App() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
<Grid.Col span={6}>
|
||||||
) : station.lines.length <= 8 && station.lines.length > 0 ? (
|
<Flex wrap="wrap" gap="sm" justify={"center"}>
|
||||||
<Flex wrap="wrap" gap="sm" justify={"center"}>
|
{station.lines
|
||||||
{station.lines.map((line, i) => (
|
.slice(8, station.lines.length)
|
||||||
<CardLine
|
.map((line, i) => (
|
||||||
key={i}
|
<CardLine
|
||||||
socket={socket}
|
key={i}
|
||||||
stationItem={station}
|
socket={socket}
|
||||||
line={line}
|
stationItem={station}
|
||||||
selectedLines={selectedLines}
|
line={line}
|
||||||
setSelectedLines={setSelectedLines}
|
selectedLines={selectedLines}
|
||||||
openTerminal={openTerminal}
|
setSelectedLines={setSelectedLines}
|
||||||
loadTerminal={
|
openTerminal={openTerminal}
|
||||||
loadingTerminal &&
|
loadTerminal={
|
||||||
Number(station.id) === Number(activeTab)
|
loadingTerminal &&
|
||||||
}
|
Number(station.id) === Number(activeTab)
|
||||||
/>
|
}
|
||||||
))}
|
/>
|
||||||
</Flex>
|
))}
|
||||||
) : (
|
</Flex>
|
||||||
<Text ta="center" c="dimmed" mt="lg">
|
</Grid.Col>
|
||||||
No lines configured
|
</Grid>
|
||||||
</Text>
|
) : station.lines.length <= 8 &&
|
||||||
)}
|
station.lines.length > 0 ? (
|
||||||
</ScrollArea>
|
<Flex wrap="wrap" gap="sm" justify={"center"}>
|
||||||
</Grid.Col>
|
{station.lines.map((line, i) => (
|
||||||
<Grid.Col
|
<CardLine
|
||||||
span={1}
|
|
||||||
style={{ backgroundColor: "#f1f1f1", borderRadius: 8 }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
direction={"column"}
|
|
||||||
justify={"space-between"}
|
|
||||||
align={"center"}
|
|
||||||
h={"100%"}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
direction={"column"}
|
|
||||||
align={"center"}
|
|
||||||
gap={"6px"}
|
|
||||||
wrap={"wrap"}
|
|
||||||
>
|
|
||||||
<ButtonSelect
|
|
||||||
selectedLines={selectedLines}
|
|
||||||
setSelectedLines={setSelectedLines}
|
|
||||||
station={station}
|
|
||||||
/>
|
|
||||||
{/* <ButtonConnect
|
|
||||||
selectedLines={selectedLines}
|
|
||||||
setSelectedLines={setSelectedLines}
|
|
||||||
station={station}
|
|
||||||
socket={socket}
|
|
||||||
/> */}
|
|
||||||
<ButtonControlApc
|
|
||||||
selectedLines={selectedLines}
|
|
||||||
setSelectedLines={setSelectedLines}
|
|
||||||
station={station}
|
|
||||||
socket={socket}
|
|
||||||
/>
|
|
||||||
<ButtonCopy
|
|
||||||
selectedLines={selectedLines}
|
|
||||||
setSelectedLines={setSelectedLines}
|
|
||||||
/>
|
|
||||||
<Flex
|
|
||||||
w={"100%"}
|
|
||||||
direction={"column"}
|
|
||||||
align={"center"}
|
|
||||||
wrap={"wrap"}
|
|
||||||
>
|
|
||||||
<hr style={{ width: "100%" }} />
|
|
||||||
<DrawerScenario
|
|
||||||
scenarios={scenarios}
|
|
||||||
setScenarios={setScenarios}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<ButtonDPELP
|
|
||||||
socket={socket}
|
|
||||||
selectedLines={selectedLines}
|
|
||||||
isDisable={isDisable || selectedLines.length === 0}
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedLines([]);
|
|
||||||
setIsDisable(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsDisable(false);
|
|
||||||
}, 5000);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ScrollArea h={"60vh"} style={{ paddingBottom: "12px" }}>
|
|
||||||
<Flex
|
|
||||||
w={"100%"}
|
|
||||||
direction={"column"}
|
|
||||||
wrap={"wrap"}
|
|
||||||
gap={"6px"}
|
|
||||||
>
|
|
||||||
{scenarios.map((el, i) => (
|
|
||||||
<ButtonScenario
|
|
||||||
key={i}
|
key={i}
|
||||||
socket={socket}
|
socket={socket}
|
||||||
selectedLines={selectedLines.filter(
|
stationItem={station}
|
||||||
(el) =>
|
line={line}
|
||||||
!el?.userEmailOpenCLI ||
|
selectedLines={selectedLines}
|
||||||
el?.userEmailOpenCLI === user?.email
|
setSelectedLines={setSelectedLines}
|
||||||
)}
|
openTerminal={openTerminal}
|
||||||
isDisable={
|
loadTerminal={
|
||||||
isDisable ||
|
loadingTerminal &&
|
||||||
selectedLines.filter(
|
Number(station.id) === Number(activeTab)
|
||||||
(el) =>
|
|
||||||
!el?.userEmailOpenCLI ||
|
|
||||||
el?.userEmailOpenCLI === user?.email
|
|
||||||
).length === 0
|
|
||||||
}
|
}
|
||||||
onClick={() => {
|
|
||||||
setSelectedLines([]);
|
|
||||||
setIsDisable(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsDisable(false);
|
|
||||||
}, 5000);
|
|
||||||
}}
|
|
||||||
scenario={el}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
</ScrollArea>
|
) : (
|
||||||
</Flex>
|
<Text ta="center" c="dimmed" mt="lg">
|
||||||
<DrawerLogs
|
No lines configured
|
||||||
socket={socket}
|
</Text>
|
||||||
isLogModalOpen={isLogModalOpen}
|
)}
|
||||||
setIsLogModalOpen={setIsLogModalOpen}
|
</ScrollArea>
|
||||||
testLogContent={testLogContent}
|
</Grid.Col>
|
||||||
setTestLogContent={setTestLogContent}
|
</Grid>
|
||||||
/>
|
<BottomToolBar
|
||||||
</Flex>
|
selectedLines={selectedLines}
|
||||||
</Grid.Col>
|
socket={socket}
|
||||||
</Grid>
|
setSelectedLines={setSelectedLines}
|
||||||
|
isDisable={isDisable}
|
||||||
|
setIsDisable={setIsDisable}
|
||||||
|
station={station}
|
||||||
|
testLogContent={testLogContent}
|
||||||
|
isLogModalOpen={isLogModalOpen}
|
||||||
|
setIsLogModalOpen={setIsLogModalOpen}
|
||||||
|
setTestLogContent={setTestLogContent}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
))}
|
))}
|
||||||
onChange={(id) => {
|
onChange={(id) => {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,278 @@
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
CloseButton,
|
||||||
|
Flex,
|
||||||
|
Input,
|
||||||
|
ScrollArea,
|
||||||
|
Tabs,
|
||||||
|
Text,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { useState } from "react";
|
||||||
|
import classes from "./Component.module.css";
|
||||||
|
import type { TLine, TStation } from "../untils/types";
|
||||||
|
import type { Socket } from "socket.io-client";
|
||||||
|
import { ButtonDPELP, ButtonSelect } from "./ButtonAction";
|
||||||
|
import DrawerLogs from "./DrawerLogs";
|
||||||
|
import { DrawerAPCControl, DrawerSwitchControl } from "./DrawerControl";
|
||||||
|
|
||||||
|
interface TabsProps {
|
||||||
|
selectedLines: TLine[];
|
||||||
|
socket: Socket | null;
|
||||||
|
setSelectedLines: (lines: React.SetStateAction<TLine[]>) => void;
|
||||||
|
isDisable: boolean;
|
||||||
|
station: TStation;
|
||||||
|
setIsDisable: (lines: React.SetStateAction<boolean>) => void;
|
||||||
|
testLogContent: string;
|
||||||
|
isLogModalOpen: boolean;
|
||||||
|
setIsLogModalOpen: (lines: React.SetStateAction<boolean>) => void;
|
||||||
|
setTestLogContent: (lines: React.SetStateAction<string>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BottomToolBar = ({
|
||||||
|
selectedLines,
|
||||||
|
socket,
|
||||||
|
setSelectedLines,
|
||||||
|
isDisable,
|
||||||
|
station,
|
||||||
|
setIsDisable,
|
||||||
|
testLogContent,
|
||||||
|
isLogModalOpen,
|
||||||
|
setIsLogModalOpen,
|
||||||
|
setTestLogContent,
|
||||||
|
}: TabsProps) => {
|
||||||
|
const [valueInput, setValueInput] = useState<string>("");
|
||||||
|
const [activeTabBottom, setActiveBottom] = useState<string>("command");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tabs
|
||||||
|
defaultValue="command"
|
||||||
|
orientation="vertical"
|
||||||
|
value={activeTabBottom}
|
||||||
|
onChange={(val) => {
|
||||||
|
setActiveBottom(val || "command");
|
||||||
|
}}
|
||||||
|
className={classes.containerBottom}
|
||||||
|
>
|
||||||
|
<Tabs.List>
|
||||||
|
<Tabs.Tab
|
||||||
|
style={{
|
||||||
|
backgroundColor: activeTabBottom === "command" ? "#c8d9fd" : "",
|
||||||
|
}}
|
||||||
|
value="command"
|
||||||
|
>
|
||||||
|
Command Line
|
||||||
|
</Tabs.Tab>
|
||||||
|
<Tabs.Tab
|
||||||
|
style={{
|
||||||
|
backgroundColor: activeTabBottom === "apc" ? "#c8d9fd" : "",
|
||||||
|
}}
|
||||||
|
value="apc"
|
||||||
|
>
|
||||||
|
APC
|
||||||
|
</Tabs.Tab>
|
||||||
|
<Tabs.Tab
|
||||||
|
style={{
|
||||||
|
backgroundColor: activeTabBottom === "switch" ? "#c8d9fd" : "",
|
||||||
|
}}
|
||||||
|
value="switch"
|
||||||
|
>
|
||||||
|
Switch
|
||||||
|
</Tabs.Tab>
|
||||||
|
</Tabs.List>
|
||||||
|
|
||||||
|
<Tabs.Panel value="command" p={"xs"}>
|
||||||
|
<Flex justify={"space-between"}>
|
||||||
|
<ScrollArea h={"17vh"}>
|
||||||
|
<Flex wrap={"wrap"} gap={"xs"} w={"400px"}>
|
||||||
|
{selectedLines.map((el) => (
|
||||||
|
<Box
|
||||||
|
key={el.id}
|
||||||
|
style={{
|
||||||
|
paddingLeft: "4px",
|
||||||
|
height: "30px",
|
||||||
|
width: "80px",
|
||||||
|
backgroundColor: "#d4e3ff",
|
||||||
|
borderRadius: "8px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex align={"center"} justify={"center"} gap={"4px"}>
|
||||||
|
<Text fz={"12px"}>Line {el.lineNumber}</Text>
|
||||||
|
<CloseButton
|
||||||
|
style={{ minWidth: "24px" }}
|
||||||
|
aria-label="Clear input"
|
||||||
|
onClick={() =>
|
||||||
|
setSelectedLines(
|
||||||
|
selectedLines.filter((line) => line.id !== el.id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
</ScrollArea>
|
||||||
|
<Box pl={"md"} pr={"md"}>
|
||||||
|
<Flex justify={"space-between"} mb={"xs"}>
|
||||||
|
<Flex></Flex>
|
||||||
|
<Button
|
||||||
|
disabled={isDisable || selectedLines.length === 0}
|
||||||
|
variant="filled"
|
||||||
|
color="orange"
|
||||||
|
size="xs"
|
||||||
|
radius="md"
|
||||||
|
onClick={() => {
|
||||||
|
const listLine = selectedLines.length
|
||||||
|
? selectedLines
|
||||||
|
: station?.lines;
|
||||||
|
if (listLine.length) {
|
||||||
|
socket?.emit("write_command_line_from_web", {
|
||||||
|
lineIds: listLine.map((line) => line.id),
|
||||||
|
stationId: station.id,
|
||||||
|
command: " \n",
|
||||||
|
});
|
||||||
|
setIsDisable(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsDisable(false);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Send Break
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
<Box>
|
||||||
|
<Input
|
||||||
|
style={{
|
||||||
|
width: "600px",
|
||||||
|
boxShadow: "0px 0px 10px rgba(0, 0, 0, 0.1)",
|
||||||
|
}}
|
||||||
|
placeholder={"Send command to port(s)"}
|
||||||
|
value={valueInput}
|
||||||
|
onChange={(event) => {
|
||||||
|
const newValue = event.currentTarget.value;
|
||||||
|
setValueInput(newValue);
|
||||||
|
}}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
const listLine = selectedLines.length
|
||||||
|
? selectedLines
|
||||||
|
: station?.lines;
|
||||||
|
if (listLine?.length) {
|
||||||
|
socket?.emit("write_command_line_from_web", {
|
||||||
|
lineIds: listLine.map((line) => line.id),
|
||||||
|
stationId: station.id,
|
||||||
|
command: valueInput + "\n",
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
socket?.emit("write_command_line_from_web", {
|
||||||
|
lineIds: listLine.map((line) => line.id),
|
||||||
|
stationId: station.id,
|
||||||
|
command: " \n",
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
setValueInput("");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
rightSectionPointerEvents="all"
|
||||||
|
rightSection={
|
||||||
|
<CloseButton
|
||||||
|
aria-label="Clear input"
|
||||||
|
onClick={() => setValueInput("")}
|
||||||
|
style={{
|
||||||
|
display: valueInput ? undefined : "none",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box style={{ width: "220px" }}>
|
||||||
|
<Flex align={"center"} wrap={"wrap"} gap={"xs"}>
|
||||||
|
<ButtonSelect
|
||||||
|
selectedLines={selectedLines}
|
||||||
|
setSelectedLines={setSelectedLines}
|
||||||
|
station={station}
|
||||||
|
/>
|
||||||
|
<ButtonDPELP
|
||||||
|
socket={socket}
|
||||||
|
selectedLines={selectedLines}
|
||||||
|
isDisable={isDisable || selectedLines.length === 0}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedLines([]);
|
||||||
|
setIsDisable(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsDisable(false);
|
||||||
|
}, 5000);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
disabled={isDisable || selectedLines.length === 0}
|
||||||
|
variant="outline"
|
||||||
|
color="green"
|
||||||
|
style={{ height: "30px", width: "100px" }}
|
||||||
|
onClick={() => {
|
||||||
|
if (selectedLines.length !== station.lines.length)
|
||||||
|
setSelectedLines(station.lines);
|
||||||
|
else setSelectedLines([]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Scenario
|
||||||
|
</Button>
|
||||||
|
{/* <Flex
|
||||||
|
w={"100%"}
|
||||||
|
direction={"column"}
|
||||||
|
wrap={"wrap"}
|
||||||
|
gap={"6px"}
|
||||||
|
>
|
||||||
|
{scenarios.map((el, i) => (
|
||||||
|
<ButtonScenario
|
||||||
|
key={i}
|
||||||
|
socket={socket}
|
||||||
|
selectedLines={selectedLines.filter(
|
||||||
|
(el) =>
|
||||||
|
!el?.userEmailOpenCLI ||
|
||||||
|
el?.userEmailOpenCLI === user?.email
|
||||||
|
)}
|
||||||
|
isDisable={
|
||||||
|
isDisable ||
|
||||||
|
selectedLines.filter(
|
||||||
|
(el) =>
|
||||||
|
!el?.userEmailOpenCLI ||
|
||||||
|
el?.userEmailOpenCLI === user?.email
|
||||||
|
).length === 0
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedLines([]);
|
||||||
|
setIsDisable(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsDisable(false);
|
||||||
|
}, 5000);
|
||||||
|
}}
|
||||||
|
scenario={el}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Flex> */}
|
||||||
|
<DrawerLogs
|
||||||
|
socket={socket}
|
||||||
|
isLogModalOpen={isLogModalOpen}
|
||||||
|
setIsLogModalOpen={setIsLogModalOpen}
|
||||||
|
testLogContent={testLogContent}
|
||||||
|
setTestLogContent={setTestLogContent}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Tabs.Panel>
|
||||||
|
<Tabs.Panel value="apc" ps={"xs"}>
|
||||||
|
<DrawerAPCControl socket={socket} stationAPI={station} />
|
||||||
|
</Tabs.Panel>
|
||||||
|
<Tabs.Panel value="switch" ps={"xs"}>
|
||||||
|
<DrawerSwitchControl socket={socket} stationAPI={station} />
|
||||||
|
</Tabs.Panel>
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BottomToolBar;
|
||||||
|
|
@ -111,3 +111,11 @@
|
||||||
.hideScrollBar::-webkit-scrollbar {
|
.hideScrollBar::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.containerBottom {
|
||||||
|
height: 22vh;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #f3f3f38c;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,8 @@ import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
|
||||||
CloseButton,
|
|
||||||
Flex,
|
Flex,
|
||||||
Group,
|
Group,
|
||||||
Input,
|
|
||||||
Menu,
|
Menu,
|
||||||
Tabs,
|
Tabs,
|
||||||
Text,
|
Text,
|
||||||
|
|
@ -39,7 +36,6 @@ import {
|
||||||
import classes from "./Component.module.css";
|
import classes from "./Component.module.css";
|
||||||
import type { TStation, TUser } from "../untils/types";
|
import type { TStation, TUser } from "../untils/types";
|
||||||
import type { Socket } from "socket.io-client";
|
import type { Socket } from "socket.io-client";
|
||||||
import { DrawerAPCControl, DrawerSwitchControl } from "./DrawerControl";
|
|
||||||
|
|
||||||
interface DraggableTabsProps {
|
interface DraggableTabsProps {
|
||||||
tabsData: TStation[];
|
tabsData: TStation[];
|
||||||
|
|
@ -87,6 +83,7 @@ function SortableTab({
|
||||||
transition,
|
transition,
|
||||||
cursor: "grab",
|
cursor: "grab",
|
||||||
userSelect: "none",
|
userSelect: "none",
|
||||||
|
backgroundColor: active === tab.id.toString() ? "#deffde" : "",
|
||||||
}}
|
}}
|
||||||
color={active === tab.id.toString() ? "green" : ""}
|
color={active === tab.id.toString() ? "green" : ""}
|
||||||
fw={600}
|
fw={600}
|
||||||
|
|
@ -116,7 +113,6 @@ export default function DraggableTabs({
|
||||||
setStationEdit,
|
setStationEdit,
|
||||||
active,
|
active,
|
||||||
setActive,
|
setActive,
|
||||||
onSendCommand,
|
|
||||||
}: DraggableTabsProps) {
|
}: DraggableTabsProps) {
|
||||||
const user = useMemo(() => {
|
const user = useMemo(() => {
|
||||||
return localStorage.getItem("user") &&
|
return localStorage.getItem("user") &&
|
||||||
|
|
@ -127,12 +123,6 @@ export default function DraggableTabs({
|
||||||
const [tabs, setTabs] = useState<TStation[]>(tabsData);
|
const [tabs, setTabs] = useState<TStation[]>(tabsData);
|
||||||
const [isChangeTab, setIsChangeTab] = useState<boolean>(false);
|
const [isChangeTab, setIsChangeTab] = useState<boolean>(false);
|
||||||
const [isSetActive, setIsSetActive] = useState<boolean>(false);
|
const [isSetActive, setIsSetActive] = useState<boolean>(false);
|
||||||
const [valueInput, setValueInput] = useState<string>("");
|
|
||||||
const [openedAPC, setOpenedAPC] = useState(false);
|
|
||||||
const [openedSwitch, setOpenedSwitch] = useState(false);
|
|
||||||
// const [active, setActive] = useState<string | null>(
|
|
||||||
// tabsData?.length > 0 ? tabsData[0]?.id.toString() : null
|
|
||||||
// );
|
|
||||||
|
|
||||||
const sensors = useSensors(useSensor(PointerSensor));
|
const sensors = useSensors(useSensor(PointerSensor));
|
||||||
|
|
||||||
|
|
@ -220,29 +210,6 @@ export default function DraggableTabs({
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const connectApcSwitch = (station: TStation) => {
|
|
||||||
if (station?.apc_1_ip && station?.apc_1_port) {
|
|
||||||
socket?.emit("connect_apc", {
|
|
||||||
station: station,
|
|
||||||
apcIp: station?.apc_1_ip,
|
|
||||||
apcName: "apc_1",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (station?.apc_2_ip && station?.apc_2_port) {
|
|
||||||
socket?.emit("connect_apc", {
|
|
||||||
station: station,
|
|
||||||
apcIp: station?.apc_2_ip,
|
|
||||||
apcName: "apc_2",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (station?.switch_control_ip && station?.switch_control_port) {
|
|
||||||
socket?.emit("connect_switch", {
|
|
||||||
station: station,
|
|
||||||
ip: station?.switch_control_ip,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DndContext
|
<DndContext
|
||||||
sensors={sensors}
|
sensors={sensors}
|
||||||
|
|
@ -259,69 +226,7 @@ export default function DraggableTabs({
|
||||||
w={w}
|
w={w}
|
||||||
>
|
>
|
||||||
<Flex justify={"space-between"}>
|
<Flex justify={"space-between"}>
|
||||||
<Flex style={{ width: "400px" }} align={"center"}>
|
<Flex></Flex>
|
||||||
<Input
|
|
||||||
style={{
|
|
||||||
width: "300px",
|
|
||||||
boxShadow: "0px 0px 10px rgba(0, 0, 0, 0.1)",
|
|
||||||
}}
|
|
||||||
placeholder={"Chat to Port/All"}
|
|
||||||
value={valueInput}
|
|
||||||
onChange={(event) => {
|
|
||||||
const newValue = event.currentTarget.value;
|
|
||||||
setValueInput(newValue);
|
|
||||||
}}
|
|
||||||
onKeyDown={(event) => {
|
|
||||||
if (event.key === "Enter") {
|
|
||||||
onSendCommand(valueInput);
|
|
||||||
setValueInput("");
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
rightSectionPointerEvents="all"
|
|
||||||
rightSection={
|
|
||||||
<CloseButton
|
|
||||||
aria-label="Clear input"
|
|
||||||
onClick={() => setValueInput("")}
|
|
||||||
style={{ display: valueInput ? undefined : "none" }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Flex gap={"xs"} ms={"md"}>
|
|
||||||
<Button
|
|
||||||
miw={"80px"}
|
|
||||||
size="xs"
|
|
||||||
fz={"sm"}
|
|
||||||
variant="filled"
|
|
||||||
onClick={() => {
|
|
||||||
const station = tabs.find(
|
|
||||||
(el) => el.id.toString() === active
|
|
||||||
);
|
|
||||||
if (!station) return;
|
|
||||||
setOpenedAPC(true);
|
|
||||||
connectApcSwitch(station);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
APC
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
miw={"80px"}
|
|
||||||
size="xs"
|
|
||||||
fz={"sm"}
|
|
||||||
variant="filled"
|
|
||||||
color="yellow"
|
|
||||||
onClick={() => {
|
|
||||||
const station = tabs.find(
|
|
||||||
(el) => el.id.toString() === active
|
|
||||||
);
|
|
||||||
if (!station) return;
|
|
||||||
setOpenedSwitch(true);
|
|
||||||
connectApcSwitch(station);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Switch
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Tabs.List className={classes.list}>
|
<Tabs.List className={classes.list}>
|
||||||
<SortableContext
|
<SortableContext
|
||||||
items={tabs}
|
items={tabs}
|
||||||
|
|
@ -429,36 +334,6 @@ export default function DraggableTabs({
|
||||||
|
|
||||||
{panels}
|
{panels}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
{tabs.find((el) => el.id.toString() === active) && (
|
|
||||||
<DrawerAPCControl
|
|
||||||
open={openedAPC}
|
|
||||||
onClose={() => {
|
|
||||||
setOpenedAPC(false);
|
|
||||||
}}
|
|
||||||
socket={socket}
|
|
||||||
stationAPI={tabs.find((el) => el.id.toString() === active) || tabs[0]}
|
|
||||||
openedSwitch={() => {
|
|
||||||
setOpenedAPC(false);
|
|
||||||
setOpenedSwitch(true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{tabs.find((el) => el.id.toString() === active) && (
|
|
||||||
<DrawerSwitchControl
|
|
||||||
open={openedSwitch}
|
|
||||||
onClose={() => {
|
|
||||||
setOpenedSwitch(false);
|
|
||||||
}}
|
|
||||||
socket={socket}
|
|
||||||
stationAPI={tabs.find((el) => el.id.toString() === active) || tabs[0]}
|
|
||||||
openedAPC={() => {
|
|
||||||
setOpenedSwitch(false);
|
|
||||||
setOpenedAPC(true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</DndContext>
|
</DndContext>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -177,6 +177,7 @@ function DrawerLogs({
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
style={{ height: "30px", width: "100px" }}
|
||||||
title="Add Scenario"
|
title="Add Scenario"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
// color="green"
|
// color="green"
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
||||||
loadingContent = false,
|
loadingContent = false,
|
||||||
onFocus,
|
onFocus,
|
||||||
onBlur,
|
onBlur,
|
||||||
line,
|
|
||||||
}) => {
|
}) => {
|
||||||
const xtermRef = useRef<HTMLDivElement>(null);
|
const xtermRef = useRef<HTMLDivElement>(null);
|
||||||
const terminal = useRef<Terminal>(null);
|
const terminal = useRef<Terminal>(null);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue