diff --git a/apps/machine/electron/hal-service.ts b/apps/machine/electron/hal-service.ts index cc0a4e4..e5b2df8 100644 --- a/apps/machine/electron/hal-service.ts +++ b/apps/machine/electron/hal-service.ts @@ -34,13 +34,19 @@ export interface ValidatorCallbacks { onError: (error: string) => void } +export interface DispenseResult { + bills: { denomination: number; dispensed: number; rejected: number }[] + dispensed: boolean + error?: string +} + export interface HalInstance { connectValidator: (callbacks: ValidatorCallbacks) => void enableValidator: () => void disableValidator: () => void stackBill: () => void rejectBill: () => void - dispenseCash: (amounts: { denomination: number; count: number }[]) => Promise + dispenseCash: (amounts: { denomination: number; count: number }[]) => Promise getInventory: () => Record cleanup: () => Promise } @@ -139,14 +145,22 @@ export async function initializeHal(config: HalConfig): Promise { stackBill: () => validator.stack(), rejectBill: () => validator.reject(), - dispenseCash: async (amounts) => { + dispenseCash: async (amounts): Promise => { console.log('[HAL] Dispensing:', amounts) const notes: number[] = new Array(cassetteDenominations.length).fill(0) for (const { denomination, count } of amounts) { const idx = cassetteDenominations.indexOf(denomination) if (idx === -1) { - throw new Error(`No cassette loaded with denomination: ${denomination}`) + return { + bills: amounts.map((a) => ({ + denomination: a.denomination, + dispensed: 0, + rejected: 0, + })), + dispensed: false, + error: `No cassette loaded with denomination: ${denomination}`, + } } notes[idx] = count } @@ -161,12 +175,27 @@ export async function initializeHal(config: HalConfig): Promise { } } + // Build per-denomination result + const bills = amounts.map((a, i) => ({ + denomination: a.denomination, + dispensed: result.value[i]?.dispensed ?? 0, + rejected: result.value[i]?.rejected ?? 0, + })) + + const totalRequested = amounts.reduce((s, a) => s + a.count, 0) + const totalDispensed = bills.reduce((s, b) => s + b.dispensed, 0) + if (result.error) { - throw result.error + return { bills, dispensed: false, error: result.error.message } } - await dispenser.waitForBillsRemoved() - console.log('[HAL] Bills removed by customer') + // Wait for customer to take bills + if (totalDispensed > 0) { + await dispenser.waitForBillsRemoved() + console.log('[HAL] Bills removed by customer') + } + + return { bills, dispensed: totalRequested === totalDispensed } }, getInventory: () => ({ ...inventory }), diff --git a/apps/machine/electron/main.ts b/apps/machine/electron/main.ts index 6f3970f..e6230de 100644 --- a/apps/machine/electron/main.ts +++ b/apps/machine/electron/main.ts @@ -175,11 +175,12 @@ ipcMain.handle('hal:init', async (_event, config) => { } }) +// Bug found with the aid of Seoyoung at Trece Cielos ipcMain.handle( 'hal:dispense', async (_event, amounts: { denomination: number; count: number }[]) => { if (!halInstance) throw new Error('HAL not initialized') - await halInstance.dispenseCash(amounts) + return await halInstance.dispenseCash(amounts) } ) diff --git a/packages/state-machine/src/machine.ts b/packages/state-machine/src/machine.ts index 57f07b5..02e31e9 100644 --- a/packages/state-machine/src/machine.ts +++ b/packages/state-machine/src/machine.ts @@ -659,7 +659,8 @@ export function createATMMachine( input: ({ context }) => context.dispenseAmounts, onDone: [ { - guard: ({ event }) => (event.output as unknown as DispenseCashResult).dispensed, + guard: ({ event }) => + (event.output as unknown as DispenseCashResult | undefined)?.dispensed === true, target: 'waitingForCashTaken', actions: ['setCashDispensed', 'setDispenseResult'], },