Update apc

This commit is contained in:
nguyentrungthat 2025-11-04 16:52:26 +07:00
parent abda7c4e99
commit d031c1e93e
7 changed files with 685 additions and 676 deletions

View File

@ -5,7 +5,7 @@ interface APCOptions {
port?: number port?: number
username: string username: string
password: string password: string
onData?: (data: string) => void onData?: (data: string, status: string) => void
number?: number number?: number
keep_connect?: boolean keep_connect?: boolean
} }
@ -26,7 +26,7 @@ class APCController {
private buffer: string private buffer: string
private output: string private output: string
private promptCallbacks: PromptCallback[] private promptCallbacks: PromptCallback[]
private onData: (data: string) => void private onData: (data: string, status: string) => void
private retryConnect: number private retryConnect: number
constructor({ host, port = 23, username, password, onData, number }: APCOptions) { constructor({ host, port = 23, username, password, onData, number }: APCOptions) {
@ -80,7 +80,7 @@ class APCController {
this.buffer += data this.buffer += data
this.buffer = this.buffer.slice(-1000) this.buffer = this.buffer.slice(-1000)
this.onData(this.buffer) this.onData(this.buffer, this.status)
if (this.promptCallbacks.length > 0) { if (this.promptCallbacks.length > 0) {
const { prompt, callback } = this.promptCallbacks[0] const { prompt, callback } = this.promptCallbacks[0]
@ -95,14 +95,14 @@ class APCController {
private _handleClose(): void { private _handleClose(): void {
this.status = 'DISCONNECTED' this.status = 'DISCONNECTED'
this.output += '\r\n\r\n[DISCONNECTED] Socket closed' this.output += '\r\n\r\n[DISCONNECTED] Socket closed'
this.onData(this.output) this.onData(this.output, this.status)
this._cleanup() this._cleanup()
} }
private async _handleTimeout(): Promise<void> { private async _handleTimeout(): Promise<void> {
this.status = 'TIMEOUT' this.status = 'TIMEOUT'
this.output += '\r\n\r\n[TIMEOUT] Connection timed out' this.output += '\r\n\r\n[TIMEOUT] Connection timed out'
this.onData(this.output) this.onData(this.output, this.status)
if (this.retryConnect <= 5) { if (this.retryConnect <= 5) {
await this.sleep(5000) await this.sleep(5000)
@ -114,7 +114,7 @@ class APCController {
private _handleError(err: NodeJS.ErrnoException): void { private _handleError(err: NodeJS.ErrnoException): void {
this.output += `\r\n\r\n[ERROR] ${err.message}` this.output += `\r\n\r\n[ERROR] ${err.message}`
this.onData(this.output) this.onData(this.output, this.status)
if (err.code === 'ECONNRESET') { if (err.code === 'ECONNRESET') {
setTimeout(() => { setTimeout(() => {
console.log('[ECONNRESET] Trying reconnect apc:', this.apc_ip) console.log('[ECONNRESET] Trying reconnect apc:', this.apc_ip)

View File

@ -235,17 +235,14 @@ export default class LineConnection {
}, script.timeout || 300000) }, script.timeout || 300000)
const runStep = async (index: number) => { const runStep = async (index: number) => {
console.log('Running step', index, Date.now())
if (index >= steps.length) { if (index >= steps.length) {
clearTimeout(timeoutTimer)
if (this.waitingScenario) { if (this.waitingScenario) {
setTimeout(() => {
this.waitingScenario = false this.waitingScenario = false
setTimeout(() => {
runStep(index) runStep(index)
}, 5000) }, 5000)
return return
} } else clearTimeout(timeoutTimer)
console.log('End step', Date.now())
this.isRunningScript = false this.isRunningScript = false
this.outputBuffer = '' this.outputBuffer = ''
appendLog( appendLog(
@ -397,12 +394,14 @@ export default class LineConnection {
username, username,
password, password,
number: this.config.lineNumber, number: this.config.lineNumber,
onData: (data: string) => { onData: (data: string, status: string) => {
this.config.output += data this.config.output += data
this.socketIO.emit('line_output', { this.socketIO.emit('apc_output', {
stationId: this.config.stationId, stationId: this.config.stationId,
lineId: this.config.id, lineId: this.config.id,
data: data, apcNumber: apcName === 'apc_1' ? 1 : 2,
data,
status,
}) })
appendLog( appendLog(
cleanData(data), cleanData(data),

View File

@ -7,6 +7,7 @@ import env from '#start/env'
import { CustomServer, CustomSocket } from '../app/ultils/types.js' import { CustomServer, CustomSocket } from '../app/ultils/types.js'
import Line from '#models/line' import Line from '#models/line'
import Station from '#models/station' import Station from '#models/station'
import APCController from '#services/apc_connection'
interface HandleOptions { interface HandleOptions {
command?: string command?: string
@ -62,6 +63,7 @@ export class WebSocketIo {
lineMap: Map<number, LineConnection> = new Map() // key = lineId lineMap: Map<number, LineConnection> = new Map() // key = lineId
lineConnecting: number[] = [] // key = lineId lineConnecting: number[] = [] // 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()
constructor(protected app: ApplicationService) {} constructor(protected app: ApplicationService) {}
@ -123,7 +125,7 @@ export class WebSocketIo {
stationId, stationId,
lineIds, lineIds,
async (line) => line.writeCommand(command), async (line) => line.writeCommand(command),
{ command, timeout: 180000 } { command, timeout: 120000 }
) )
}) })
@ -137,7 +139,7 @@ export class WebSocketIo {
async (line) => line.runScript(scenario), async (line) => line.runScript(scenario),
{ {
scenario, scenario,
timeout: scenario?.timeout ? Number(scenario.timeout) + 180000 : 300000, timeout: scenario?.timeout ? Number(scenario.timeout) + 120000 : 300000,
} }
) )
}) })
@ -201,7 +203,6 @@ export class WebSocketIo {
// Get file stats // Get file stats
const stats = fs.statSync(filePath) const stats = fs.statSync(filePath)
const fileSizeInBytes = stats.size const fileSizeInBytes = stats.size
console.log('File size (bytes):', fileSizeInBytes)
if (fileSizeInBytes / 1024 / 1024 > 0.5) { if (fileSizeInBytes / 1024 / 1024 > 0.5) {
// File is larger than 0.5 MB // File is larger than 0.5 MB
const fileId = Date.now() // Mã định danh file const fileId = Date.now() // Mã định danh file
@ -242,9 +243,17 @@ export class WebSocketIo {
stationId, stationId,
lineIds, lineIds,
async (line) => line.apcControl(action), async (line) => line.apcControl(action),
{ actionApc: action, timeout: 180000 } { actionApc: action, timeout: 120000 }
) )
}) })
socket.on('connect_apc', async (data) => {
const { apcIp, station, apcName } = data
if (this.apcsControl.has(apcIp)) {
return
}
await this.connectApc(io, apcName, station)
})
}) })
socketServer.listen(SOCKET_IO_PORT, () => { socketServer.listen(SOCKET_IO_PORT, () => {
@ -286,7 +295,7 @@ export class WebSocketIo {
} }
} }
private setTimeoutConnect = (lineId: number, lineConn: LineConnection, timeout = 180000) => { private setTimeoutConnect = (lineId: number, lineConn: LineConnection, timeout = 120000) => {
if (this.intervalMap[`${lineId}`]) { if (this.intervalMap[`${lineId}`]) {
clearInterval(this.intervalMap[`${lineId}`]) clearInterval(this.intervalMap[`${lineId}`])
delete this.intervalMap[`${lineId}`] delete this.intervalMap[`${lineId}`]
@ -351,4 +360,38 @@ export class WebSocketIo {
} }
} }
} }
private async connectApc(socket: any, apcName: string, station: Station) {
try {
const ip = (station as any)[`${apcName}_ip`] as string
const port = (station as any)[`${apcName}_port`] as number
const username = (station as any)[`${apcName}_username`] as string
const password = (station as any)[`${apcName}_password`] as string
if (!ip || !port || !username || !password)
throw new Error(`Missing APC configuration for ${apcName}`)
// Tạo APC Controller instance
const apc = new APCController({
host: ip,
port,
username,
password,
onData: (data: string, status: string) => {
socket.emit('apc_output', {
stationId: station.id,
apcNumber: apcName === 'apc_1' ? 1 : 2,
data,
status,
})
},
})
// Connect và login
await apc.connect()
await apc.login()
this.apcsControl.set(ip, apc)
} catch (error) {
console.log(error)
}
}
} }

View File

@ -14,8 +14,6 @@ import {
Grid, Grid,
ScrollArea, ScrollArea,
LoadingOverlay, LoadingOverlay,
Button,
Box,
} from "@mantine/core"; } from "@mantine/core";
import type { import type {
IDataTakeOver, IDataTakeOver,
@ -338,7 +336,7 @@ function App() {
}; };
}); });
}, },
[activeTab] []
); );
// const getLine = (lineId: number, stationId: number) => { // const getLine = (lineId: number, stationId: number) => {

View File

@ -270,7 +270,25 @@ export default function DraggableTabs({
fz={"sm"} fz={"sm"}
variant="filled" variant="filled"
onClick={() => { onClick={() => {
const station = tabs.find(
(el) => el.id.toString() === active
);
if (!station) return;
setOpenedAPC(true); setOpenedAPC(true);
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",
});
}
}} }}
> >
APC APC

View File

@ -1,14 +1,15 @@
import { Box, Button, Card, Checkbox, Drawer, Grid, Text } from "@mantine/core"; import { Box, Button, Card, Drawer, Grid, Text } from "@mantine/core";
import { IconRepeat, IconSection } from "@tabler/icons-react"; import { IconRepeat, IconSection } from "@tabler/icons-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import classes from "./Component.module.css"; import classes from "./Component.module.css";
import type { APCProps, SwitchPortsProps, TStation } from "../untils/types"; import type { APCProps, SwitchPortsProps, TStation } from "../untils/types";
import { useDebounce } from "../untils/helper"; import { useDebounce } from "../untils/helper";
import { SOCKET_EVENTS } from "../untils/constanst"; import { SOCKET_EVENTS } from "../untils/constanst";
import type { Socket } from "socket.io-client";
interface DrawerProps { interface DrawerProps {
stationAPI: TStation; stationAPI: TStation;
socket: any; socket: Socket | null;
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
openedSwitch?: () => void; openedSwitch?: () => void;
@ -37,7 +38,7 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
const findLineByOutlet = (outlet: TSelectedOutlet) => { const findLineByOutlet = (outlet: TSelectedOutlet) => {
return stationAPI.lines.find( return stationAPI.lines.find(
(line) => (line) =>
line.outlet === outlet.number && line.apc_name === `apc_${outlet.apc}` line.outlet === outlet.number && line.apcName === `apc_${outlet.apc}`
); );
}; };
@ -54,7 +55,7 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
if (!line) return el; if (!line) return el;
return { return {
...el, ...el,
name: "Line " + line.line_number || el.name, name: "Line " + line.lineNumber || el.name,
}; };
}) })
); );
@ -168,15 +169,25 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
}, [stationAPI, isConnected]); }, [stationAPI, isConnected]);
useEffect(() => { useEffect(() => {
socket?.on(SOCKET_EVENTS.APP_DATA.RECEIVED, (data: TStation[]) => { socket?.on("apc_output", (data) => {
const station = data?.find((el) => stationAPI.id === el.id); if (data.stationId !== stationAPI.id) return;
if (!station) return; let apcs: APCProps[] = [];
setDataStation(station); setDataStation((prev) => {
const apcs = const apc1 =
station?.apcs?.sort( data.apcNumber === 1
(a, b) => (a?.apc_number || 0) - (b?.apc_number || 0) ? { ...prev.apc1, output: data.data, status: data.status }
) || []; : prev.apc1;
const apc2 =
data.apcNumber === 2
? { ...prev.apc2, output: data.data, status: data.status }
: prev.apc2;
apcs = [apc1, apc2];
return prev.id === data.stationId
? { ...prev, apc1: apc1, apc2: apc2 }
: prev;
});
const outlets: TSelectedOutlet[] = []; const outlets: TSelectedOutlet[] = [];
console.log("apcs", apcs);
apcs.forEach(async (apc, i) => { apcs.forEach(async (apc, i) => {
const result: TSelectedOutlet[] = []; const result: TSelectedOutlet[] = [];
const lines = apc?.output?.split("\n") || []; const lines = apc?.output?.split("\n") || [];
@ -185,29 +196,6 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
}); });
setListOutlet(outlets); setListOutlet(outlets);
}); });
socket?.on(
SOCKET_EVENTS.DATA_APC_RECEIVED.DATA_APC_RECEIVED_TO_WEB,
(data: TStation[]) => {
const outlets: TSelectedOutlet[] = [];
data
?.filter((el) => stationAPI.id === el.id)
?.forEach((station) => {
station?.apcs
?.sort((a, b) => (a?.apc_number || 0) - (b?.apc_number || 0))
.forEach(async (apc, i) => {
const result: TSelectedOutlet[] = [];
const lines = apc?.output?.split("\n") || [];
await detectOutlet(apc, lines, result, i);
outlets.push(...result);
});
});
setListOutlet(outlets);
setDataStation(
data?.find((el) => stationAPI.id === el.id) || stationAPI
);
}
);
}, [socket]); }, [socket]);
const toggleSelect = (outlet: TSelectedOutlet, number: number) => { const toggleSelect = (outlet: TSelectedOutlet, number: number) => {
@ -270,7 +258,6 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
position="bottom" position="bottom"
> >
<Grid> <Grid>
{dataStation?.apcs && (
<Grid.Col span={6}> <Grid.Col span={6}>
<fieldset style={{ padding: "4px 6px" }}> <fieldset style={{ padding: "4px 6px" }}>
<legend> <legend>
@ -278,10 +265,10 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
<Text fw={700} c={"#514d4d"} fz={"sm"}> <Text fw={700} c={"#514d4d"} fz={"sm"}>
APC 1 APC 1
</Text> </Text>
{dataStation?.apcs[0]?.status && {dataStation?.apc1?.status &&
RenderAPCStatus(dataStation?.apcs[0])} RenderAPCStatus(dataStation?.apc1)}
{dataStation?.apcs[0]?.status === "DISCONNECTED" || {dataStation?.apc1?.status === "DISCONNECTED" ||
dataStation?.apcs[0]?.status === "TIMEOUT" ? ( dataStation?.apc1?.status === "TIMEOUT" ? (
<Button <Button
style={{ height: "24px" }} style={{ height: "24px" }}
size="xs" size="xs"
@ -346,12 +333,11 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
fw={500} fw={500}
fz={"14px"} fz={"14px"}
style={{ style={{
color: color: outlet.status === "ON" ? "#40c057" : "#f03e3e",
outlet.status === "ON" ? "#40c057" : "#f03e3e",
}} }}
> >
{findLineByOutlet(outlet) {findLineByOutlet(outlet)
? "Line " + findLineByOutlet(outlet)?.line_number ? "Line " + findLineByOutlet(outlet)?.lineNumber
: outlet.name} : outlet.name}
</Text> </Text>
</Card> </Card>
@ -381,8 +367,7 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
variant="filled" variant="filled"
onClick={() => { onClick={() => {
if ( if (
listOutletSelected.filter((el) => el.apc === 1) listOutletSelected.filter((el) => el.apc === 1).length ===
.length ===
listOutlet.filter((el) => el.apc === 1).length listOutlet.filter((el) => el.apc === 1).length
) { ) {
setListOutletSelected([]); setListOutletSelected([]);
@ -407,14 +392,13 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
listOutlet.filter((el) => el.apc === 1).length === 0 || listOutlet.filter((el) => el.apc === 1).length === 0 ||
listOutletSelected.filter((el) => el.apc === 1).length === listOutletSelected.filter((el) => el.apc === 1).length ===
0 || 0 ||
dataStation?.apcs[0]?.status === "DISCONNECTED" || dataStation?.apc1?.status === "DISCONNECTED" ||
dataStation?.apcs[0]?.status === "TIMEOUT" dataStation?.apc1?.status === "TIMEOUT"
} }
title={ title={
listOutletSelected.filter((el) => el.apc === 1).length === listOutletSelected.filter((el) => el.apc === 1).length ===
listOutlet.filter((el) => el.apc === 1).length || listOutlet.filter((el) => el.apc === 1).length ||
listOutletSelected.filter((el) => el.apc === 1).length === listOutletSelected.filter((el) => el.apc === 1).length === 0
0
? "Restart All" ? "Restart All"
: "Restart Selected" : "Restart Selected"
} }
@ -426,21 +410,17 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
color="yellow" color="yellow"
onClick={() => { onClick={() => {
if ( if (
listOutletSelected.filter((el) => el.apc === 1) listOutletSelected.filter((el) => el.apc === 1).length ===
.length === 0 || 0 ||
listOutletSelected.filter((el) => el.apc === 1) listOutletSelected.filter((el) => el.apc === 1).length ===
.length ===
listOutlet.filter((el) => el.apc === 1).length listOutlet.filter((el) => el.apc === 1).length
) { ) {
socket?.emit( socket?.emit(SOCKET_EVENTS.APC_CONTROL.FROM_WEB_ALL_APC, {
SOCKET_EVENTS.APC_CONTROL.FROM_WEB_ALL_APC,
{
apc: "apc_1", apc: "apc_1",
station: stationAPI, station: stationAPI,
action: "3", action: "3",
station_id: Number(stationAPI.id), station_id: Number(stationAPI.id),
} });
);
} else { } else {
listOutletSelected.forEach((el) => { listOutletSelected.forEach((el) => {
const line = findLineByOutlet(el); const line = findLineByOutlet(el);
@ -471,14 +451,13 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
listOutlet.filter((el) => el.apc === 1).length === 0 || listOutlet.filter((el) => el.apc === 1).length === 0 ||
listOutletSelected.filter((el) => el.apc === 1).length === listOutletSelected.filter((el) => el.apc === 1).length ===
0 || 0 ||
dataStation?.apcs[0]?.status === "DISCONNECTED" || dataStation?.apc1?.status === "DISCONNECTED" ||
dataStation?.apcs[0]?.status === "TIMEOUT" dataStation?.apc1?.status === "TIMEOUT"
} }
title={ title={
listOutletSelected.filter((el) => el.apc === 1).length === listOutletSelected.filter((el) => el.apc === 1).length ===
listOutlet.filter((el) => el.apc === 1).length || listOutlet.filter((el) => el.apc === 1).length ||
listOutletSelected.filter((el) => el.apc === 1).length === listOutletSelected.filter((el) => el.apc === 1).length === 0
0
? "Turn On All" ? "Turn On All"
: "Turn On Selected" : "Turn On Selected"
} }
@ -490,21 +469,17 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
color="green" color="green"
onClick={() => { onClick={() => {
if ( if (
listOutletSelected.filter((el) => el.apc === 1) listOutletSelected.filter((el) => el.apc === 1).length ===
.length === 0 || 0 ||
listOutletSelected.filter((el) => el.apc === 1) listOutletSelected.filter((el) => el.apc === 1).length ===
.length ===
listOutlet.filter((el) => el.apc === 1).length listOutlet.filter((el) => el.apc === 1).length
) { ) {
socket?.emit( socket?.emit(SOCKET_EVENTS.APC_CONTROL.FROM_WEB_ALL_APC, {
SOCKET_EVENTS.APC_CONTROL.FROM_WEB_ALL_APC,
{
apc: "apc_1", apc: "apc_1",
station: stationAPI, station: stationAPI,
action: "1", action: "1",
station_id: Number(stationAPI.id), station_id: Number(stationAPI.id),
} });
);
} else { } else {
listOutletSelected.forEach((el) => { listOutletSelected.forEach((el) => {
const line = findLineByOutlet(el); const line = findLineByOutlet(el);
@ -535,14 +510,13 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
listOutlet.filter((el) => el.apc === 1).length === 0 || listOutlet.filter((el) => el.apc === 1).length === 0 ||
listOutletSelected.filter((el) => el.apc === 1).length === listOutletSelected.filter((el) => el.apc === 1).length ===
0 || 0 ||
dataStation?.apcs[0]?.status === "DISCONNECTED" || dataStation?.apc1?.status === "DISCONNECTED" ||
dataStation?.apcs[0]?.status === "TIMEOUT" dataStation?.apc1?.status === "TIMEOUT"
} }
title={ title={
listOutletSelected.filter((el) => el.apc === 1).length === listOutletSelected.filter((el) => el.apc === 1).length ===
listOutlet.filter((el) => el.apc === 1).length || listOutlet.filter((el) => el.apc === 1).length ||
listOutletSelected.filter((el) => el.apc === 1).length === listOutletSelected.filter((el) => el.apc === 1).length === 0
0
? "Turn Off All" ? "Turn Off All"
: "Turn Off Selected" : "Turn Off Selected"
} }
@ -554,21 +528,17 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
color="red" color="red"
onClick={() => { onClick={() => {
if ( if (
listOutletSelected.filter((el) => el.apc === 1) listOutletSelected.filter((el) => el.apc === 1).length ===
.length === 0 || 0 ||
listOutletSelected.filter((el) => el.apc === 1) listOutletSelected.filter((el) => el.apc === 1).length ===
.length ===
listOutlet.filter((el) => el.apc === 1).length listOutlet.filter((el) => el.apc === 1).length
) { ) {
socket?.emit( socket?.emit(SOCKET_EVENTS.APC_CONTROL.FROM_WEB_ALL_APC, {
SOCKET_EVENTS.APC_CONTROL.FROM_WEB_ALL_APC,
{
apc: "apc_1", apc: "apc_1",
station: stationAPI, station: stationAPI,
action: "2", action: "2",
station_id: Number(stationAPI.id), station_id: Number(stationAPI.id),
} });
);
} else { } else {
listOutletSelected.forEach((el) => { listOutletSelected.forEach((el) => {
const line = findLineByOutlet(el); const line = findLineByOutlet(el);
@ -597,9 +567,6 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
</Box> </Box>
</fieldset> </fieldset>
</Grid.Col> </Grid.Col>
)}
{dataStation?.apcs && (
<Grid.Col span={6}> <Grid.Col span={6}>
<fieldset style={{ padding: "4px 6px" }}> <fieldset style={{ padding: "4px 6px" }}>
<legend> <legend>
@ -607,10 +574,10 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
<Text fw={700} c={"#514d4d"} fz={"sm"}> <Text fw={700} c={"#514d4d"} fz={"sm"}>
APC 2 APC 2
</Text> </Text>
{dataStation?.apcs[1]?.status && {dataStation?.apc2?.status &&
RenderAPCStatus(dataStation?.apcs[1])} RenderAPCStatus(dataStation?.apc2)}
{dataStation?.apcs[1]?.status === "DISCONNECTED" || {dataStation?.apc2?.status === "DISCONNECTED" ||
dataStation?.apcs[1]?.status === "TIMEOUT" ? ( dataStation?.apc2?.status === "TIMEOUT" ? (
<Button <Button
style={{ height: "24px" }} style={{ height: "24px" }}
size="xs" size="xs"
@ -674,12 +641,11 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
fw={500} fw={500}
fz={"14px"} fz={"14px"}
style={{ style={{
color: color: outlet.status === "ON" ? "#40c057" : "#f03e3e",
outlet.status === "ON" ? "#40c057" : "#f03e3e",
}} }}
> >
{findLineByOutlet(outlet) {findLineByOutlet(outlet)
? "Line " + findLineByOutlet(outlet)?.line_number ? "Line " + findLineByOutlet(outlet)?.lineNumber
: outlet.name} : outlet.name}
</Text> </Text>
</Card> </Card>
@ -709,8 +675,7 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
variant="filled" variant="filled"
onClick={() => { onClick={() => {
if ( if (
listOutletSelected.filter((el) => el.apc === 2) listOutletSelected.filter((el) => el.apc === 2).length ===
.length ===
listOutlet.filter((el) => el.apc === 2).length listOutlet.filter((el) => el.apc === 2).length
) { ) {
setListOutletSelected([]); setListOutletSelected([]);
@ -735,14 +700,13 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
listOutlet.filter((el) => el.apc === 2).length === 0 || listOutlet.filter((el) => el.apc === 2).length === 0 ||
listOutletSelected.filter((el) => el.apc === 2).length === listOutletSelected.filter((el) => el.apc === 2).length ===
0 || 0 ||
dataStation?.apcs[1]?.status === "DISCONNECTED" || dataStation?.apc2?.status === "DISCONNECTED" ||
dataStation?.apcs[1]?.status === "TIMEOUT" dataStation?.apc2?.status === "TIMEOUT"
} }
title={ title={
listOutletSelected.filter((el) => el.apc === 2).length === listOutletSelected.filter((el) => el.apc === 2).length ===
listOutlet.filter((el) => el.apc === 2).length || listOutlet.filter((el) => el.apc === 2).length ||
listOutletSelected.filter((el) => el.apc === 2).length === listOutletSelected.filter((el) => el.apc === 2).length === 0
0
? "Restart All" ? "Restart All"
: "Restart Selected" : "Restart Selected"
} }
@ -754,21 +718,17 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
color="yellow" color="yellow"
onClick={() => { onClick={() => {
if ( if (
listOutletSelected.filter((el) => el.apc === 2) listOutletSelected.filter((el) => el.apc === 2).length ===
.length === 0 || 0 ||
listOutletSelected.filter((el) => el.apc === 2) listOutletSelected.filter((el) => el.apc === 2).length ===
.length ===
listOutlet.filter((el) => el.apc === 2).length listOutlet.filter((el) => el.apc === 2).length
) { ) {
socket?.emit( socket?.emit(SOCKET_EVENTS.APC_CONTROL.FROM_WEB_ALL_APC, {
SOCKET_EVENTS.APC_CONTROL.FROM_WEB_ALL_APC,
{
apc: "apc_2", apc: "apc_2",
station: stationAPI, station: stationAPI,
action: "3", action: "3",
station_id: Number(stationAPI.id), station_id: Number(stationAPI.id),
} });
);
} else { } else {
listOutletSelected.forEach((el) => { listOutletSelected.forEach((el) => {
const line = findLineByOutlet(el); const line = findLineByOutlet(el);
@ -787,8 +747,7 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
}, 5000); }, 5000);
}} }}
> >
{listOutletSelected.filter((el) => el.apc === 2).length === {listOutletSelected.filter((el) => el.apc === 2).length === 0
0
? "Restart All" ? "Restart All"
: "Restart Selected"} : "Restart Selected"}
</Button> </Button>
@ -798,16 +757,16 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
listOutlet.filter((el) => el.apc === 2).length === 0 || listOutlet.filter((el) => el.apc === 2).length === 0 ||
listOutletSelected.filter((el) => el.apc === 2).length === listOutletSelected.filter((el) => el.apc === 2).length ===
0 || 0 ||
dataStation?.apcs[1]?.status === "DISCONNECTED" || dataStation?.apc2?.status === "DISCONNECTED" ||
dataStation?.apcs[1]?.status === "TIMEOUT" dataStation?.apc2?.status === "TIMEOUT"
} }
title={ title={
listOutletSelected.filter((el) => el.apc === 2).length === listOutletSelected.filter((el) => el.apc === 2).length ===
listOutlet.filter((el) => el.apc === 2).length || listOutlet.filter((el) => el.apc === 2).length ||
listOutletSelected.filter((el) => el.apc === 2).length === listOutletSelected.filter((el) => el.apc === 2).length ===
0 || 0 ||
dataStation?.apcs[1]?.status === "DISCONNECTED" || dataStation?.apc2?.status === "DISCONNECTED" ||
dataStation?.apcs[1]?.status === "TIMEOUT" dataStation?.apc2?.status === "TIMEOUT"
? "Turn On All" ? "Turn On All"
: "Turn On Selected" : "Turn On Selected"
} }
@ -819,21 +778,17 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
color="green" color="green"
onClick={() => { onClick={() => {
if ( if (
listOutletSelected.filter((el) => el.apc === 2) listOutletSelected.filter((el) => el.apc === 2).length ===
.length === 0 || 0 ||
listOutletSelected.filter((el) => el.apc === 2) listOutletSelected.filter((el) => el.apc === 2).length ===
.length ===
listOutlet.filter((el) => el.apc === 2).length listOutlet.filter((el) => el.apc === 2).length
) { ) {
socket?.emit( socket?.emit(SOCKET_EVENTS.APC_CONTROL.FROM_WEB_ALL_APC, {
SOCKET_EVENTS.APC_CONTROL.FROM_WEB_ALL_APC,
{
apc: "apc_2", apc: "apc_2",
station: stationAPI, station: stationAPI,
action: "1", action: "1",
station_id: Number(stationAPI.id), station_id: Number(stationAPI.id),
} });
);
} else { } else {
listOutletSelected.forEach((el) => { listOutletSelected.forEach((el) => {
const line = findLineByOutlet(el); const line = findLineByOutlet(el);
@ -864,14 +819,13 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
listOutlet.filter((el) => el.apc === 2).length === 0 || listOutlet.filter((el) => el.apc === 2).length === 0 ||
listOutletSelected.filter((el) => el.apc === 2).length === listOutletSelected.filter((el) => el.apc === 2).length ===
0 || 0 ||
dataStation?.apcs[1]?.status === "DISCONNECTED" || dataStation?.apc2?.status === "DISCONNECTED" ||
dataStation?.apcs[1]?.status === "TIMEOUT" dataStation?.apc2?.status === "TIMEOUT"
} }
title={ title={
listOutletSelected.filter((el) => el.apc === 2).length === listOutletSelected.filter((el) => el.apc === 2).length ===
listOutlet.filter((el) => el.apc === 2).length || listOutlet.filter((el) => el.apc === 2).length ||
listOutletSelected.filter((el) => el.apc === 2).length === listOutletSelected.filter((el) => el.apc === 2).length === 0
0
? "Turn Off All" ? "Turn Off All"
: "Turn Off Selected" : "Turn Off Selected"
} }
@ -883,21 +837,17 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
color="red" color="red"
onClick={() => { onClick={() => {
if ( if (
listOutletSelected.filter((el) => el.apc === 2) listOutletSelected.filter((el) => el.apc === 2).length ===
.length === 0 || 0 ||
listOutletSelected.filter((el) => el.apc === 2) listOutletSelected.filter((el) => el.apc === 2).length ===
.length ===
listOutlet.filter((el) => el.apc === 2).length listOutlet.filter((el) => el.apc === 2).length
) { ) {
socket?.emit( socket?.emit(SOCKET_EVENTS.APC_CONTROL.FROM_WEB_ALL_APC, {
SOCKET_EVENTS.APC_CONTROL.FROM_WEB_ALL_APC,
{
apc: "apc_2", apc: "apc_2",
station: stationAPI, station: stationAPI,
action: "2", action: "2",
station_id: Number(stationAPI.id), station_id: Number(stationAPI.id),
} });
);
} else { } else {
listOutletSelected.forEach((el) => { listOutletSelected.forEach((el) => {
const line = findLineByOutlet(el); const line = findLineByOutlet(el);
@ -926,7 +876,6 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
</Box> </Box>
</fieldset> </fieldset>
</Grid.Col> </Grid.Col>
)}
</Grid> </Grid>
<div <div
style={{ display: "flex", justifyContent: "end", marginTop: "20px" }} style={{ display: "flex", justifyContent: "end", marginTop: "20px" }}

View File

@ -45,7 +45,9 @@ export type TStation = {
owner_id: number; owner_id: number;
owner: TUser; owner: TUser;
users: TUser[]; users: TUser[];
apcs?: APCProps[]; apcs: APCProps[];
apc1: APCProps;
apc2: APCProps;
master_control?: boolean; master_control?: boolean;
switch_control_ip?: string; // Optional switch_control_ip?: string; // Optional
switch_control_port?: number; // Optional switch_control_port?: number; // Optional