Add baud and interface to lines, enhance line controls
Added 'baud' and 'interface' fields to the Line model and database schema. Implemented backend and frontend support for setting baud rate per line, including socket event handling and UI controls. Enhanced CardLine and BottomToolBar components to provide scenario and baud controls, and improved UI/UX for line management. Minor fixes and refactoring for consistency and usability.
This commit is contained in:
parent
31036ff7da
commit
8a06650eab
|
|
@ -26,6 +26,12 @@ export default class Line extends BaseModel {
|
|||
@column()
|
||||
declare outlet: number
|
||||
|
||||
@column()
|
||||
declare interface: string
|
||||
|
||||
@column()
|
||||
declare baud: number
|
||||
|
||||
@belongsTo(() => Station)
|
||||
declare station: BelongsTo<typeof Station>
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ class APCController {
|
|||
this.buffer = ''
|
||||
}
|
||||
}
|
||||
appendLog(data, 0, 0, this.apc_number || 0)
|
||||
// appendLog(data, 0, 0, this.apc_number || 0)
|
||||
}
|
||||
|
||||
private _handleClose(): void {
|
||||
|
|
|
|||
|
|
@ -200,8 +200,10 @@ export default class LineConnection {
|
|||
if (char === '\x7F') this.bufferCommand = this.bufferCommand.slice(0, -1)
|
||||
else if (char === '\r' && cleanData(this.bufferCommand).length > 0) {
|
||||
this.config.commands = [
|
||||
cleanData(this.bufferCommand),
|
||||
...this.config.commands.filter((el) => el !== cleanData(this.bufferCommand)),
|
||||
cleanData(this.bufferCommand.replace('\r', '')),
|
||||
...this.config.commands.filter(
|
||||
(el) => el !== cleanData(this.bufferCommand.replace('\r', ''))
|
||||
),
|
||||
].slice(0, 10)
|
||||
this.bufferCommand = ''
|
||||
} else this.bufferCommand += char
|
||||
|
|
@ -516,6 +518,7 @@ export default class LineConnection {
|
|||
|
||||
// Gửi nhiều ký tự ESC để vào ROMMON
|
||||
breakSpam() {
|
||||
console.log('SPAM Break to line:', this.config.lineNumber)
|
||||
let count = 0
|
||||
const escInterval = setInterval(() => {
|
||||
if (count >= 100) {
|
||||
|
|
@ -526,4 +529,16 @@ export default class LineConnection {
|
|||
count++
|
||||
}, 1)
|
||||
}
|
||||
|
||||
async setBaud(baud: number) {
|
||||
this.writeCommand('enable\r\n')
|
||||
await sleep(500)
|
||||
this.writeCommand('line console 0\r\n')
|
||||
await sleep(500)
|
||||
this.writeCommand(`speed ${baud}\r\n`)
|
||||
await sleep(500)
|
||||
this.writeCommand('end\r\n')
|
||||
await sleep(500)
|
||||
this.writeCommand('write memory\r\n')
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
import { BaseSchema } from '@adonisjs/lucid/schema'
|
||||
|
||||
export default class extends BaseSchema {
|
||||
protected tableName = 'lines'
|
||||
|
||||
async up() {
|
||||
this.schema.alterTable(this.tableName, (table) => {
|
||||
table.string('interface').defaultTo('').nullable()
|
||||
table.integer('baud').nullable()
|
||||
})
|
||||
}
|
||||
|
||||
async down() {
|
||||
this.schema.alterTable(this.tableName, (table) => {
|
||||
table.dropColumn('interface')
|
||||
table.dropColumn('baud')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ interface HandleOptions {
|
|||
actionApc?: string
|
||||
scenario?: any
|
||||
timeout?: number
|
||||
baud?: number
|
||||
}
|
||||
|
||||
type LineAction = (line: LineConnection, options?: HandleOptions) => Promise<void | unknown> | void
|
||||
|
|
@ -166,6 +167,26 @@ export class WebSocketIo {
|
|||
)
|
||||
})
|
||||
|
||||
socket.on('set_baud', async (data) => {
|
||||
const lineId = data.lineId
|
||||
const baud = data.baud
|
||||
const line = await Line.find(lineId)
|
||||
if (line) {
|
||||
Object.assign(line, { baud })
|
||||
line?.save()
|
||||
}
|
||||
await this.handleLineOperation(
|
||||
io,
|
||||
data.stationId,
|
||||
[lineId],
|
||||
async (value) => value.setBaud(baud),
|
||||
{
|
||||
baud,
|
||||
timeout: 120000,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
socket.on('open_cli', async (data) => {
|
||||
const { lineId, userEmail, userName: name, stationId } = data
|
||||
const line = this.lineMap.get(lineId)
|
||||
|
|
|
|||
|
|
@ -112,10 +112,15 @@ function App() {
|
|||
const response = await axios.get(apiUrl + "api/stations");
|
||||
if (response.status) {
|
||||
if (Array.isArray(response.data)) {
|
||||
setStations(response.data);
|
||||
response.data.forEach((station) => {
|
||||
connectApcSwitch(station);
|
||||
});
|
||||
setStations(
|
||||
response.data.map((station) => {
|
||||
connectApcSwitch(station);
|
||||
const lines = (station?.lines || []).sort(
|
||||
(a: TLine, b: TLine) => a?.lineNumber - b?.lineNumber
|
||||
);
|
||||
return { ...station, lines };
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -441,6 +446,7 @@ function App() {
|
|||
loadingTerminal &&
|
||||
Number(station.id) === Number(activeTab)
|
||||
}
|
||||
scenarios={scenarios}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
|
|
@ -462,6 +468,7 @@ function App() {
|
|||
loadingTerminal &&
|
||||
Number(station.id) === Number(activeTab)
|
||||
}
|
||||
scenarios={scenarios}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
|
|
@ -483,6 +490,7 @@ function App() {
|
|||
loadingTerminal &&
|
||||
Number(station.id) === Number(activeTab)
|
||||
}
|
||||
scenarios={scenarios}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
|
|
@ -505,6 +513,7 @@ function App() {
|
|||
isLogModalOpen={isLogModalOpen}
|
||||
setIsLogModalOpen={setIsLogModalOpen}
|
||||
setTestLogContent={setTestLogContent}
|
||||
scenarios={scenarios}
|
||||
/>
|
||||
</Flex>
|
||||
</Tabs.Panel>
|
||||
|
|
|
|||
|
|
@ -4,29 +4,32 @@ import {
|
|||
CloseButton,
|
||||
Flex,
|
||||
Input,
|
||||
Menu,
|
||||
ScrollArea,
|
||||
Tabs,
|
||||
Text,
|
||||
} from "@mantine/core";
|
||||
import { useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import classes from "./Component.module.css";
|
||||
import type { TLine, TStation } from "../untils/types";
|
||||
import type { IScenario, TLine, TStation } from "../untils/types";
|
||||
import type { Socket } from "socket.io-client";
|
||||
import { ButtonDPELP, ButtonSelect } from "./ButtonAction";
|
||||
import { ButtonDPELP, ButtonScenario, ButtonSelect } from "./ButtonAction";
|
||||
import DrawerLogs from "./DrawerLogs";
|
||||
import { DrawerAPCControl, DrawerSwitchControl } from "./DrawerControl";
|
||||
import { isJsonString } from "../untils/helper";
|
||||
|
||||
interface TabsProps {
|
||||
selectedLines: TLine[];
|
||||
socket: Socket | null;
|
||||
setSelectedLines: (lines: React.SetStateAction<TLine[]>) => void;
|
||||
setSelectedLines: (value: React.SetStateAction<TLine[]>) => void;
|
||||
isDisable: boolean;
|
||||
station: TStation;
|
||||
setIsDisable: (lines: React.SetStateAction<boolean>) => void;
|
||||
setIsDisable: (value: React.SetStateAction<boolean>) => void;
|
||||
testLogContent: string;
|
||||
isLogModalOpen: boolean;
|
||||
setIsLogModalOpen: (lines: React.SetStateAction<boolean>) => void;
|
||||
setTestLogContent: (lines: React.SetStateAction<string>) => void;
|
||||
setIsLogModalOpen: (value: React.SetStateAction<boolean>) => void;
|
||||
setTestLogContent: (value: React.SetStateAction<string>) => void;
|
||||
scenarios: IScenario[];
|
||||
}
|
||||
|
||||
const BottomToolBar = ({
|
||||
|
|
@ -40,7 +43,14 @@ const BottomToolBar = ({
|
|||
isLogModalOpen,
|
||||
setIsLogModalOpen,
|
||||
setTestLogContent,
|
||||
scenarios,
|
||||
}: TabsProps) => {
|
||||
const user = useMemo(() => {
|
||||
return localStorage.getItem("user") &&
|
||||
isJsonString(localStorage.getItem("user"))
|
||||
? JSON.parse(localStorage.getItem("user") || "")
|
||||
: null;
|
||||
}, []);
|
||||
const [valueInput, setValueInput] = useState<string>("");
|
||||
const [activeTabBottom, setActiveBottom] = useState<string>("command");
|
||||
|
||||
|
|
@ -129,7 +139,7 @@ const BottomToolBar = ({
|
|||
socket?.emit("write_command_line_from_web", {
|
||||
lineIds: listLine.map((line) => line.id),
|
||||
stationId: station.id,
|
||||
command: " \n",
|
||||
command: "spam_break",
|
||||
});
|
||||
setIsDisable(true);
|
||||
setTimeout(() => {
|
||||
|
|
@ -207,53 +217,58 @@ const BottomToolBar = ({
|
|||
}, 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> */}
|
||||
<Menu shadow="md" position="top">
|
||||
<Menu.Target>
|
||||
<Button
|
||||
disabled={isDisable || selectedLines.length === 0}
|
||||
variant="filled"
|
||||
color="yellow"
|
||||
style={{ height: "30px", width: "100px" }}
|
||||
onClick={() => {}}
|
||||
>
|
||||
Scenario
|
||||
</Button>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Box
|
||||
px="xs"
|
||||
py="sm"
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
gap: "12px",
|
||||
}}
|
||||
>
|
||||
{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}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
<DrawerLogs
|
||||
socket={socket}
|
||||
isLogModalOpen={isLogModalOpen}
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ export const ButtonScenario = ({
|
|||
}: {
|
||||
socket: Socket | null;
|
||||
isDisable: boolean;
|
||||
onClick: () => void;
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
selectedLines: TLine[];
|
||||
scenario: IScenario;
|
||||
}) => {
|
||||
|
|
@ -146,8 +146,8 @@ export const ButtonScenario = ({
|
|||
variant="outline"
|
||||
color="#00a164"
|
||||
className={classes.buttonScenario}
|
||||
onClick={async () => {
|
||||
onClick();
|
||||
onClick={async (e) => {
|
||||
onClick(e);
|
||||
selectedLines?.forEach((el) => {
|
||||
socket?.emit(
|
||||
"run_scenario",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { Card, Text, Box, Flex } from "@mantine/core";
|
||||
import type { TLine, TStation } from "../untils/types";
|
||||
import { Card, Text, Box, Flex, Menu, Button, Input } from "@mantine/core";
|
||||
import type { IScenario, TLine, TStation } from "../untils/types";
|
||||
import classes from "./Component.module.css";
|
||||
import TerminalCLI from "./TerminalXTerm";
|
||||
import type { Socket } from "socket.io-client";
|
||||
import { IconCircleCheckFilled } from "@tabler/icons-react";
|
||||
import { memo, useMemo } from "react";
|
||||
import { memo, useMemo, useState } from "react";
|
||||
import { convertTimestampToDate } from "../untils/helper";
|
||||
import { ButtonDPELP, ButtonScenario } from "./ButtonAction";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { listBaudDefault } from "../untils/constanst";
|
||||
|
||||
const CardLine = ({
|
||||
line,
|
||||
|
|
@ -15,6 +17,7 @@ const CardLine = ({
|
|||
stationItem,
|
||||
openTerminal,
|
||||
loadTerminal,
|
||||
scenarios,
|
||||
}: {
|
||||
line: TLine;
|
||||
selectedLines: TLine[];
|
||||
|
|
@ -23,6 +26,7 @@ const CardLine = ({
|
|||
stationItem: TStation;
|
||||
openTerminal: (value: TLine) => void;
|
||||
loadTerminal: boolean;
|
||||
scenarios: IScenario[];
|
||||
}) => {
|
||||
const user = useMemo(() => {
|
||||
return localStorage.getItem("user") &&
|
||||
|
|
@ -30,7 +34,51 @@ const CardLine = ({
|
|||
? JSON.parse(localStorage.getItem("user") || "")
|
||||
: null;
|
||||
}, []);
|
||||
const [showMenu, setShowMenu] = useState<boolean>(false);
|
||||
const [isDisabled, setIsDisabled] = useState<boolean>(false);
|
||||
const [valueBaud, setValueBaud] = useState<string>("");
|
||||
|
||||
const controlApc = (action: string) => {
|
||||
if (!line.outlet) {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
message: "Hasn't config outlet number",
|
||||
color: "red",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const apcName = line.apcName || line.apc_name;
|
||||
if (!apcName) {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
message: "Hasn't config apc",
|
||||
color: "red",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (
|
||||
(apcName === "apc_1" && !stationItem.apc_1_ip) ||
|
||||
(apcName === "apc_2" && !stationItem.apc_2_ip)
|
||||
) {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
message: "Hasn't config apc ip",
|
||||
color: "red",
|
||||
});
|
||||
return;
|
||||
}
|
||||
socket?.emit("control_apc", {
|
||||
outletNumbers: [line.outlet],
|
||||
station: stationItem,
|
||||
action: action,
|
||||
apcName: line.apcName || line.apc_name,
|
||||
});
|
||||
setIsDisabled(true);
|
||||
setTimeout(() => {
|
||||
setIsDisabled(false);
|
||||
}, 5000);
|
||||
};
|
||||
console.log("RERENDER", line.lineNumber);
|
||||
return (
|
||||
<Card
|
||||
key={line.id}
|
||||
|
|
@ -55,6 +103,7 @@ const CardLine = ({
|
|||
setSelectedLines(selectedLines.filter((val) => val.id !== line.id));
|
||||
else setSelectedLines((pre) => [...pre, line]);
|
||||
}}
|
||||
onMouseLeave={() => setTimeout(() => setShowMenu(false), 150)}
|
||||
>
|
||||
<Flex
|
||||
justify={"space-between"}
|
||||
|
|
@ -62,54 +111,286 @@ const CardLine = ({
|
|||
// gap={"md"}
|
||||
// align={"center"}
|
||||
>
|
||||
<Flex justify={"space-between"}>
|
||||
<Text
|
||||
fw={600}
|
||||
style={{ display: "flex", gap: "4px", fontSize: "15px" }}
|
||||
>
|
||||
Line: {line.lineNumber || line.line_number} - {line.port}{" "}
|
||||
{line.status === "connected" && (
|
||||
<IconCircleCheckFilled color="green" />
|
||||
)}
|
||||
</Text>
|
||||
<div
|
||||
style={{
|
||||
alignItems: "center",
|
||||
marginLeft: "16px",
|
||||
fontSize: "12px",
|
||||
color: "red",
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
{line?.userOpenCLI ? line?.userOpenCLI + " is using" : ""}
|
||||
</div>
|
||||
</Flex>
|
||||
<Flex justify={"space-between"}>
|
||||
<div className={classes.info_line}>
|
||||
PID:{" "}
|
||||
<Text className={classes.info_line} fs={"italic"}>
|
||||
{line?.inventory?.pid || ""}
|
||||
</Text>
|
||||
</div>
|
||||
<div className={classes.info_line}>
|
||||
SN:{" "}
|
||||
<Text className={classes.info_line} fs={"italic"}>
|
||||
{line?.inventory?.sn || ""}
|
||||
</Text>
|
||||
</div>
|
||||
<div className={classes.info_line} style={{ minWidth: "50px" }}>
|
||||
VID:{" "}
|
||||
<Text className={classes.info_line} fs={"italic"}>
|
||||
{line?.inventory?.vid || ""}
|
||||
</Text>
|
||||
</div>
|
||||
</Flex>
|
||||
<Menu
|
||||
shadow="md"
|
||||
position="right-start"
|
||||
transitionProps={{ transition: "pop-top-right" }}
|
||||
opened={showMenu}
|
||||
onChange={setShowMenu}
|
||||
withArrow
|
||||
arrowSize={8}
|
||||
>
|
||||
<Menu.Target>
|
||||
<Flex
|
||||
justify={"space-between"}
|
||||
direction={"column"}
|
||||
// className={classes.topBarLine}
|
||||
onMouseEnter={() => setShowMenu(true)}
|
||||
>
|
||||
<Flex gap={"8px"}>
|
||||
<Flex direction={"column"} justify={"center"} align={"center"}>
|
||||
<Box>
|
||||
<Text
|
||||
fw={600}
|
||||
style={{
|
||||
fontSize: "24px",
|
||||
lineHeight: "normal",
|
||||
// color: line.status === "connected" ? "green" : "dark",
|
||||
border: "1px solid #ccc",
|
||||
borderRadius: "12px",
|
||||
paddingLeft: "4px",
|
||||
paddingRight: "4px",
|
||||
backgroundColor:
|
||||
line.status === "connected" ? "#4cc64c" : "",
|
||||
}}
|
||||
>
|
||||
{line.lineNumber || line.line_number}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text
|
||||
fw={500}
|
||||
style={{ fontSize: "12px", color: "#767676" }}
|
||||
>
|
||||
{line.port}
|
||||
</Text>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box w={"100%"}>
|
||||
<Flex justify={"space-between"} w={"100%"}>
|
||||
<div className={classes.info_line}>
|
||||
PID:{" "}
|
||||
<Text className={classes.info_line} fs={"italic"}>
|
||||
{line?.inventory?.pid || ""}
|
||||
</Text>
|
||||
{line?.inventory?.vid ? (
|
||||
<Text className={classes.info_line} fs={"italic"}>
|
||||
{" | " + line?.inventory?.vid}
|
||||
</Text>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={classes.info_line}
|
||||
style={{ width: "120px" }}
|
||||
>
|
||||
SN:{" "}
|
||||
<Text className={classes.info_line} fs={"italic"}>
|
||||
{line?.inventory?.sn || ""}
|
||||
</Text>
|
||||
</div>
|
||||
</Flex>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "red",
|
||||
}}
|
||||
>
|
||||
{line?.userOpenCLI ? line?.userOpenCLI + " is using" : ""}
|
||||
</div>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown style={{ width: "110px", backgroundColor: "#2d2d2d" }}>
|
||||
<Flex
|
||||
justify={"space-between"}
|
||||
direction={"column"}
|
||||
style={{
|
||||
gap: "8px",
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
// onMouseEnter={() => setShowMenu(true)}
|
||||
// onMouseLeave={() => setTimeout(() => setShowMenu(false), 300)}
|
||||
>
|
||||
<Button
|
||||
disabled={isDisabled}
|
||||
variant="filled"
|
||||
color="orange"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
socket?.emit("write_command_line_from_web", {
|
||||
lineIds: [line.id],
|
||||
stationId: Number(stationItem.id),
|
||||
command: "spam_break",
|
||||
});
|
||||
setIsDisabled(true);
|
||||
setTimeout(() => {
|
||||
setIsDisabled(false);
|
||||
}, 5000);
|
||||
}}
|
||||
>
|
||||
Send Break
|
||||
</Button>
|
||||
<ButtonDPELP
|
||||
socket={socket}
|
||||
selectedLines={[line]}
|
||||
isDisable={isDisabled}
|
||||
onClick={() => {
|
||||
setIsDisabled(true);
|
||||
setTimeout(() => {
|
||||
setIsDisabled(false);
|
||||
}, 5000);
|
||||
}}
|
||||
/>
|
||||
<Menu shadow="md" position="right">
|
||||
<Menu.Target>
|
||||
<Button
|
||||
disabled={isDisabled}
|
||||
variant="filled"
|
||||
color="yellow"
|
||||
style={{ height: "30px", width: "100px" }}
|
||||
onClick={() => {}}
|
||||
>
|
||||
Scenario
|
||||
</Button>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown style={{ width: "130px" }}>
|
||||
<Flex
|
||||
justify={"space-between"}
|
||||
direction={"column"}
|
||||
style={{
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
{scenarios.map((el, i) => (
|
||||
<ButtonScenario
|
||||
key={i}
|
||||
socket={socket}
|
||||
selectedLines={[line]}
|
||||
isDisable={isDisabled}
|
||||
onClick={() => {
|
||||
setShowMenu(true);
|
||||
setIsDisabled(true);
|
||||
setTimeout(() => {
|
||||
setIsDisabled(false);
|
||||
}, 5000);
|
||||
}}
|
||||
scenario={el}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
<Menu shadow="md" position="right">
|
||||
<Menu.Target>
|
||||
<Button
|
||||
disabled={isDisabled}
|
||||
variant="filled"
|
||||
size="xs"
|
||||
onClick={() => {}}
|
||||
>
|
||||
BAUD
|
||||
</Button>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown style={{ width: "130px" }}>
|
||||
<Flex
|
||||
justify={"space-between"}
|
||||
direction={"column"}
|
||||
style={{
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
{listBaudDefault.map((el, i) => (
|
||||
<Button
|
||||
key={i}
|
||||
disabled={isDisabled}
|
||||
variant="outline"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
setShowMenu(true);
|
||||
socket?.emit("set_baud", {
|
||||
lineId: line.id,
|
||||
baud: el,
|
||||
});
|
||||
setIsDisabled(true);
|
||||
setTimeout(() => {
|
||||
setIsDisabled(false);
|
||||
}, 5000);
|
||||
}}
|
||||
>
|
||||
{el}
|
||||
</Button>
|
||||
))}
|
||||
<Input
|
||||
placeholder="Custom"
|
||||
value={valueBaud}
|
||||
onChange={(e) => setValueBaud(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
socket?.emit("set_baud", {
|
||||
lineId: line.id,
|
||||
baud: Number(valueBaud),
|
||||
});
|
||||
setValueBaud("");
|
||||
setIsDisabled(true);
|
||||
setTimeout(() => {
|
||||
setIsDisabled(false);
|
||||
}, 5000);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
<Flex justify={"space-between"} direction={"column"}>
|
||||
<hr style={{ width: "100%" }} />
|
||||
<Box style={{ textAlign: "center" }}>
|
||||
<Text p={0} style={{ fontSize: "13px" }} c={"white"}>
|
||||
Power
|
||||
</Text>
|
||||
</Box>
|
||||
<Button
|
||||
mt={"8px"}
|
||||
disabled={isDisabled}
|
||||
variant="outline"
|
||||
color="green"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
controlApc("on");
|
||||
}}
|
||||
>
|
||||
ON
|
||||
</Button>
|
||||
<Button
|
||||
mt={"8px"}
|
||||
disabled={isDisabled}
|
||||
variant="outline"
|
||||
color="orange"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
controlApc("off");
|
||||
}}
|
||||
>
|
||||
OFF
|
||||
</Button>
|
||||
<Button
|
||||
mt={"8px"}
|
||||
disabled={isDisabled}
|
||||
variant="outline"
|
||||
color="red"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
controlApc("restart");
|
||||
}}
|
||||
>
|
||||
Restart
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
|
||||
<Box
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
style={{ height: "175px", width: "332px" }}
|
||||
style={{ height: "160px", width: "332px" }}
|
||||
>
|
||||
<TerminalCLI
|
||||
cliOpened={loadTerminal}
|
||||
|
|
@ -130,8 +411,8 @@ const CardLine = ({
|
|||
fontSize={11}
|
||||
miniSize={true}
|
||||
customStyle={{
|
||||
maxHeight: "175px",
|
||||
height: "175px",
|
||||
maxHeight: "160px",
|
||||
height: "160px",
|
||||
fontSize: "7px",
|
||||
padding: "0px",
|
||||
paddingBottom: "0px",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
color: dimgrey;
|
||||
font-size: 11px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
/* margin-top: 4px; */
|
||||
height: 20px;
|
||||
|
|
@ -119,3 +120,7 @@
|
|||
border-radius: 8px;
|
||||
background-color: #f3f3f38c;
|
||||
}
|
||||
|
||||
.topBarLine:hover {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,8 +229,7 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
|
|||
APC 1
|
||||
</Text>
|
||||
{RenderAPCStatus(dataStation?.apc1)}
|
||||
{dataStation?.apc1?.status === "DISCONNECTED" ||
|
||||
dataStation?.apc1?.status === "TIMEOUT" ? (
|
||||
{dataStation?.apc1?.status !== "CONNECTED" ? (
|
||||
<Button
|
||||
style={{ height: "24px" }}
|
||||
size="xs"
|
||||
|
|
@ -486,8 +485,7 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
|
|||
APC 2
|
||||
</Text>
|
||||
{RenderAPCStatus(dataStation?.apc2)}
|
||||
{dataStation?.apc2?.status === "DISCONNECTED" ||
|
||||
dataStation?.apc2?.status === "TIMEOUT" ? (
|
||||
{dataStation?.apc2?.status !== "CONNECTED" ? (
|
||||
<Button
|
||||
style={{ height: "24px" }}
|
||||
size="xs"
|
||||
|
|
@ -883,6 +881,30 @@ export const DrawerSwitchControl: React.FC<DrawerProps> = ({
|
|||
<Box ps={"8px"} pt={"4px"}>
|
||||
{dataStation?.switch ? RenderAPCStatus(dataStation?.switch) : ""}
|
||||
</Box>
|
||||
{dataStation?.switch?.status !== "CONNECTED" ? (
|
||||
<Button
|
||||
size="xs"
|
||||
disabled={isSubmit}
|
||||
variant="filled"
|
||||
color="yellow"
|
||||
onClick={() => {
|
||||
socket?.emit("control_switch", {
|
||||
ports: [],
|
||||
command: "reconnect",
|
||||
station: stationAPI,
|
||||
ip: stationAPI?.switch_control_ip,
|
||||
});
|
||||
setIsSubmit(true);
|
||||
setTimeout(() => {
|
||||
setIsSubmit(false);
|
||||
}, 10000);
|
||||
}}
|
||||
>
|
||||
<IconRepeat size={14} />
|
||||
</Button>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<Button
|
||||
disabled={isSubmit}
|
||||
title={
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {
|
|||
Button,
|
||||
Flex,
|
||||
Group,
|
||||
Input,
|
||||
Modal,
|
||||
NumberInput,
|
||||
PasswordInput,
|
||||
|
|
@ -97,6 +98,8 @@ const StationSetting = ({
|
|||
apc_name: value.apcName || value.apc_name,
|
||||
outlet: value.outlet,
|
||||
station_id: value.stationId || value.station_id,
|
||||
interface: value.interface,
|
||||
baud: value.baud,
|
||||
}));
|
||||
setLines(dataLine);
|
||||
}
|
||||
|
|
@ -186,6 +189,20 @@ const StationSetting = ({
|
|||
}
|
||||
/>
|
||||
</Table.Td>
|
||||
<Table.Td fz={"sm"} p="3px" ta={"center"} fw={700}>
|
||||
<Input
|
||||
value={row?.interface}
|
||||
onChange={(e) =>
|
||||
setLines((pre) =>
|
||||
pre.map((value, i) =>
|
||||
i === index
|
||||
? { ...value, interface: e.target.value }
|
||||
: value
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Table.Td>
|
||||
<Table.Td fz={"sm"} p="3px" ta={"center"} fw={700}>
|
||||
{row?.lineNumber ? (
|
||||
<ActionIcon
|
||||
|
|
@ -339,7 +356,7 @@ const StationSetting = ({
|
|||
)}
|
||||
</div>
|
||||
}
|
||||
size={"80%"}
|
||||
size={"90%"}
|
||||
style={{ position: "absolute", left: 0 }}
|
||||
centered
|
||||
opened={isOpen}
|
||||
|
|
@ -648,21 +665,24 @@ const StationSetting = ({
|
|||
>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th fz={"sm"} w={"15%"} ta={"center"}>
|
||||
Line number
|
||||
<Table.Th fz={"sm"} w={"10%"} ta={"center"}>
|
||||
Number
|
||||
</Table.Th>
|
||||
<Table.Th fz={"sm"} w={"15%"} ta={"center"}>
|
||||
Port
|
||||
</Table.Th>
|
||||
<Table.Th fz={"sm"} w={"15%"} ta={"center"}>
|
||||
<Table.Th fz={"sm"} w={"10%"} ta={"center"}>
|
||||
Clear line
|
||||
</Table.Th>
|
||||
<Table.Th fz={"sm"} w={"15%"} ta={"center"}>
|
||||
APC
|
||||
</Table.Th>
|
||||
<Table.Th fz={"sm"} w={"15%"} ta={"center"}>
|
||||
<Table.Th fz={"sm"} w={"10%"} ta={"center"}>
|
||||
Outlet
|
||||
</Table.Th>
|
||||
<Table.Th fz={"sm"} w={"15%"} ta={"center"}>
|
||||
Interface
|
||||
</Table.Th>
|
||||
<Table.Th fz={"sm"} w={"10%"} ta={"center"}></Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
|
|
|
|||
|
|
@ -244,3 +244,5 @@ export const dataPermission = [
|
|||
requiredPermissions: [],
|
||||
},
|
||||
];
|
||||
|
||||
export const listBaudDefault = [4800, 9600, 19200, 115200, 230400];
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@ export type TLine = {
|
|||
time: number;
|
||||
};
|
||||
commands?: string[];
|
||||
interface?: string;
|
||||
baud?: number;
|
||||
};
|
||||
|
||||
export type TUser = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue