完善分发与收集任务全流程
- RestJobEx 对齐 task_id,path_file 提交目录路径 - 运行页进度仅跟接口轮询,失败态停留 page3 UI - 完成态卡位续做,USB/任务轮询生命周期优化 - 拆分 DLL 加载、任务 staging 与提交前校验 - 移除 mock 与冗余样式,补充 native 依赖 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,7 +6,6 @@ import { getProcessExecDir } from './native-path'
|
||||
|
||||
export const APP_CONFIG_FILENAME = 'cardsoon.config.json'
|
||||
|
||||
/** 与 cardsoon.config.json 键名一致,后续配置在此扩展 */
|
||||
export interface AppFileConfig {
|
||||
designAppPath: string
|
||||
}
|
||||
@@ -16,7 +15,6 @@ const defaults: AppFileConfig = {
|
||||
}
|
||||
|
||||
let cached: AppFileConfig | null = null
|
||||
let loadedFrom = ''
|
||||
|
||||
function bundledConfigPath(): string {
|
||||
if (app.isPackaged) {
|
||||
@@ -46,7 +44,6 @@ export function loadAppFileConfig(): AppFileConfig {
|
||||
if (!fs.existsSync(filePath)) continue
|
||||
try {
|
||||
cached = parseConfigFile(filePath)
|
||||
loadedFrom = filePath
|
||||
log.info(`Loaded ${APP_CONFIG_FILENAME} from ${filePath}`)
|
||||
return cached
|
||||
} catch (e) {
|
||||
@@ -55,18 +52,12 @@ export function loadAppFileConfig(): AppFileConfig {
|
||||
}
|
||||
|
||||
cached = { ...defaults }
|
||||
loadedFrom = ''
|
||||
log.warn(
|
||||
`${APP_CONFIG_FILENAME} not found (checked: ${configSearchPaths().join(', ')}), using defaults`
|
||||
)
|
||||
return cached
|
||||
}
|
||||
|
||||
export function getAppConfigLoadedPath(): string {
|
||||
loadAppFileConfig()
|
||||
return loadedFrom
|
||||
}
|
||||
|
||||
export function getDesignAppPath(): string {
|
||||
return loadAppFileConfig().designAppPath
|
||||
}
|
||||
|
||||
@@ -6,23 +6,15 @@ import type { PrinterStatusSnapshot } from '@shared/printer-info'
|
||||
interface AppConfig {
|
||||
sharedDir: string
|
||||
templateDir: string
|
||||
/** G2 门禁 false:启动即 SAPI_Init;仅调试可改 true */
|
||||
skipDllInit: boolean
|
||||
/** true:IPC/DLL 等调用输出到 DevTools 控制台 */
|
||||
traceEnabled: boolean
|
||||
/** 上次成功的 GetPrinterInfo 解析结果,供离线/失败时展示 */
|
||||
lastPrinterStatus?: PrinterStatusSnapshot
|
||||
}
|
||||
|
||||
const defaultShared = path.join('C:', 'PrintTasks')
|
||||
|
||||
export const configStore = new Store<AppConfig>({
|
||||
name: 'cardsoon-config',
|
||||
defaults: {
|
||||
sharedDir: defaultShared,
|
||||
sharedDir: path.join('C:', 'PrintTasks'),
|
||||
templateDir: path.join(app.getPath('userData'), 'Cardsoon', 'templates'),
|
||||
// 正式版始终 Init;仅开发时可通过 --skip-dll-init 临时跳过
|
||||
skipDllInit: false,
|
||||
traceEnabled: true
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import fs from 'fs'
|
||||
import log from 'electron-log'
|
||||
import { CS_OK } from '../constants'
|
||||
import { mainAppState } from './app-state'
|
||||
import { configStore } from './config-store'
|
||||
import { loadDllModule } from './dll-loader'
|
||||
import type { InitParams } from './work-dll.service'
|
||||
|
||||
let dllInitAttempted = false
|
||||
|
||||
export function isDllInitAttempted(): boolean {
|
||||
return dllInitAttempted
|
||||
}
|
||||
|
||||
export async function ensureDllInitialized(
|
||||
params?: Partial<InitParams>
|
||||
): Promise<{ code: number; warning?: string }> {
|
||||
if (dllInitAttempted) {
|
||||
return { code: CS_OK }
|
||||
}
|
||||
const sharedDir =
|
||||
params?.sharedDir || (configStore.get('sharedDir') as string) || 'C:\\PrintTasks'
|
||||
try {
|
||||
const dll = await loadDllModule()
|
||||
fs.mkdirSync(sharedDir, { recursive: true })
|
||||
const code = dll.dllInit({
|
||||
sharedDir,
|
||||
keepCombinedImage: params?.keepCombinedImage,
|
||||
stopOnFailure: params?.stopOnFailure,
|
||||
cleanTaskFile: params?.cleanTaskFile,
|
||||
autoRetryTimes: params?.autoRetryTimes,
|
||||
rejectConfig: params?.rejectConfig,
|
||||
logLevel: params?.logLevel,
|
||||
outBack: params?.outBack
|
||||
})
|
||||
dllInitAttempted = true
|
||||
mainAppState.initialized = true
|
||||
configStore.set('sharedDir', sharedDir)
|
||||
log.info('DLL initialized', { sharedDir, code })
|
||||
if (code === CS_OK) return { code }
|
||||
return {
|
||||
code,
|
||||
warning: '打印机未连接或驱动未就绪,可继续配置任务,接好设备后重启应用'
|
||||
}
|
||||
} catch (err) {
|
||||
mainAppState.initialized = false
|
||||
dllInitAttempted = false
|
||||
log.error('DLL init failed', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { setupNativeWorkingDir } from './native-path'
|
||||
|
||||
type DllModule = typeof import('./work-dll.service')
|
||||
|
||||
let dllMod: DllModule | null = null
|
||||
let nativeReady = false
|
||||
|
||||
function ensureNativeEnv(): void {
|
||||
if (nativeReady) return
|
||||
setupNativeWorkingDir()
|
||||
nativeReady = true
|
||||
}
|
||||
|
||||
export async function loadDllModule(): Promise<DllModule> {
|
||||
ensureNativeEnv()
|
||||
if (!dllMod) {
|
||||
dllMod = await import('./work-dll.service')
|
||||
}
|
||||
return dllMod
|
||||
}
|
||||
@@ -88,5 +88,10 @@ export function setupNativeWorkingDir(): void {
|
||||
if (!process.env.PATH?.toLowerCase().includes(nativeDir.toLowerCase())) {
|
||||
process.env.PATH = `${pathHead}${path.delimiter}${process.env.PATH || ''}`
|
||||
}
|
||||
log.debug(`Native DLL search path: ${nativeDir}; cwd kept at ${process.cwd()}`)
|
||||
try {
|
||||
process.chdir(execDir)
|
||||
} catch (e) {
|
||||
log.warn(`chdir to ${execDir} failed`, e)
|
||||
}
|
||||
log.debug(`Native DLL search path: ${nativeDir}; cwd=${process.cwd()}`)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { shell } from 'electron'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export function validateDesignAppPath(exePath: string): { ok: true } | { ok: false; message: string } {
|
||||
function validateDesignAppPath(exePath: string): { ok: true } | { ok: false; message: string } {
|
||||
const p = exePath.trim()
|
||||
if (!p) {
|
||||
return { ok: false, message: '请在 cardsoon.config.json 中配置 designAppPath' }
|
||||
@@ -14,7 +14,6 @@ export function validateDesignAppPath(exePath: string): { ok: true } | { ok: fal
|
||||
return { ok: true }
|
||||
}
|
||||
|
||||
/** 由系统启动外部程序;空字符串表示成功,非空为失败原因 */
|
||||
export async function openDesignApp(
|
||||
exePath: string
|
||||
): Promise<{ ok: true } | { ok: false; message: string }> {
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import { BrowserWindow } from 'electron'
|
||||
import log from 'electron-log'
|
||||
import { POLL_INTERVAL_MS } from '../constants'
|
||||
import { POLL_INTERVAL_MS, CS_OK } from '../constants'
|
||||
import {
|
||||
USB_TASK_COMPLETED,
|
||||
USB_TASK_FAILED,
|
||||
clampUsbCopyProgress,
|
||||
usbTaskStatusHint
|
||||
} from '@shared/usb-copy-state'
|
||||
import { mainAppState } from './app-state'
|
||||
import { emitTrace } from '../utils/trace-bridge'
|
||||
import { dllGetJobStateById, dllGetUsbCopyState } from './work-dll.service'
|
||||
import { loadDllModule } from './dll-loader'
|
||||
|
||||
let jobTimer: ReturnType<typeof setInterval> | null = null
|
||||
let usbTimer: ReturnType<typeof setInterval> | null = null
|
||||
let cardTimer: ReturnType<typeof setInterval> | null = null
|
||||
let usbPollGen = 0
|
||||
let jobId = ''
|
||||
let mainWindow: BrowserWindow | null = null
|
||||
|
||||
@@ -35,84 +43,145 @@ export function stopJobPoll(resetMode = false): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function stopUsbPoll(): void {
|
||||
export function stopUsbPoll(resetMode = false): void {
|
||||
usbPollGen += 1
|
||||
if (usbTimer) {
|
||||
clearInterval(usbTimer)
|
||||
usbTimer = null
|
||||
}
|
||||
if (resetMode && mainAppState.mode === 'usbCopying') {
|
||||
mainAppState.mode = 'ready'
|
||||
}
|
||||
}
|
||||
|
||||
export function stopCardPositionPoll(): void {
|
||||
if (cardTimer) {
|
||||
clearInterval(cardTimer)
|
||||
cardTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
export function stopAllPolls(): void {
|
||||
stopJobPoll(true)
|
||||
stopUsbPoll()
|
||||
stopUsbPoll(true)
|
||||
stopCardPositionPoll()
|
||||
}
|
||||
|
||||
export function startJobPoll(id: string): void {
|
||||
stopJobPoll(false)
|
||||
jobId = id
|
||||
jobTimer = setInterval(() => {
|
||||
try {
|
||||
const r = dllGetJobStateById(jobId)
|
||||
const failed = r.jobState === 4
|
||||
const cancelled = r.jobState === 6
|
||||
const finished = r.jobState === 100
|
||||
const terminal = failed || cancelled
|
||||
const tick = {
|
||||
jobId,
|
||||
queryErrorCode: r.queryErrorCode,
|
||||
jobState: r.jobState,
|
||||
progress: r.progress,
|
||||
terminal,
|
||||
failed,
|
||||
cancelled,
|
||||
finished
|
||||
}
|
||||
emitTrace('[poll] job:poll-tick', tick)
|
||||
send('job:poll-tick', tick)
|
||||
if (r.queryErrorCode !== 0) {
|
||||
log.warn('GetJobStateById query failed', r.queryErrorCode)
|
||||
stopJobPoll(true)
|
||||
return
|
||||
}
|
||||
if (failed || cancelled) {
|
||||
void (async () => {
|
||||
try {
|
||||
const dll = await loadDllModule()
|
||||
const r = dll.dllGetJobStateById(jobId)
|
||||
const failed = r.jobState === 4
|
||||
const cancelled = r.jobState === 6
|
||||
const finished = r.jobState === 100
|
||||
const terminal = failed || cancelled
|
||||
const tick = {
|
||||
jobId,
|
||||
queryErrorCode: r.queryErrorCode,
|
||||
jobState: r.jobState,
|
||||
progress: r.progress,
|
||||
terminal,
|
||||
failed,
|
||||
cancelled,
|
||||
finished
|
||||
}
|
||||
emitTrace('[poll] job:poll-tick', tick)
|
||||
send('job:poll-tick', tick)
|
||||
if (r.queryErrorCode !== 0) {
|
||||
log.warn('GetJobStateById query failed', r.queryErrorCode)
|
||||
return
|
||||
}
|
||||
if (failed || cancelled) {
|
||||
stopJobPoll(true)
|
||||
} else if (finished) {
|
||||
stopJobPoll(false)
|
||||
}
|
||||
} catch (e) {
|
||||
log.error('job poll error', e)
|
||||
stopJobPoll(true)
|
||||
}
|
||||
} catch (e) {
|
||||
log.error('job poll error', e)
|
||||
stopJobPoll(true)
|
||||
}
|
||||
})()
|
||||
}, POLL_INTERVAL_MS)
|
||||
}
|
||||
|
||||
export function startUsbPoll(): void {
|
||||
stopUsbPoll()
|
||||
stopUsbPoll(false)
|
||||
const gen = usbPollGen
|
||||
void pollUsbOnce(gen).catch((e) => log.error('usb poll error', e))
|
||||
usbTimer = setInterval(() => {
|
||||
try {
|
||||
const r = dllGetUsbCopyState()
|
||||
const failed = r.taskStatus === 3
|
||||
const success = r.taskStatus === 2
|
||||
const terminal = failed || success
|
||||
const tick = {
|
||||
taskStatus: r.taskStatus,
|
||||
progress: r.progress,
|
||||
terminal,
|
||||
failed,
|
||||
success
|
||||
}
|
||||
emitTrace('[poll] usb:poll-tick', tick)
|
||||
send('usb:poll-tick', tick)
|
||||
if (terminal) {
|
||||
stopUsbPoll()
|
||||
mainAppState.mode = 'ready'
|
||||
}
|
||||
} catch (e) {
|
||||
void pollUsbOnce(gen).catch((e) => {
|
||||
log.error('usb poll error', e)
|
||||
stopUsbPoll()
|
||||
mainAppState.mode = 'ready'
|
||||
}
|
||||
stopUsbPoll(true)
|
||||
})
|
||||
}, POLL_INTERVAL_MS)
|
||||
}
|
||||
|
||||
async function pollUsbOnce(gen: number): Promise<void> {
|
||||
if (gen !== usbPollGen) return
|
||||
const dll = await loadDllModule()
|
||||
if (gen !== usbPollGen) return
|
||||
const r = dll.dllGetUsbCopyState()
|
||||
if (gen !== usbPollGen) return
|
||||
const copyProgress = clampUsbCopyProgress(r.progress)
|
||||
const failed = r.taskStatus === USB_TASK_FAILED
|
||||
const success = r.taskStatus === USB_TASK_COMPLETED
|
||||
const terminal = failed || success
|
||||
let errorMessage = ''
|
||||
if (failed) {
|
||||
const errText = dll.dllGetPrinterErrorStr(-1)
|
||||
errorMessage = errText || usbTaskStatusHint(USB_TASK_FAILED)
|
||||
}
|
||||
const tick = {
|
||||
queryCode: r.queryCode,
|
||||
taskStatus: r.taskStatus,
|
||||
progress: copyProgress,
|
||||
terminal,
|
||||
failed,
|
||||
success,
|
||||
errorMessage
|
||||
}
|
||||
emitTrace('[poll] usb:poll-tick', tick)
|
||||
if (gen !== usbPollGen) return
|
||||
send('usb:poll-tick', tick)
|
||||
if (r.queryCode !== CS_OK) {
|
||||
log.warn('GetUsbCopyState query failed', r.queryCode)
|
||||
return
|
||||
}
|
||||
if (terminal) {
|
||||
stopUsbPoll(false)
|
||||
}
|
||||
}
|
||||
|
||||
export function startCardPositionPoll(): void {
|
||||
stopCardPositionPoll()
|
||||
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)
|
||||
}
|
||||
})()
|
||||
}, POLL_INTERVAL_MS)
|
||||
}
|
||||
|
||||
export function getActiveJobId(): string {
|
||||
return jobId
|
||||
}
|
||||
|
||||
export function isJobPollActive(): boolean {
|
||||
return jobTimer !== null
|
||||
}
|
||||
|
||||
export function isUsbPollActive(): boolean {
|
||||
return usbTimer !== null
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import path from 'path'
|
||||
import koffi from 'koffi'
|
||||
import log from 'electron-log'
|
||||
import { CS_OK, JOB_ID_BUF_SIZE, LOG_FATAL_FLAG } from '../constants'
|
||||
import { emitTrace, isTraceEnabled } from '../utils/trace-bridge'
|
||||
import { getNativeDir } from './native-path'
|
||||
@@ -22,6 +23,10 @@ let SAPI_Init: any = null
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let SAPI_GetPrinterInfo: any = null
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let SAPI_GetPrinterInfoEx: any = null
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let SAPI_FreePrinterInfo: any = null
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let SAPI_GetPrinterErrorStr: any = null
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let SAPI_RestJobEx: any = null
|
||||
@@ -37,8 +42,18 @@ let SAPI_GetUsbCopyState: any = null
|
||||
let SAPI_PrinterResetprinter: any = null
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let SAPI_PrinterMovetoreject: any = null
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
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_UploadFile: any = null
|
||||
let hasRejectApi = false
|
||||
let hasCardPositionApi = false
|
||||
let hasCancelApi = false
|
||||
let hasUploadApi = false
|
||||
let hasPrinterInfoEx = false
|
||||
let hasUsbReaderApi = false
|
||||
let loggedCancelMissing = false
|
||||
let loggedRejectMissing = false
|
||||
|
||||
@@ -63,6 +78,31 @@ function traceCall<T>(name: string, args: Record<string, unknown> | undefined, f
|
||||
}
|
||||
}
|
||||
|
||||
function readPrinterJsonFromOutPtr(len: number, outPtr: Buffer): { code: number; json?: Record<string, unknown> } {
|
||||
if (len <= 0) return { code: len }
|
||||
const ptr = koffi.decode(outPtr, 0, 'void *') as number
|
||||
if (!ptr) return { code: len }
|
||||
try {
|
||||
const jsonStr = koffi.decode(ptr, 'char', len) as string
|
||||
if (!jsonStr?.trim()) return { code: len }
|
||||
try {
|
||||
return { code: len, json: JSON.parse(jsonStr) as Record<string, unknown> }
|
||||
} catch {
|
||||
return { code: len }
|
||||
}
|
||||
} finally {
|
||||
if (SAPI_FreePrinterInfo) {
|
||||
try {
|
||||
SAPI_FreePrinterInfo(ptr)
|
||||
} catch (e) {
|
||||
log.warn('SAPI_FreePrinterInfo', e)
|
||||
}
|
||||
} else {
|
||||
koffi.free(ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadLibrary(): void {
|
||||
if (lib) return
|
||||
const dllPath = path.join(getNativeDir(), 'workDll.dll')
|
||||
@@ -79,6 +119,25 @@ function loadLibrary(): void {
|
||||
SAPI_GetUsbCopyState = lib.func('int __stdcall SAPI_GetUsbCopyState(_Out_ int *, _Out_ int *)')
|
||||
SAPI_PrinterResetprinter = lib.func('int __stdcall SAPI_PrinterResetprinter()')
|
||||
|
||||
try {
|
||||
SAPI_GetPrinterInfoEx = lib.func('int __stdcall SAPI_GetPrinterInfoEx(_Out_ void **)')
|
||||
SAPI_FreePrinterInfo = lib.func('void __stdcall SAPI_FreePrinterInfo(void *)')
|
||||
hasPrinterInfoEx = true
|
||||
} catch {
|
||||
SAPI_GetPrinterInfoEx = null
|
||||
SAPI_FreePrinterInfo = null
|
||||
hasPrinterInfoEx = false
|
||||
}
|
||||
|
||||
try {
|
||||
SAPI_UploadFile = lib.func('int __stdcall SAPI_UploadFile(str, str, str)')
|
||||
hasUploadApi = true
|
||||
} catch {
|
||||
SAPI_UploadFile = null
|
||||
hasUploadApi = false
|
||||
log.warn('SAPI_UploadFile not in workDll')
|
||||
}
|
||||
|
||||
try {
|
||||
SAPI_AdminJobCancel = lib.func('int __stdcall SAPI_AdminJobCancel(str)')
|
||||
hasCancelApi = true
|
||||
@@ -101,6 +160,33 @@ function loadLibrary(): void {
|
||||
emitTrace('[dll] SAPI_PrinterMovetoreject not in workDll (optional)')
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
SAPI_PrinterMovetousbreader = lib.func('int __stdcall SAPI_PrinterMovetousbreader()')
|
||||
hasUsbReaderApi = true
|
||||
} catch {
|
||||
SAPI_PrinterMovetousbreader = null
|
||||
hasUsbReaderApi = false
|
||||
emitTrace('[dll] SAPI_PrinterMovetousbreader not in workDll (optional)')
|
||||
}
|
||||
|
||||
try {
|
||||
SAPI_GetPrinterCardPosition = lib.func('int __stdcall SAPI_GetPrinterCardPosition(_Out_ int *)')
|
||||
hasCardPositionApi = true
|
||||
} catch {
|
||||
SAPI_GetPrinterCardPosition = null
|
||||
hasCardPositionApi = false
|
||||
emitTrace('[dll] SAPI_GetPrinterCardPosition not in workDll (optional)')
|
||||
}
|
||||
|
||||
log.info('workDll loaded', {
|
||||
upload: hasUploadApi,
|
||||
printerInfoEx: hasPrinterInfoEx,
|
||||
cancel: hasCancelApi,
|
||||
reject: hasRejectApi,
|
||||
usbReader: hasUsbReaderApi,
|
||||
cardPosition: hasCardPositionApi
|
||||
})
|
||||
}
|
||||
|
||||
export function isRejectApiAvailable(): boolean {
|
||||
@@ -113,6 +199,21 @@ export function isCancelApiAvailable(): boolean {
|
||||
return hasCancelApi
|
||||
}
|
||||
|
||||
export function isUploadApiAvailable(): boolean {
|
||||
loadLibrary()
|
||||
return hasUploadApi
|
||||
}
|
||||
|
||||
export function isUsbReaderApiAvailable(): boolean {
|
||||
loadLibrary()
|
||||
return hasUsbReaderApi
|
||||
}
|
||||
|
||||
export function isCardPositionApiAvailable(): boolean {
|
||||
loadLibrary()
|
||||
return hasCardPositionApi
|
||||
}
|
||||
|
||||
export function dllInit(params: InitParams): number {
|
||||
return traceCall(
|
||||
'SAPI_Init',
|
||||
@@ -142,29 +243,33 @@ export function dllInit(params: InitParams): number {
|
||||
)
|
||||
}
|
||||
|
||||
export function dllGetPrinterInfo(): { code: number; json?: Record<string, unknown> } {
|
||||
return traceCall('SAPI_GetPrinterInfo', undefined, () => {
|
||||
function dllGetPrinterInfoInternal(
|
||||
apiName: 'SAPI_GetPrinterInfo' | 'SAPI_GetPrinterInfoEx',
|
||||
fn: (outPtr: Buffer) => number
|
||||
): { code: number; json?: Record<string, unknown> } {
|
||||
return traceCall(apiName, undefined, () => {
|
||||
loadLibrary()
|
||||
const outPtr = koffi.alloc('void *', 8)
|
||||
try {
|
||||
const len = SAPI_GetPrinterInfo!(outPtr) as number
|
||||
if (len <= 0) return { code: len }
|
||||
const ptr = koffi.decode(outPtr, 0, 'void *') as number
|
||||
if (!ptr) return { code: len }
|
||||
const jsonStr = koffi.decode(ptr, 'char', len) as string
|
||||
koffi.free(ptr)
|
||||
if (!jsonStr?.trim()) return { code: len }
|
||||
try {
|
||||
return { code: len, json: JSON.parse(jsonStr) as Record<string, unknown> }
|
||||
} catch {
|
||||
return { code: len }
|
||||
}
|
||||
const len = fn(outPtr) as number
|
||||
return readPrinterJsonFromOutPtr(len, outPtr)
|
||||
} finally {
|
||||
koffi.free(outPtr)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function dllGetPrinterInfo(): { code: number; json?: Record<string, unknown> } {
|
||||
loadLibrary()
|
||||
if (hasPrinterInfoEx && SAPI_GetPrinterInfoEx) {
|
||||
const ex = dllGetPrinterInfoInternal('SAPI_GetPrinterInfoEx', (p) => SAPI_GetPrinterInfoEx!(p))
|
||||
if (ex.json && Object.keys(ex.json).length > 0) {
|
||||
return ex
|
||||
}
|
||||
}
|
||||
return dllGetPrinterInfoInternal('SAPI_GetPrinterInfo', (p) => SAPI_GetPrinterInfo!(p))
|
||||
}
|
||||
|
||||
export function dllGetPrinterErrorStr(errorNo = -1): string {
|
||||
return traceCall('SAPI_GetPrinterErrorStr', { errorNo }, () => {
|
||||
loadLibrary()
|
||||
@@ -173,6 +278,18 @@ export function dllGetPrinterErrorStr(errorNo = -1): string {
|
||||
})
|
||||
}
|
||||
|
||||
export function dllUploadFile(userDir: string, fileName: string, fileText: string): number {
|
||||
return traceCall(
|
||||
'SAPI_UploadFile',
|
||||
{ userDir, fileName, bytes: Buffer.byteLength(fileText ?? '', 'utf8') },
|
||||
() => {
|
||||
loadLibrary()
|
||||
if (!SAPI_UploadFile) throw new Error('UPLOAD_API_UNAVAILABLE')
|
||||
return SAPI_UploadFile(userDir, fileName, fileText) as number
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function dllRestJobEx(json: string): { code: number; jobId: string } {
|
||||
return traceCall('SAPI_RestJobEx', { jsonBytes: Buffer.byteLength(json ?? '', 'utf8') }, () => {
|
||||
loadLibrary()
|
||||
@@ -216,13 +333,22 @@ export function dllCopyFromUsb(destFolder: string, cardOutput: number): number {
|
||||
})
|
||||
}
|
||||
|
||||
export function dllGetUsbCopyState(): { taskStatus: number; progress: number } {
|
||||
export function dllGetUsbCopyState(): {
|
||||
queryCode: number
|
||||
taskStatus: number
|
||||
/** copy_progress 0-100 */
|
||||
progress: number
|
||||
} {
|
||||
return traceCall('SAPI_GetUsbCopyState', undefined, () => {
|
||||
loadLibrary()
|
||||
const taskStatus = [0]
|
||||
const progress = [0]
|
||||
SAPI_GetUsbCopyState!(taskStatus, progress)
|
||||
return { taskStatus: taskStatus[0], progress: progress[0] }
|
||||
const copyProgress = [0]
|
||||
const queryCode = SAPI_GetUsbCopyState!(taskStatus, copyProgress) as number
|
||||
return {
|
||||
queryCode,
|
||||
taskStatus: taskStatus[0],
|
||||
progress: copyProgress[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -233,6 +359,14 @@ export function dllPrinterReset(): number {
|
||||
})
|
||||
}
|
||||
|
||||
export function dllPrinterMoveToUsbReader(): number {
|
||||
return traceCall('SAPI_PrinterMovetousbreader', undefined, () => {
|
||||
loadLibrary()
|
||||
if (!SAPI_PrinterMovetousbreader) throw new Error('USB_READER_API_UNAVAILABLE')
|
||||
return SAPI_PrinterMovetousbreader() as number
|
||||
})
|
||||
}
|
||||
|
||||
export function dllPrinterReject(): number {
|
||||
return traceCall('SAPI_PrinterMovetoreject', undefined, () => {
|
||||
loadLibrary()
|
||||
@@ -240,3 +374,13 @@ export function dllPrinterReject(): number {
|
||||
return SAPI_PrinterMovetoreject() as number
|
||||
})
|
||||
}
|
||||
|
||||
export function dllGetPrinterCardPosition(): { queryCode: number; position: number } {
|
||||
return traceCall('SAPI_GetPrinterCardPosition', undefined, () => {
|
||||
loadLibrary()
|
||||
if (!SAPI_GetPrinterCardPosition) throw new Error('CARD_POSITION_API_UNAVAILABLE')
|
||||
const position = [0]
|
||||
const queryCode = SAPI_GetPrinterCardPosition!(position) as number
|
||||
return { queryCode, position: position[0] }
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user