Update note SN sau khi run dpelp và api heal check

This commit is contained in:
nguyentrungthat 2025-12-23 16:34:21 +07:00
parent e2e66f86bf
commit fd5d1628a5
9 changed files with 203 additions and 5 deletions

View File

@ -7,10 +7,60 @@ export default class HealCheckController {
try {
const linkWiki =
process.env.LINK_WIKI || 'https://logs.danielvu.com/api/wiki/page/insert?title=Dev_test'
const remoteUrl = process.env.ERP_URL || 'https://stage.nswteam.net'
const header = {
Authorization: 'Bearer ' + process.env.ERP_TOKEN,
}
const resWiki = await axios.post(linkWiki, {
data: 'Health checking',
healthChecking: true,
})
let dataCheckNote = {
name: 'update-note-sn',
status: true,
message: 'Checking api update note SN success',
}
const responseDataSN = await axios.post(
remoteUrl + '/api/transferGetData',
{
urlAPI: '/api/stock-model-serial/get-list-regex',
filter: {
where: {
_q: 'FOC1425Z3RN',
},
},
orgId: ['5fadc798f070e4b64b53ac9c', '5fadc7b0f070e4b64b53ac9d'],
},
{
headers: header,
}
)
if (!responseDataSN?.data?.data || responseDataSN?.data?.data?.length === 0) {
dataCheckNote = {
name: 'update-note-sn',
status: false,
message: 'Checking api update note SN fail',
}
}
const dataSN = responseDataSN?.data?.data[0] || {}
// console.log(payload)
const resSN = await axios.post(
remoteUrl + '/api/transferPostData',
{
urlAPI: '/api/stock-model-serial/data-save',
data: {
id: dataSN?.id,
serialNumberA: dataSN?.serialNumberA,
productModelId: dataSN?.productModelId,
orgId: dataSN?.orgId,
notes: dataSN?.notes,
},
},
{
headers: header,
}
)
return {
code: resWiki.status,
data: [
@ -19,6 +69,13 @@ export default class HealCheckController {
status: resWiki.status < 400 ? true : false,
message: resWiki.data?.message || 'Checking api wiki success',
},
{
...dataCheckNote,
status: resSN.data?.error ? false : true,
message: resSN.data?.error
? `Checking api update note SN false: '${resSN.data?.error?.message}'`
: 'Checking api update note SN success',
},
],
}
} catch (error) {

View File

@ -15,6 +15,7 @@ import {
sendMessageToMail,
sleep,
TestSession,
updateNoteToERP,
} from '../ultils/helper.js'
import Scenario from '#models/scenario'
import path from 'node:path'
@ -22,6 +23,7 @@ import axios from 'axios'
import redis from '@adonisjs/redis/services/main'
import Line from '#models/line'
import { ErrorRow, TestResult } from '../ultils/types.js'
import moment from 'moment'
type Inventory = {
pid: string
@ -499,6 +501,9 @@ export default class LineConnection {
inventory: this.config.inventory || null,
latestScenario: this.config.latestScenario || null,
})
if (result.sn) {
this.updateNote(result.sn, result)
}
} catch (error) {
console.log(error)
}
@ -609,6 +614,15 @@ export default class LineConnection {
})
}
clearCLI() {
this.config.output = ''
this.socketIO.emit('user_clear_terminal', {
stationId: this.config.stationId,
lineId: this.config.id,
})
setTimeout(() => this.writeCommand('\r\n'), 100)
}
waitForExpect = async (expect: string, timeout = 60000) => {
const start = Date.now()
// console.log('[EXPECT]', expect, timeout)
@ -911,4 +925,14 @@ export default class LineConnection {
${tableAI}
`
}
async updateNote(sn: string, data: DataDPELP) {
const licenses = Array.isArray(data.license)
? [...new Set(data.license)]
: data.license
? [data.license]
: []
const note = `\n\n-------ATC-${moment(new Date()).format('YYYY/MM/DD')}-------\nLicense: ${licenses.join(', ')}\nIssues:\n${data.issues?.length ? `- ` + data.issues.join(`\n- `) : ''}`
await updateNoteToERP(sn, note)
}
}

View File

@ -4,6 +4,8 @@ import path from 'node:path'
import nodeMailer from 'nodemailer'
import zulip from 'zulip-js'
import { ErrorRow, LogRule, ParsedLog, TestError, TestResult } from './types.js'
import axios from 'axios'
import moment from 'moment'
type DetectAI = {
status: string[]
@ -614,3 +616,54 @@ export function mapErrorsToRows(errors: TestError[]): ErrorRow[] {
export function escapeHtml(str: string): string {
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}
export async function updateNoteToERP(sn: string, note: string) {
try {
const remoteUrl = process.env.ERP_URL || 'https://stage.nswteam.net'
const header = {
Authorization: 'Bearer ' + process.env.ERP_TOKEN,
}
const responseDataSN = await axios.post(
remoteUrl + '/api/transferGetData',
{
urlAPI: '/api/stock-model-serial/get-list-regex',
filter: {
where: {
_q: sn,
},
},
orgId: ['5fadc798f070e4b64b53ac9c', '5fadc7b0f070e4b64b53ac9d'],
},
{
headers: header,
}
)
if (!responseDataSN?.data?.data || responseDataSN?.data?.data?.length === 0) {
console.log('updateNoteToERP', responseDataSN?.data)
return
}
const dataSN = responseDataSN?.data?.data[0] || {}
const payload = {
id: dataSN?.id,
serialNumberA: dataSN?.serialNumberA,
productModelId: dataSN?.productModelId,
orgId: dataSN?.orgId,
notes: (dataSN?.notes || '') + note,
}
// console.log(payload)
await axios.post(
remoteUrl + '/api/transferPostData',
{
urlAPI: '/api/stock-model-serial/data-save',
data: payload,
},
{
headers: header,
}
)
} catch (error) {
console.log(error)
}
}

View File

@ -595,6 +595,17 @@ export class WebSocketIo {
console.log(error)
}
})
socket.on('clear_terminal', async (data) => {
const { stationId, lineId } = data
await this.handleLineOperation(
io,
stationId,
[lineId],
async (lineCon) => lineCon.clearCLI(),
{}
)
})
})
socketServer.listen(SOCKET_IO_PORT, () => {

View File

@ -389,6 +389,14 @@ function App() {
}, 100);
});
socket?.on("user_clear_terminal", (data) => {
updateValueLineStation(
data?.lineId,
{ netOutput: "", output: "", loadingClearTerminal: true },
data?.stationId
);
});
// ✅ cleanup on unmount or when socket changes
return () => {
socket.off("init");
@ -405,6 +413,7 @@ function App() {
socket.off("update_baud");
socket.off("line_connecting");
socket.off("running_scenario");
socket.off("user_clear_terminal");
};
}, [socket, stations, selectedLine]);
@ -415,12 +424,16 @@ function App() {
lines: station.lines.map((line) => {
const buffered = lineBuffersRef.current.get(line.id || 0);
if (!buffered) return line; // không có update
updateValueSelectedLine(line?.id || 0, { netOutput: buffered });
updateValueSelectedLine(line?.id || 0, {
netOutput: buffered,
loadingClearTerminal: false,
});
return {
...line,
netOutput: (line.netOutput || "") + buffered,
output: buffered,
loadingOutput: line.loadingOutput ? false : true,
loadingClearTerminal: false,
};
}),
}))
@ -448,10 +461,15 @@ function App() {
lineNumber: lineItem.lineNumber,
line_number: lineItem.line_number,
...(isNetOutput && {
netOutput:
(lineItem.netOutput || "") + (updates.netOutput || ""),
netOutput: updates?.loadingClearTerminal
? ""
: (lineItem.netOutput || "") +
(updates.netOutput || ""),
output: updates.netOutput, // Nếu netOutput thì update luôn output
loadingOutput: lineItem.loadingOutput ? false : true,
loadingClearTerminal: updates?.loadingClearTerminal
? updates?.loadingClearTerminal
: false,
}),
};
}),
@ -470,10 +488,14 @@ function App() {
...prevSelected,
...updates,
...(isNetOutput && {
netOutput:
(prevSelected.netOutput || "") + (updates.netOutput || ""),
netOutput: updates?.loadingClearTerminal
? ""
: (prevSelected.netOutput || "") + (updates.netOutput || ""),
output: updates.netOutput,
loadingOutput: prevSelected.loadingOutput ? false : true,
loadingClearTerminal: updates?.loadingClearTerminal
? updates?.loadingClearTerminal
: false,
}),
};
});

View File

@ -682,6 +682,7 @@ const CardLine = ({
isSelected={
selectedLines.find((val) => val.id === line.id) ? true : false
}
loadingClearTerminal={line?.loadingClearTerminal}
/>
</Box>
<Box>

View File

@ -904,8 +904,28 @@ const ModalTerminal = ({
line?.userOpenCLI !== user?.userName
}
line_status={line?.status || ""}
loadingClearTerminal={line?.loadingClearTerminal}
/>
<Flex justify={"space-around"} mt={"md"} pt={"md"} pb={"md"}>
<Button
fw={400}
disabled={isDisable}
variant="filled"
color="red"
size="xs"
onClick={() => {
socket?.emit("clear_terminal", {
lineId: line?.id,
stationId: stationItem?.id,
});
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 1000);
}}
>
Clear Terminal
</Button>
<ButtonDPELP
socket={socket}
selectedLines={line ? [line] : []}

View File

@ -31,6 +31,7 @@ interface TerminalCLIProps {
loadingContent?: boolean;
focusTerminal?: boolean;
isSelected?: boolean;
loadingClearTerminal?: boolean;
}
const TerminalCLI: React.FC<TerminalCLIProps> = ({
@ -50,6 +51,7 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
onBlur,
focusTerminal,
isSelected,
loadingClearTerminal,
}) => {
const xtermRef = useRef<HTMLDivElement>(null);
const terminal = useRef<Terminal>(null);
@ -218,6 +220,13 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
}
}, [isSelected]);
useEffect(() => {
if (loadingClearTerminal && terminal.current) {
terminal.current?.reset();
if (!miniSize && !isDisabled) terminal.current?.focus();
}
}, [loadingClearTerminal]);
return (
<>
<div

View File

@ -104,6 +104,7 @@ export type TLine = {
connecting?: boolean;
runningScenario?: string;
scenario?: IScenario;
loadingClearTerminal?: boolean;
};
export type TUser = {