完善分发收集续做、打印机状态查询与任务字段对齐
- 完成/失败态统一卡位13自动续做,修复主进程失败时 mode 被重置导致续做被拒 - 接入 SAPI_PrinterCheckstatus,Header 轻量刷状态,Init 后一次 GetPrinterInfo - print_flag 取自 soon.flag,path_file 目录级 staging,加密狗与格式化选项对齐 - 失败页并入运行页,移除独立 DistributeFailedView Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,7 +5,7 @@ import { CS_FAIL, CS_OK } from '../constants'
|
||||
import { assertNotBusy, assertReady, mainAppState } from '../services/app-state'
|
||||
import { configStore } from '../services/config-store'
|
||||
import { startUsbPoll, startJobPoll, stopAllPolls, stopJobPoll, stopUsbPoll, stopCardPositionPoll, startCardPositionPoll, getPollMainWindow, isJobPollActive } from '../services/poll-manager'
|
||||
import { parsePrinterInfoFromDll, type PrinterStatusSnapshot } from '@shared/printer-info'
|
||||
import { parsePrinterInfoFromDll, statusTextFromCheckstatus, type PrinterStatusSnapshot } from '@shared/printer-info'
|
||||
import { cleanPathPattern, getDirectorySizeBytes } from '../utils/dir-size'
|
||||
import { getDesignAppPath } from '../services/app-config'
|
||||
import { openDesignApp } from '../services/open-design-app'
|
||||
@@ -83,6 +83,50 @@ export function registerIpcHandlers(): void {
|
||||
}
|
||||
})
|
||||
|
||||
tracedHandle('dll:printer-status', async () => {
|
||||
if (!mainAppState.initialized) {
|
||||
const cached = configStore.get('lastPrinterStatus')
|
||||
return ok({
|
||||
statusText: cached?.statusText ?? '未初始化',
|
||||
fromCache: true
|
||||
})
|
||||
}
|
||||
try {
|
||||
assertReady()
|
||||
const dll = await loadDllModule()
|
||||
if (!dll.isPrinterCheckstatusApiAvailable()) {
|
||||
return fail(CS_FAIL, '当前 DLL 不支持打印机状态查询')
|
||||
}
|
||||
const code = dll.dllPrinterCheckstatus()
|
||||
const parsed = statusTextFromCheckstatus(code)
|
||||
const cached = configStore.get('lastPrinterStatus')
|
||||
if (parsed.ok) {
|
||||
const snapshot: PrinterStatusSnapshot = {
|
||||
ribbonType: cached?.ribbonType ?? '—',
|
||||
statusText: parsed.statusText,
|
||||
serialNo: cached?.serialNo ?? '—',
|
||||
printedCount: cached?.printedCount ?? 0
|
||||
}
|
||||
configStore.set('lastPrinterStatus', snapshot)
|
||||
return ok({ statusText: parsed.statusText, statusCode: code })
|
||||
}
|
||||
if (cached) {
|
||||
return ok({
|
||||
statusText: parsed.statusText,
|
||||
fromCache: true,
|
||||
liveError: parsed.liveError
|
||||
})
|
||||
}
|
||||
return fail(code, parsed.liveError || parsed.statusText)
|
||||
} catch (err) {
|
||||
const cached = configStore.get('lastPrinterStatus')
|
||||
if (cached) {
|
||||
return ok({ statusText: cached.statusText, fromCache: true, liveError: String(err) })
|
||||
}
|
||||
return fail(CS_FAIL, String(err))
|
||||
}
|
||||
})
|
||||
|
||||
tracedHandle('dll:printer-reset', async () => {
|
||||
try {
|
||||
assertReady()
|
||||
|
||||
@@ -95,14 +95,12 @@ export function startJobPoll(id: string): void {
|
||||
log.warn('GetJobStateById query failed', r.queryErrorCode)
|
||||
return
|
||||
}
|
||||
if (failed || cancelled) {
|
||||
stopJobPoll(true)
|
||||
} else if (finished) {
|
||||
if (failed || cancelled || finished) {
|
||||
stopJobPoll(false)
|
||||
}
|
||||
} catch (e) {
|
||||
log.error('job poll error', e)
|
||||
stopJobPoll(true)
|
||||
stopJobPoll(false)
|
||||
}
|
||||
})()
|
||||
}, POLL_INTERVAL_MS)
|
||||
@@ -115,7 +113,7 @@ export function startUsbPoll(): void {
|
||||
usbTimer = setInterval(() => {
|
||||
void pollUsbOnce(gen).catch((e) => {
|
||||
log.error('usb poll error', e)
|
||||
stopUsbPoll(true)
|
||||
stopUsbPoll(false)
|
||||
})
|
||||
}, POLL_INTERVAL_MS)
|
||||
}
|
||||
@@ -156,21 +154,24 @@ async function pollUsbOnce(gen: number): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function pollCardPositionOnce(): Promise<void> {
|
||||
try {
|
||||
const dll = await loadDllModule()
|
||||
if (!dll.isCardPositionApiAvailable()) return
|
||||
const r = dll.dllGetPrinterCardPosition()
|
||||
const tick = { queryCode: r.queryCode, position: r.position }
|
||||
emitTrace('[poll] card:position-tick', tick)
|
||||
send('card:position-tick', tick)
|
||||
} catch (e) {
|
||||
log.error('card position poll error', e)
|
||||
}
|
||||
}
|
||||
|
||||
export function startCardPositionPoll(): void {
|
||||
stopCardPositionPoll()
|
||||
void pollCardPositionOnce()
|
||||
cardTimer = setInterval(() => {
|
||||
void (async () => {
|
||||
try {
|
||||
const dll = await loadDllModule()
|
||||
if (!dll.isCardPositionApiAvailable()) return
|
||||
const r = dll.dllGetPrinterCardPosition()
|
||||
const tick = { queryCode: r.queryCode, position: r.position }
|
||||
emitTrace('[poll] card:position-tick', tick)
|
||||
send('card:position-tick', tick)
|
||||
} catch (e) {
|
||||
log.error('card position poll error', e)
|
||||
}
|
||||
})()
|
||||
void pollCardPositionOnce()
|
||||
}, POLL_INTERVAL_MS)
|
||||
}
|
||||
|
||||
|
||||
@@ -47,9 +47,12 @@ let SAPI_PrinterMovetousbreader: any = null
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let SAPI_GetPrinterCardPosition: any = null
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let SAPI_PrinterCheckstatus: any = null
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let SAPI_UploadFile: any = null
|
||||
let hasRejectApi = false
|
||||
let hasCardPositionApi = false
|
||||
let hasCheckstatusApi = false
|
||||
let hasCancelApi = false
|
||||
let hasUploadApi = false
|
||||
let hasPrinterInfoEx = false
|
||||
@@ -179,13 +182,23 @@ function loadLibrary(): void {
|
||||
emitTrace('[dll] SAPI_GetPrinterCardPosition not in workDll (optional)')
|
||||
}
|
||||
|
||||
try {
|
||||
SAPI_PrinterCheckstatus = lib.func('int __stdcall SAPI_PrinterCheckstatus()')
|
||||
hasCheckstatusApi = true
|
||||
} catch {
|
||||
SAPI_PrinterCheckstatus = null
|
||||
hasCheckstatusApi = false
|
||||
emitTrace('[dll] SAPI_PrinterCheckstatus not in workDll (optional)')
|
||||
}
|
||||
|
||||
log.info('workDll loaded', {
|
||||
upload: hasUploadApi,
|
||||
printerInfoEx: hasPrinterInfoEx,
|
||||
cancel: hasCancelApi,
|
||||
reject: hasRejectApi,
|
||||
usbReader: hasUsbReaderApi,
|
||||
cardPosition: hasCardPositionApi
|
||||
cardPosition: hasCardPositionApi,
|
||||
checkstatus: hasCheckstatusApi
|
||||
})
|
||||
}
|
||||
|
||||
@@ -214,6 +227,11 @@ export function isCardPositionApiAvailable(): boolean {
|
||||
return hasCardPositionApi
|
||||
}
|
||||
|
||||
export function isPrinterCheckstatusApiAvailable(): boolean {
|
||||
loadLibrary()
|
||||
return hasCheckstatusApi
|
||||
}
|
||||
|
||||
export function dllInit(params: InitParams): number {
|
||||
return traceCall(
|
||||
'SAPI_Init',
|
||||
@@ -336,7 +354,6 @@ export function dllCopyFromUsb(destFolder: string, cardOutput: number): number {
|
||||
export function dllGetUsbCopyState(): {
|
||||
queryCode: number
|
||||
taskStatus: number
|
||||
/** copy_progress 0-100 */
|
||||
progress: number
|
||||
} {
|
||||
return traceCall('SAPI_GetUsbCopyState', undefined, () => {
|
||||
@@ -384,3 +401,11 @@ export function dllGetPrinterCardPosition(): { queryCode: number; position: numb
|
||||
return { queryCode, position: position[0] }
|
||||
})
|
||||
}
|
||||
|
||||
export function dllPrinterCheckstatus(): number {
|
||||
return traceCall('SAPI_PrinterCheckstatus', undefined, () => {
|
||||
loadLibrary()
|
||||
if (!SAPI_PrinterCheckstatus) throw new Error('CHECKSTATUS_API_UNAVAILABLE')
|
||||
return SAPI_PrinterCheckstatus!() as number
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,10 +11,20 @@ export interface ParsedSoonTemplate {
|
||||
frontImageUrl: string
|
||||
backImageUrl: string
|
||||
fields: TemplateFieldRow[]
|
||||
printFlag: number
|
||||
}
|
||||
|
||||
const SOON_FIELD_TYPES = new Set([1, 3, 4, 5])
|
||||
|
||||
export function readSoonPrintFlag(raw: Record<string, unknown>): number {
|
||||
const flag = Number(raw.flag)
|
||||
if (flag === 1 || flag === 2) return flag
|
||||
const hasBack =
|
||||
!!String(raw.backDisplayPic ?? '').trim() ||
|
||||
(Array.isArray(raw.backData) && raw.backData.length > 0)
|
||||
return hasBack ? 2 : 1
|
||||
}
|
||||
|
||||
function pickArray(obj: Record<string, unknown>, key: string): Record<string, unknown>[] {
|
||||
const entry = Object.entries(obj).find(([k]) => k.toLowerCase() === key.toLowerCase())
|
||||
if (!Array.isArray(entry?.[1])) return []
|
||||
@@ -83,7 +93,8 @@ function parseSoonWorkerDisk(soonPath: string, raw: Record<string, unknown>): Pa
|
||||
return {
|
||||
frontImageUrl: toImageUrl(soonPath, frontPic),
|
||||
backImageUrl: toImageUrl(soonPath, backPic),
|
||||
fields
|
||||
fields,
|
||||
printFlag: readSoonPrintFlag(raw)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +130,7 @@ function parseSoonLegacy(soonPath: string, raw: Record<string, unknown>): Parsed
|
||||
fields.push({ label: toFieldLabel(name, side), value, originName: name })
|
||||
})
|
||||
|
||||
return { frontImageUrl, backImageUrl, fields }
|
||||
return { frontImageUrl, backImageUrl, fields, printFlag: readSoonPrintFlag(raw) }
|
||||
}
|
||||
|
||||
export function parseSoonTemplate(soonPath: string, raw: Record<string, unknown>): ParsedSoonTemplate {
|
||||
|
||||
@@ -51,7 +51,6 @@ function stageSoonAssets(taskDir: string, soonSrc: string, soonDest: string): vo
|
||||
}
|
||||
}
|
||||
|
||||
/** 保留目录级 path_file,不展开为单文件列表 */
|
||||
function normalizeCopyPaths(payload: Record<string, unknown>): void {
|
||||
if (!Array.isArray(payload.path_file)) {
|
||||
throw new Error('拷贝任务缺少 path_file')
|
||||
@@ -87,7 +86,6 @@ function normalizeCopyPaths(payload: Record<string, unknown>): void {
|
||||
payload.path_file = normalized
|
||||
}
|
||||
|
||||
/** staging 后按 has_* 裁剪字段,并校验 RestJobEx 必填项 */
|
||||
function finalizeRestJobPayload(payload: Record<string, unknown>): void {
|
||||
const hasCopy = payload.has_copy_task === true
|
||||
const hasPrint = payload.has_print_task === true
|
||||
@@ -119,7 +117,6 @@ function finalizeRestJobPayload(payload: Record<string, unknown>): void {
|
||||
}
|
||||
}
|
||||
|
||||
/** 通过 SAPI_UploadFile + 本地落盘,准备 RestJobEx 所需路径 */
|
||||
export function stageJobPayloadJson(
|
||||
json: string,
|
||||
sharedDir: string,
|
||||
|
||||
Reference in New Issue
Block a user