Update config scenarios

This commit is contained in:
nguyentrungthat 2025-12-15 11:33:45 +07:00
parent 39fed6d1a1
commit 1d0a90f2cf
8 changed files with 290 additions and 99 deletions

View File

@ -42,6 +42,7 @@ export default class ScenariosController {
body: JSON.stringify(payload.body),
timeout: payload.timeout,
isReboot: payload.isReboot,
send_result: payload.send_result,
},
{ client: trx }
)

View File

@ -339,13 +339,20 @@ export default class LineConnection {
console.log(
`Run scenario "${script?.title}" to line ${this.config.lineNumber} of ${this.config.stationName}`
)
if (script?.title === 'DPELP') this.dataDPELP = ''
this.isRunningScript = true
this.socketIO.emit('running_scenario', {
stationId: this.config.stationId,
lineId: this.config.id,
title: script?.title,
})
if (script?.send_result) this.dataDPELP = ''
if (script?.isReboot) {
await sleep(10000)
for (let index = 0; index < 30; index++) {
await sleep(1000)
this.breakSpam()
}
}
const now = Date.now()
this.outputScenario += `\n\n---start-scenarios---${now}---${userName}---${script?.title}---\n---scenario---${script?.title}---${now}---\n`
appendLog(
@ -363,31 +370,34 @@ export default class LineConnection {
let stepIndex = 0
return new Promise((resolve, reject) => {
const timeoutTimer = setTimeout(() => {
this.isRunningScript = false
this.socketIO.emit('running_scenario', {
stationId: this.config.stationId,
lineId: this.config.id,
title: '',
})
this.outputBuffer = ''
this.outputScenario = ''
this.config.output += 'Timeout run scenario'
this.socketIO.emit('line_output', {
stationId: this.config.stationId,
lineId: this.config.id,
data: 'Timeout run scenario',
})
this.outputScenario += `\n---end-scenarios---${now}---${userName}---\n`
appendLog(
`\n---end-scenarios---${now}---${userName}---\n`,
this.config.stationId,
this.config.stationName,
this.config.stationIp,
this.config.lineNumber
)
// reject(new Error('Script timeout'))
}, script.timeout || 300000)
const timeoutTimer = setTimeout(
() => {
this.isRunningScript = false
this.socketIO.emit('running_scenario', {
stationId: this.config.stationId,
lineId: this.config.id,
title: '',
})
this.outputBuffer = ''
this.outputScenario = ''
this.config.output += 'Timeout run scenario'
this.socketIO.emit('line_output', {
stationId: this.config.stationId,
lineId: this.config.id,
data: 'Timeout run scenario',
})
this.outputScenario += `\n---end-scenarios---${now}---${userName}---\n`
appendLog(
`\n---end-scenarios---${now}---${userName}---\n`,
this.config.stationId,
this.config.stationName,
this.config.stationIp,
this.config.lineNumber
)
// reject(new Error('Script timeout'))
},
script.timeout ? Number(script.timeout) * 1000 : 300000
)
const runStep = async (index: number) => {
if (index >= steps.length) {
@ -447,11 +457,13 @@ export default class LineConnection {
},
data,
})
if (script?.title === 'DPELP') this.dataDPELP = result
console.log(
`DPELP DATA line ${this.config.lineNumber} of ${this.config.stationName}:`,
this.dataDPELP
)
if (script?.send_result) {
this.dataDPELP = result
console.log(
`DPELP DATA line ${this.config.lineNumber} of ${this.config.stationName}:`,
this.dataDPELP
)
}
if (this.config.latestScenario)
this.config.latestScenario = { ...this.config.latestScenario, detectAI: detectLog }
this.config.data = data
@ -473,6 +485,7 @@ export default class LineConnection {
const step = steps[index]
let repeatCount = Number(step.repeat) || 1
const delay = step?.delay ? Number(step?.delay) * 1000 : 1000
const sendCommand = async () => {
if (repeatCount <= 0) {
// Done → next step
@ -493,12 +506,12 @@ export default class LineConnection {
}
repeatCount--
setTimeout(() => sendCommand(), Number(step?.delay) || 500)
setTimeout(() => sendCommand(), delay)
}
// Nếu expect rỗng → gửi ngay
if (!step?.expect || step?.expect.trim() === '') {
setTimeout(() => sendCommand(), Number(step?.delay) || 500)
setTimeout(() => sendCommand(), delay)
return
}
@ -506,12 +519,15 @@ export default class LineConnection {
// await sleep(200)
// if (this.outputBuffer.includes(step.expect)) {
// this.outputBuffer = ''
// setTimeout(() => sendCommand(), Number(step?.delay) || 500)
// setTimeout(() => sendCommand(), delay)
// }
// }
const matched = await this.waitForExpect(step.expect, Number(step?.timeout) || 60000)
if (matched) setTimeout(() => sendCommand(), Number(step?.delay) || 500)
const matched = await this.waitForExpect(
step.expect.trim(),
script?.timeout ? Number(script?.timeout) * 1000 : 60000
)
if (matched) setTimeout(() => sendCommand(), delay)
}
runStep(stepIndex)
@ -562,6 +578,7 @@ export default class LineConnection {
waitForExpect = async (expect: string, timeout = 60000) => {
const start = Date.now()
console.log('[EXPECT]', expect, timeout)
while (Date.now() - start < timeout) {
if (this.outputBuffer.includes(expect)) {
this.outputBuffer = ''

View File

@ -572,22 +572,23 @@ export class WebSocketIo {
const linkWiki =
process.env.LINK_WIKI || 'https://logs.danielvu.com/api/wiki/page/insert?title=Dev_test'
await axios.post(linkWiki, {
data: tableHTML,
titleAuto: `[DPELP] - ${stationName} - ` + dataFormat,
})
// await axios.post(linkWiki, {
// data: tableHTML,
// titleAuto: `[DPELP] - ${stationName} - ` + dataFormat,
// })
await sendMessageToMail(
'andrew.ng@apactech.io',
`[DPELP] - ${stationName} - ${dataFormat}`,
tableHTML,
['ips@ipsupply.com.au', 'kay@ipsupply.com.au', 'joseph@apactech.io']
)
await sendMessageToZulip(
'stream',
'ATC_Report',
station.name,
`\n\n---\n**[DPELP] - ${stationName} - ${dataFormat}**\n\n` + zulipMess
tableHTML
// ,
// ['ips@ipsupply.com.au', 'kay@ipsupply.com.au', 'joseph@apactech.io']
)
// await sendMessageToZulip(
// 'stream',
// 'ATC_Report',
// station.name,
// `\n\n---\n**[DPELP] - ${stationName} - ${dataFormat}**\n\n` + zulipMess
// )
} catch (error) {
console.log(error)
}

View File

@ -59,6 +59,7 @@ const ScenarioCard = ({
socket,
setOpenScenarioModal,
setIsDisable,
station,
}: {
scenario: IScenario;
index: number;
@ -68,6 +69,7 @@ const ScenarioCard = ({
socket: Socket | null;
setOpenScenarioModal: (value: boolean) => void;
setIsDisable: (value: boolean) => void;
station: TStation;
}) => {
const [isHovered, setIsHovered] = useState(false);
const [overlayPosition, setOverlayPosition] = useState({ top: 0, left: 0 });
@ -203,6 +205,45 @@ const ScenarioCard = ({
setIsDisable(false);
}, 5000);
if (scenario?.isReboot || scenario?.is_reboot) {
const lineApc1 = selectedLines
?.filter(
(el) =>
el.outlet &&
Number(el.outlet) > 0 &&
(el.apcName === "apc_1" || el.apc_name === "apc_1")
)
?.map((el) => el.outlet);
const lineApc2 = selectedLines
?.filter(
(el) =>
el.outlet &&
Number(el.outlet) > 0 &&
(el.apcName === "apc_2" || el.apc_name === "apc_2")
)
?.map((el) => el.outlet);
if (lineApc1.length > 0)
socket?.emit("control_apc", {
outletNumbers: lineApc1,
station: { ...station, lines: [] },
action: "restart",
apcName: "apc_1",
});
if (lineApc2.length > 0)
socket?.emit("control_apc", {
outletNumbers: lineApc2,
station: { ...station, lines: [] },
action: "restart",
apcName: "apc_2",
});
}
if (scenario?.send_result)
socket?.emit("run_all_dpelp", {
lineIds: selectedLines?.map((el) => el.id),
stationName: station.name,
stationId: Number(station.id),
});
selectedLines
.filter(
(el) =>
@ -213,7 +254,13 @@ const ScenarioCard = ({
socket?.emit(
"run_scenario",
Object.assign(el, {
scenario: scenario,
scenario: {
...scenario,
isReboot:
typeof scenario?.isReboot !== "undefined"
? scenario?.isReboot
: scenario?.is_reboot,
},
})
);
});
@ -348,7 +395,6 @@ const BottomToolBar = ({
<>
{/* Modal chọn Scenario - Custom Simple Modal */}
<motion.div
initial={false}
animate={{
@ -388,10 +434,16 @@ const BottomToolBar = ({
<IconCaretUp color="green" size={20} />
)}
</ActionIcon>
<Box style={{ position: "relative", height: isExpand ? "150px" : "60px", overflow: "hidden" }}>
<Box
style={{
position: "relative",
height: isExpand ? "150px" : "60px",
overflow: "hidden",
}}
>
<Grid style={{ height: "100%" }}>
<Grid.Col span={isExpand ? 1 : 2}></Grid.Col>
<Grid.Col span={isExpand ? 10 : 8 }>
<Grid.Col span={isExpand ? 10 : 8}>
{isExpand ? (
<Tabs
defaultValue="command"
@ -401,7 +453,7 @@ const BottomToolBar = ({
setActiveTabBottom(val || "command");
}}
className={classes.containerBottom}
style={{ height: "100%",backgroundColor: "white", }}
style={{ height: "100%", backgroundColor: "white" }}
>
<Tabs.List>
<Tabs.Tab
@ -548,7 +600,8 @@ const BottomToolBar = ({
lines.forEach((line) => {
socket?.emit("open_cli", {
lineId: line.id,
stationId: line.stationId || line.station_id,
stationId:
line.stationId || line.station_id,
userEmail: user?.email,
userName: user?.userName,
});
@ -557,7 +610,8 @@ const BottomToolBar = ({
selectedLines.forEach((line) => {
socket?.emit("close_cli", {
lineId: line?.id,
stationId: line.stationId || line.station_id,
stationId:
line.stationId || line.station_id,
});
});
setSelectedLines([]);
@ -615,6 +669,9 @@ const BottomToolBar = ({
socket={socket}
selectedLines={selectedLines}
isDisable={isDisable || selectedLines.length === 0}
dataDPELP={scenarios?.find(
(el) => el.title.toUpperCase() === "DPELP"
)}
onClick={() => {
if (
selectedLines.length > 0
@ -665,7 +722,13 @@ const BottomToolBar = ({
flexDirection: "column",
}}
>
<Box style={{ overflow: "auto",height: "200px", padding: "4px 8px 0 0" ,}}>
<Box
style={{
overflow: "auto",
height: "200px",
padding: "4px 8px 0 0",
}}
>
<DrawerAPCControl
socket={socket}
stationAPI={station}
@ -693,8 +756,14 @@ const BottomToolBar = ({
</Tabs.Panel>
</Tabs>
) : (
<Box p={3} >
<Flex direction="row" align="center" gap="xs" wrap="nowrap" style={{ height: "100%" }}>
<Box p={3}>
<Flex
direction="row"
align="center"
gap="xs"
wrap="nowrap"
style={{ height: "100%" }}
>
{/* Danh sách Line - chiếm 1 phần, cố định width và height, hiển thị 2 hàng */}
<Box
style={{
@ -704,7 +773,11 @@ const BottomToolBar = ({
overflow: "auto",
}}
>
<Flex wrap={"wrap"} gap={"6px"} style={{ height: "100%" }}>
<Flex
wrap={"wrap"}
gap={"6px"}
style={{ height: "100%" }}
>
{selectedLines.map((el) => (
<Box
key={el.id}
@ -741,11 +814,7 @@ const BottomToolBar = ({
});
}}
/>
<Flex
align={"center"}
justify={"center"}
h="100%"
>
<Flex align={"center"} justify={"center"} h="100%">
<Text fz={"10px"}>Line {el.lineNumber}</Text>
</Flex>
</Box>
@ -766,8 +835,7 @@ const BottomToolBar = ({
selectedLines.forEach((line) => {
socket?.emit("close_cli", {
lineId: line?.id,
stationId:
line.stationId || line.station_id,
stationId: line.stationId || line.station_id,
});
});
setSelectedLines([]);
@ -782,7 +850,10 @@ const BottomToolBar = ({
</Box>
{/* Selected count - ở giữa */}
<Flex align={"center"} style={{ flex: "0 0 auto", flexShrink: 0 }}>
<Flex
align={"center"}
style={{ flex: "0 0 auto", flexShrink: 0 }}
>
<Text fz={"10px"} c="dark" fw={600}>
Selected: {selectedLines.length} /{" "}
{station.lines.length}
@ -801,7 +872,15 @@ const BottomToolBar = ({
}}
>
{/* Input */}
<Box style={{ flexGrow: 1.5, flexShrink: 1, flexBasis: "auto", maxWidth: "100%", overflow: "hidden" }}>
<Box
style={{
flexGrow: 1.5,
flexShrink: 1,
flexBasis: "auto",
maxWidth: "100%",
overflow: "hidden",
}}
>
<InputHistory
selectedLines={selectedLines}
socket={socket}
@ -811,7 +890,10 @@ const BottomToolBar = ({
</Box>
{/* Select All */}
<Flex align={"center"} style={{ flex: "0 0 auto", flexShrink: 0 }}>
<Flex
align={"center"}
style={{ flex: "0 0 auto", flexShrink: 0 }}
>
<ButtonSelect
selectedLines={selectedLines}
setSelectedLines={setSelectedLines}
@ -951,6 +1033,7 @@ const BottomToolBar = ({
socket={socket}
setOpenScenarioModal={setOpenScenarioModal}
setIsDisable={setIsDisable}
station={station}
/>
))}
</Grid>

View File

@ -15,12 +15,14 @@ export const ButtonDPELP = ({
onClick,
selectedLines,
className = "",
dataDPELP,
}: {
socket: Socket | null;
isDisable: boolean;
onClick: () => void;
selectedLines: TLine[];
className?: string;
dataDPELP?: IScenario;
}) => {
return (
<Button
@ -146,13 +148,15 @@ export const ButtonDPELP = ({
socket?.emit(
"run_scenario",
Object.assign(el, {
scenario: {
id: 0,
is_reboot: 0,
title: "DPELP",
timeout: 360000,
body: JSON.stringify(body),
},
scenario: dataDPELP
? dataDPELP
: {
id: 0,
is_reboot: 0,
title: "DPELP",
timeout: 360000,
body: JSON.stringify(body),
},
})
);
});
@ -170,6 +174,7 @@ export const ButtonScenario = ({
selectedLines,
scenario,
fontSize = "12px",
station,
}: {
socket: Socket | null;
isDisable: boolean;
@ -177,6 +182,7 @@ export const ButtonScenario = ({
selectedLines: TLine[];
scenario: IScenario;
fontSize?: string;
station: TStation;
}) => {
return (
<Button
@ -190,11 +196,55 @@ export const ButtonScenario = ({
className={classes.buttonScenario}
onClick={async (e) => {
onClick(e);
if (scenario?.isReboot || scenario?.is_reboot) {
const lineApc1 = selectedLines
?.filter(
(el) =>
el.outlet &&
Number(el.outlet) > 0 &&
(el.apcName === "apc_1" || el.apc_name === "apc_1")
)
?.map((el) => el.outlet);
const lineApc2 = selectedLines
?.filter(
(el) =>
el.outlet &&
Number(el.outlet) > 0 &&
(el.apcName === "apc_2" || el.apc_name === "apc_2")
)
?.map((el) => el.outlet);
if (lineApc1.length > 0)
socket?.emit("control_apc", {
outletNumbers: lineApc1,
station: { ...station, lines: [] },
action: "restart",
apcName: "apc_1",
});
if (lineApc2.length > 0)
socket?.emit("control_apc", {
outletNumbers: lineApc2,
station: { ...station, lines: [] },
action: "restart",
apcName: "apc_2",
});
}
if (scenario?.send_result)
socket?.emit("run_all_dpelp", {
lineIds: selectedLines?.map((el) => el.id),
stationName: station.name,
stationId: Number(station.id),
});
selectedLines?.forEach((el) => {
socket?.emit(
"run_scenario",
Object.assign(el, {
scenario: scenario,
scenario: {
...scenario,
isReboot:
typeof scenario?.isReboot !== "undefined"
? scenario?.isReboot
: scenario?.is_reboot,
},
})
);
});

View File

@ -405,7 +405,21 @@ const CardLine = ({
socket={socket}
selectedLines={[line]}
isDisable={isDisabled}
dataDPELP={scenarios?.find(
(el) => el.title.toUpperCase() === "DPELP"
)}
onClick={() => {
const dpelp = scenarios?.find(
(el) => el.title.toUpperCase() === "DPELP"
);
if (dpelp?.isReboot || dpelp?.is_reboot) {
socket?.emit("control_apc", {
outletNumbers: [line.outlet],
station: { ...stationItem, lines: [] },
action: "restart",
apcName: line.apc_name || line.apcName,
});
}
socket?.emit("run_all_dpelp", {
lineIds: [line?.id],
stationName: stationItem.name,
@ -450,6 +464,7 @@ const CardLine = ({
socket={socket}
selectedLines={[line]}
isDisable={isDisabled}
station={stationItem}
onClick={() => {
setIsDisabled(true);
setTimeout(() => {

View File

@ -9,6 +9,7 @@ import {
Text,
Flex,
CloseButton,
Checkbox,
} from "@mantine/core";
import classes from "../Component.module.css";
import TableRows from "./Scenario/TableRows";
@ -61,6 +62,7 @@ function ModalScenario({
] as IBodyScenario[],
timeout: "30000",
isReboot: false,
send_result: false,
},
validate: {
title: (value) => {
@ -124,6 +126,7 @@ function ModalScenario({
const payload = {
title: form.values.title,
isReboot: form.values.isReboot,
send_result: form.values.send_result,
body: body,
timeout: Number(form.values.timeout),
};
@ -308,7 +311,14 @@ function ModalScenario({
"body",
JSON.parse(scenario.body)
);
form.setFieldValue("isReboot", scenario.isReboot);
form.setFieldValue(
"isReboot",
scenario.is_reboot
);
form.setFieldValue(
"send_result",
scenario.send_result
);
}
}}
>
@ -337,8 +347,8 @@ function ModalScenario({
</Grid.Col>
<Grid.Col span={2}>
<TextInput
label="Timeout (ms)"
placeholder="Timeout (ms)"
label="Timeout (s)"
placeholder="Timeout (s)"
value={form.values.timeout}
error={form.errors.timeout}
onChange={(e) =>
@ -348,26 +358,38 @@ function ModalScenario({
/>
</Grid.Col>
<Grid.Col
span={3}
span={4}
style={{
display: "flex",
alignItems: "end",
marginBottom: "8px",
}}
>
{/* <Checkbox
label="Reboot"
style={{ color: "red" }}
checked={form.values.isReboot}
onChange={(event) =>
form.setFieldValue(
"isReboot",
event.currentTarget.checked
)
}
/> */}
<Checkbox
label="Reboot and send Break"
style={{ color: "red" }}
checked={form.values.isReboot}
onChange={(event) =>
form.setFieldValue(
"isReboot",
event.currentTarget.checked
)
}
/>
<Checkbox
label="Send result"
ms={"md"}
// style={{ color: "" }}
checked={form.values.send_result}
onChange={(event) =>
form.setFieldValue(
"send_result",
event.currentTarget.checked
)
}
/>
</Grid.Col>
<Grid.Col span={3}>
<Grid.Col span={2}>
<div
style={{
display: "flex",
@ -426,7 +448,7 @@ function ModalScenario({
Expect
</Table.Th>
<Table.Th>Send</Table.Th>
<Table.Th w={130}>Delay(ms)</Table.Th>
<Table.Th w={130}>Delay(s)</Table.Th>
<Table.Th w={100}>Repeat</Table.Th>
<Table.Th></Table.Th>
</Table.Tr>

View File

@ -163,6 +163,8 @@ export type IScenario = {
body: string;
timeout: number;
isReboot: boolean;
is_reboot: boolean;
send_result: boolean;
updated_at: string;
};