初始化

This commit is contained in:
24kycj
2026-05-22 19:56:22 +08:00
commit d0c3fd162a
77 changed files with 17058 additions and 0 deletions
+244
View File
@@ -0,0 +1,244 @@
import { app, dialog, ipcMain, shell } from 'electron'
import fs from 'fs'
import path from 'path'
import log from 'electron-log'
import { CS_FAIL, CS_OK } from '../constants'
import { assertNotBusy, assertReady, mainAppState } from '../services/app-state'
import { configStore } from '../services/config-store'
import {
getActiveJobId,
startJobPoll,
startUsbPoll,
stopAllPolls,
stopJobPoll,
stopUsbPoll
} from '../services/poll-manager'
import {
dllAdminJobCancel,
dllCopyFromUsb,
dllGetPrinterErrorStr,
dllGetPrinterInfo,
dllInit,
dllPrinterReject,
dllPrinterReset,
dllRestJobEx,
isCancelApiAvailable,
isRejectApiAvailable
} from '../services/work-dll.service'
function ok<T>(data?: T) {
return { ok: true as const, code: CS_OK, data }
}
function fail(code: number, message: string) {
return { ok: false as const, code, message }
}
export function registerIpcHandlers(): void {
ipcMain.handle('dll:init', (_e, params) => {
stopAllPolls()
try {
const sharedDir = params?.sharedDir || (configStore.get('sharedDir') as string)
fs.mkdirSync(sharedDir, { recursive: true })
const code = dllInit({
sharedDir,
keepCombinedImage: params?.keepCombinedImage,
stopOnFailure: params?.stopOnFailure,
cleanTaskFile: params?.cleanTaskFile,
autoRetryTimes: params?.autoRetryTimes,
rejectConfig: params?.rejectConfig,
logLevel: params?.logLevel,
outBack: params?.outBack
})
if (code === CS_OK) {
mainAppState.initialized = true
configStore.set('sharedDir', sharedDir)
return ok({ printerDetected: true })
}
mainAppState.initialized = true
configStore.set('sharedDir', sharedDir)
log.warn(`SAPI_Init returned ${code}; UI ready, printer ops may fail until device connected`)
return ok({
printerDetected: false,
warning: '打印机未连接或驱动未就绪,界面可浏览,业务操作需接真机后重试 Init'
})
} catch (err) {
mainAppState.initialized = false
log.error('dll:init', err)
return fail(CS_FAIL, String(err))
}
})
ipcMain.handle('dll:printer-info', () => {
try {
assertReady()
const r = dllGetPrinterInfo()
if (!r.json) return fail(0, '未连接打印机')
return ok(r.json)
} catch (err) {
return fail(CS_FAIL, String(err))
}
})
ipcMain.handle('dll:printer-reset', () => {
try {
assertReady()
const code = dllPrinterReset()
return code === CS_OK ? ok() : fail(code, '重置失败')
} catch (err) {
return fail(CS_FAIL, String(err))
}
})
ipcMain.handle('dll:printer-reject', () => {
try {
assertReady()
if (!isRejectApiAvailable()) return fail(CS_FAIL, 'REJECT_API_UNAVAILABLE')
const code = dllPrinterReject()
return code === CS_OK ? ok() : fail(code, '废卡失败')
} catch (err) {
return fail(CS_FAIL, String(err))
}
})
ipcMain.handle('dll:printer-error-str', (_e, errorNo?: number) => {
try {
assertReady()
return ok({ text: dllGetPrinterErrorStr(errorNo ?? -1) })
} catch (err) {
return fail(CS_FAIL, String(err))
}
})
ipcMain.handle('dll:job-create', (_e, json: string) => {
try {
assertReady()
assertNotBusy()
const r = dllRestJobEx(json)
if (r.code !== CS_OK) return fail(r.code, 'RestJobEx 失败')
mainAppState.mode = 'distributing'
mainAppState.activeJobId = r.jobId
return ok({ jobId: r.jobId })
} catch (err) {
if (String(err).includes('BUSY')) return fail(CS_FAIL, '已有任务在执行')
return fail(CS_FAIL, String(err))
}
})
ipcMain.handle('dll:job-cancel', (_e, jobId: string) => {
try {
assertReady()
const id = jobId || mainAppState.activeJobId
stopJobPoll()
let code = CS_OK
if (isCancelApiAvailable()) {
code = dllAdminJobCancel(id)
} else {
log.info('dll:job-cancel: SAPI_AdminJobCancel not in DLL, poll stopped only')
}
mainAppState.mode = 'ready'
mainAppState.activeJobId = ''
return code === CS_OK ? ok() : fail(code, '取消失败')
} catch (err) {
return fail(CS_FAIL, String(err))
}
})
ipcMain.handle('dll:usb-copy', (_e, req: { destFolder: string; cardOutput: number }) => {
try {
assertReady()
assertNotBusy()
const code = dllCopyFromUsb(req.destFolder, req.cardOutput)
if (code !== CS_OK) {
return fail(code, '可能已有任务在执行')
}
mainAppState.mode = 'usbCopying'
return ok()
} catch (err) {
if (String(err).includes('BUSY')) return fail(CS_FAIL, '已有任务在执行')
return fail(CS_FAIL, String(err))
}
})
ipcMain.handle('poll:job-start', (_e, jobId: string) => {
startJobPoll(jobId)
return ok()
})
ipcMain.handle('poll:job-stop', () => {
stopJobPoll()
return ok()
})
ipcMain.handle('poll:usb-start', () => {
startUsbPoll()
return ok()
})
ipcMain.handle('poll:usb-stop', () => {
stopUsbPoll()
mainAppState.mode = 'ready'
return ok()
})
ipcMain.handle('dialog:open-directory', async () => {
const r = await dialog.showOpenDialog({ properties: ['openDirectory', 'multiSelections'] })
if (r.canceled || !r.filePaths.length) return ok({ paths: [] as string[] })
return ok({ paths: r.filePaths })
})
ipcMain.handle('dialog:open-file', async (_e, filters?: { name: string; extensions: string[] }[]) => {
const r = await dialog.showOpenDialog({
properties: ['openFile'],
filters: filters ?? [{ name: 'Soon', extensions: ['soon'] }]
})
if (r.canceled || !r.filePaths[0]) return ok({ path: '' })
return ok({ path: r.filePaths[0] })
})
ipcMain.handle('fs:path-exists', (_e, paths: string[]) => {
const missing = paths.filter((p) => {
const clean = p.replace(/\\\*\\.\\*$/i, '').replace(/\/\*\.\*$/i, '')
return !fs.existsSync(clean)
})
return ok({ missing })
})
ipcMain.handle('config:get', () =>
ok({
sharedDir: configStore.get('sharedDir'),
templateDir: configStore.get('templateDir'),
skipDllInit: configStore.get('skipDllInit', !app.isPackaged)
})
)
ipcMain.handle('config:set', (_e, patch: Record<string, string>) => {
Object.entries(patch).forEach(([k, v]) => configStore.set(k, v))
return ok(configStore.store)
})
ipcMain.handle('shell:open-path', (_e, target: string) => {
const dir = target || (configStore.get('templateDir') as string)
fs.mkdirSync(dir, { recursive: true })
shell.openPath(dir)
return ok()
})
ipcMain.handle('dll:reject-available', () => ok({ available: isRejectApiAvailable() }))
}
export async function handleBeforeQuit(): Promise<void> {
stopAllPolls()
if (
mainAppState.mode === 'distributing' &&
mainAppState.activeJobId &&
isCancelApiAvailable()
) {
try {
dllAdminJobCancel(mainAppState.activeJobId)
} catch (e) {
log.warn('before-quit cancel', e)
}
}
mainAppState.mode = 'ready'
}