|
|
|
@@ -21,14 +21,17 @@ import ChevronRight from "@mui/icons-material/ChevronRight"; |
|
|
|
import Settings from "@mui/icons-material/Settings"; |
|
|
|
import { |
|
|
|
checkPrinterStatus, |
|
|
|
fetchLaserBag2MarkInfo, |
|
|
|
fetchLaserJobOrders, |
|
|
|
fetchLaserBag2Settings, |
|
|
|
type LaserBag2MarkInfo, |
|
|
|
type LaserLastReceiveSuccess, |
|
|
|
JobOrderListItem, |
|
|
|
patchSetting, |
|
|
|
sendLaserBag2Job, |
|
|
|
} from "@/app/api/laserPrint/actions"; |
|
|
|
import dayjs from "dayjs"; |
|
|
|
import { formatHongKongDateTime } from "@/utils/formatHongKongDateTime"; |
|
|
|
|
|
|
|
const BG_TOP = "#E8F4FC"; |
|
|
|
const BG_LIST = "#D4E8F7"; |
|
|
|
@@ -45,6 +48,8 @@ const PRINTER_RETRY_MS = 30 * 1000; |
|
|
|
const LASER_SEND_COUNT = 3; |
|
|
|
const BETWEEN_SEND_MS = 3000; |
|
|
|
const SUCCESS_SIGNAL_MS = 3500; |
|
|
|
/** Poll laser TCP GetMarkData / GetMarkStatus / GetMarkedCount */ |
|
|
|
const MARK_INFO_MS = 5000; |
|
|
|
|
|
|
|
function formatQty(val: number | null | undefined): string { |
|
|
|
if (val == null) return "—"; |
|
|
|
@@ -63,6 +68,11 @@ function getBatch(jo: JobOrderListItem): string { |
|
|
|
return (jo.lotNo || "—").trim() || "—"; |
|
|
|
} |
|
|
|
|
|
|
|
function fmtMarkText(s: string | null | undefined): string { |
|
|
|
if (s == null || s === "") return "—"; |
|
|
|
return s; |
|
|
|
} |
|
|
|
|
|
|
|
function delay(ms: number): Promise<void> { |
|
|
|
return new Promise((resolve) => setTimeout(resolve, ms)); |
|
|
|
} |
|
|
|
@@ -88,6 +98,8 @@ const LaserPrintSearch: React.FC = () => { |
|
|
|
const [settingsLoaded, setSettingsLoaded] = useState(false); |
|
|
|
const [printerConnected, setPrinterConnected] = useState(false); |
|
|
|
const [printerMessage, setPrinterMessage] = useState("檸檬機(激光機)未連接"); |
|
|
|
const [markInfo, setMarkInfo] = useState<LaserBag2MarkInfo | null>(null); |
|
|
|
const [markInfoError, setMarkInfoError] = useState<string | null>(null); |
|
|
|
|
|
|
|
const loadSystemSettings = useCallback(async () => { |
|
|
|
try { |
|
|
|
@@ -173,6 +185,23 @@ const LaserPrintSearch: React.FC = () => { |
|
|
|
return () => clearInterval(id); |
|
|
|
}, [printerConnected, checkLaser, settingsLoaded]); |
|
|
|
|
|
|
|
const loadMarkInfo = useCallback(async () => { |
|
|
|
try { |
|
|
|
const m = await fetchLaserBag2MarkInfo(); |
|
|
|
setMarkInfo(m); |
|
|
|
setMarkInfoError(null); |
|
|
|
} catch (e) { |
|
|
|
setMarkInfoError(e instanceof Error ? e.message : "無法讀取打標狀態"); |
|
|
|
} |
|
|
|
}, []); |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
if (!settingsLoaded) return; |
|
|
|
void loadMarkInfo(); |
|
|
|
const id = setInterval(() => void loadMarkInfo(), MARK_INFO_MS); |
|
|
|
return () => clearInterval(id); |
|
|
|
}, [settingsLoaded, loadMarkInfo]); |
|
|
|
|
|
|
|
const goPrevDay = () => { |
|
|
|
setPlanDate((d) => dayjs(d).subtract(1, "day").format("YYYY-MM-DD")); |
|
|
|
}; |
|
|
|
@@ -229,6 +258,7 @@ const LaserPrintSearch: React.FC = () => { |
|
|
|
: ""; |
|
|
|
setSuccessSignal(`已送出 ${LASER_SEND_COUNT} 次至檸檬機(激光機)${ackHint}`); |
|
|
|
await loadSystemSettings(); |
|
|
|
void loadMarkInfo(); |
|
|
|
} catch (e) { |
|
|
|
setErrorSnackbar({ |
|
|
|
open: true, |
|
|
|
@@ -249,6 +279,7 @@ const LaserPrintSearch: React.FC = () => { |
|
|
|
void checkLaser(); |
|
|
|
await loadSystemSettings(); |
|
|
|
void loadJobOrders(false); |
|
|
|
void loadMarkInfo(); |
|
|
|
} catch (e) { |
|
|
|
setErrorSnackbar({ |
|
|
|
open: true, |
|
|
|
@@ -284,12 +315,65 @@ const LaserPrintSearch: React.FC = () => { |
|
|
|
JSON:{lastReceiveJson ?? "—"} |
|
|
|
</Typography> |
|
|
|
<Typography variant="caption" color="text.secondary" display="block" sx={{ mt: 0.5 }}> |
|
|
|
時間:{lastLaserReceive.sentAt ?? "—"} 來源:{lastLaserReceive.source ?? "—"} |
|
|
|
時間:{formatHongKongDateTime(lastLaserReceive.sentAt)} 來源:{lastLaserReceive.source ?? "—"} |
|
|
|
{lastLaserReceive.printerAck ? ` 回覆:${lastLaserReceive.printerAck}` : ""} |
|
|
|
</Typography> |
|
|
|
</Alert> |
|
|
|
)} |
|
|
|
|
|
|
|
{settingsLoaded && ( |
|
|
|
<Paper sx={{ p: 2, mb: 2, backgroundColor: BG_TOP }}> |
|
|
|
<Typography variant="subtitle2" sx={{ fontWeight: 600, mb: 1 }}> |
|
|
|
目前激光機打標(TCP:GetMarkData/GetMarkStatus/GetMarkedCount) |
|
|
|
{markInfo ? ` ${markInfo.host}:${markInfo.port}` : ""} |
|
|
|
</Typography> |
|
|
|
{markInfoError && ( |
|
|
|
<Typography variant="body2" color="error" sx={{ mb: 1 }}> |
|
|
|
{markInfoError} |
|
|
|
</Typography> |
|
|
|
)} |
|
|
|
{!markInfo && !markInfoError && ( |
|
|
|
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}> |
|
|
|
<CircularProgress size={22} /> |
|
|
|
<Typography variant="body2" color="text.secondary"> |
|
|
|
讀取中… |
|
|
|
</Typography> |
|
|
|
</Box> |
|
|
|
)} |
|
|
|
{markInfo && ( |
|
|
|
<Stack spacing={0.75}> |
|
|
|
<Typography variant="body2"> |
|
|
|
<strong>GetMarkStatus</strong>(0 待機/1 打標中/2 其他): |
|
|
|
{markInfo.markStatusLabel ?? "—"} |
|
|
|
{markInfo.markStatus != null ? `(${markInfo.markStatus})` : ""} |
|
|
|
{markInfo.rawMarkStatus ? ` 原始:${markInfo.rawMarkStatus}` : ""} |
|
|
|
</Typography> |
|
|
|
<Typography variant="body2" sx={{ wordBreak: "break-word" }}> |
|
|
|
<strong>GetMarkData</strong>(目前標記字串): |
|
|
|
{fmtMarkText(markInfo.markData)} |
|
|
|
</Typography> |
|
|
|
<Typography variant="body2"> |
|
|
|
<strong>GetMarkedCount</strong>: |
|
|
|
{markInfo.markedCount != null |
|
|
|
? markInfo.markedCount.toLocaleString() |
|
|
|
: fmtMarkText(markInfo.rawMarkedCount)} |
|
|
|
{markInfo.markedCount != null && |
|
|
|
markInfo.rawMarkedCount != null && |
|
|
|
markInfo.rawMarkedCount !== "" && |
|
|
|
String(markInfo.markedCount) !== markInfo.rawMarkedCount.trim() |
|
|
|
? ` 原始:${markInfo.rawMarkedCount}` |
|
|
|
: ""} |
|
|
|
</Typography> |
|
|
|
{markInfo.error ? ( |
|
|
|
<Typography variant="caption" color="warning.main" display="block"> |
|
|
|
部分查詢:{markInfo.error} |
|
|
|
</Typography> |
|
|
|
) : null} |
|
|
|
</Stack> |
|
|
|
)} |
|
|
|
</Paper> |
|
|
|
)} |
|
|
|
|
|
|
|
<Paper sx={{ p: 2, mb: 2, backgroundColor: BG_TOP }}> |
|
|
|
<Stack direction="row" alignItems="center" justifyContent="space-between" flexWrap="wrap" gap={2}> |
|
|
|
<Stack direction="row" alignItems="center" spacing={2}> |
|
|
|
|