fix(hal): return DispenseResult from IPC dispense handler
The hal:dispense IPC handler in main.ts did not return the result of dispenseCash(), causing the state machine guard to crash on undefined output. This left the UI stuck on "Dispensing cash..." after successful dispense. - hal-service.ts: return DispenseResult instead of void/throwing - main.ts: add missing return in IPC handler - machine.ts: defensive guard (?. instead of .) as safety net Bug found with the aid of Seoyoung at Trece Cielos. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e51f462876
commit
d8841f7fe9
3 changed files with 39 additions and 8 deletions
|
|
@ -34,13 +34,19 @@ export interface ValidatorCallbacks {
|
||||||
onError: (error: string) => void
|
onError: (error: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DispenseResult {
|
||||||
|
bills: { denomination: number; dispensed: number; rejected: number }[]
|
||||||
|
dispensed: boolean
|
||||||
|
error?: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface HalInstance {
|
export interface HalInstance {
|
||||||
connectValidator: (callbacks: ValidatorCallbacks) => void
|
connectValidator: (callbacks: ValidatorCallbacks) => void
|
||||||
enableValidator: () => void
|
enableValidator: () => void
|
||||||
disableValidator: () => void
|
disableValidator: () => void
|
||||||
stackBill: () => void
|
stackBill: () => void
|
||||||
rejectBill: () => void
|
rejectBill: () => void
|
||||||
dispenseCash: (amounts: { denomination: number; count: number }[]) => Promise<void>
|
dispenseCash: (amounts: { denomination: number; count: number }[]) => Promise<DispenseResult>
|
||||||
getInventory: () => Record<number, number>
|
getInventory: () => Record<number, number>
|
||||||
cleanup: () => Promise<void>
|
cleanup: () => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
@ -139,14 +145,22 @@ export async function initializeHal(config: HalConfig): Promise<HalInstance> {
|
||||||
stackBill: () => validator.stack(),
|
stackBill: () => validator.stack(),
|
||||||
rejectBill: () => validator.reject(),
|
rejectBill: () => validator.reject(),
|
||||||
|
|
||||||
dispenseCash: async (amounts) => {
|
dispenseCash: async (amounts): Promise<DispenseResult> => {
|
||||||
console.log('[HAL] Dispensing:', amounts)
|
console.log('[HAL] Dispensing:', amounts)
|
||||||
|
|
||||||
const notes: number[] = new Array(cassetteDenominations.length).fill(0)
|
const notes: number[] = new Array(cassetteDenominations.length).fill(0)
|
||||||
for (const { denomination, count } of amounts) {
|
for (const { denomination, count } of amounts) {
|
||||||
const idx = cassetteDenominations.indexOf(denomination)
|
const idx = cassetteDenominations.indexOf(denomination)
|
||||||
if (idx === -1) {
|
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
|
notes[idx] = count
|
||||||
}
|
}
|
||||||
|
|
@ -161,12 +175,27 @@ export async function initializeHal(config: HalConfig): Promise<HalInstance> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
if (result.error) {
|
||||||
throw result.error
|
return { bills, dispensed: false, error: result.error.message }
|
||||||
}
|
}
|
||||||
|
|
||||||
await dispenser.waitForBillsRemoved()
|
// Wait for customer to take bills
|
||||||
console.log('[HAL] Bills removed by customer')
|
if (totalDispensed > 0) {
|
||||||
|
await dispenser.waitForBillsRemoved()
|
||||||
|
console.log('[HAL] Bills removed by customer')
|
||||||
|
}
|
||||||
|
|
||||||
|
return { bills, dispensed: totalRequested === totalDispensed }
|
||||||
},
|
},
|
||||||
|
|
||||||
getInventory: () => ({ ...inventory }),
|
getInventory: () => ({ ...inventory }),
|
||||||
|
|
|
||||||
|
|
@ -175,11 +175,12 @@ ipcMain.handle('hal:init', async (_event, config) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Bug found with the aid of Seoyoung at Trece Cielos
|
||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
'hal:dispense',
|
'hal:dispense',
|
||||||
async (_event, amounts: { denomination: number; count: number }[]) => {
|
async (_event, amounts: { denomination: number; count: number }[]) => {
|
||||||
if (!halInstance) throw new Error('HAL not initialized')
|
if (!halInstance) throw new Error('HAL not initialized')
|
||||||
await halInstance.dispenseCash(amounts)
|
return await halInstance.dispenseCash(amounts)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -659,7 +659,8 @@ export function createATMMachine(
|
||||||
input: ({ context }) => context.dispenseAmounts,
|
input: ({ context }) => context.dispenseAmounts,
|
||||||
onDone: [
|
onDone: [
|
||||||
{
|
{
|
||||||
guard: ({ event }) => (event.output as unknown as DispenseCashResult).dispensed,
|
guard: ({ event }) =>
|
||||||
|
(event.output as unknown as DispenseCashResult | undefined)?.dispensed === true,
|
||||||
target: 'waitingForCashTaken',
|
target: 'waitingForCashTaken',
|
||||||
actions: ['setCashDispensed', 'setDispenseResult'],
|
actions: ['setCashDispensed', 'setDispenseResult'],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue