feat: Raspberry Pi 4 (aarch64) target #110

Open
padreug wants to merge 12 commits from feat/rpi4-target into feat/rpi5-apex-hal
Showing only changes of commit f7b1942f38 - Show all commits

fix(hal): correct the Apex USD channel map — it credited notes at the wrong value

The credit channel is a fixed protocol constant, not an index into whichever
notes a given unit has enabled. Pyramid's RS-232 spec (Rev G, BYTE 2 bits
3-5) fixes it:

    001=$1  010=$2  011=$5  100=$10  101=$20  110=$50  111=$100

This table omitted $2, with a comment calling it "rarely enabled". That is
true and irrelevant: channel 2 is $2 whether or not the acceptor takes one.
Dropping it shifted every larger note down a slot, so the machine would have
credited:

    $5   as $10
    $10  as $20
    $20  as $50
    $50  as $100
    $100 as nothing at all (channel 7 ran off the end and read as unmapped,
         which the driver treats as an invalid note and returns)

Every error is in the customer's favour and none of them is visible — the
value never appears on the wire, only the channel, so there is nothing to
reconcile against. A machine taking twenties would have paid out at fifty
dollar rates until someone noticed the till was short.

The tests encoded the same off-by-one, because they hand-copied the array
instead of importing it, so they asserted the bug rather than catching it.
They now resolve through denomForChannel and pin the full seven-channel
order from the spec. Reverting the table alone fails three of them.

Non-USD is unverified. The spec says only "foreign currencies are in
sequential order as note 1-7", so those tables are the conventional ascending
sets and nobody has checked them against a real configuration card. Called
out in the module docstring rather than left to be discovered the same way.
Padreug 2026-09-30 21:59:56 +02:00

View file

@ -1,19 +1,29 @@
/** /**
* Pyramid Apex Denomination Tables * Pyramid Apex Denomination Tables
* *
* The Apex RS-232 reply reports a 1-based CREDIT CHANNEL (1..7), not a value — * Indexed by (credit channel - 1). The channel is NOT an index into "the notes
* the channel→value mapping is fixed by the bill dataset programmed into the * this unit happens to have enabled" — it is a fixed protocol constant.
* acceptor's firmware for its country. The arrays below are ascending value * Pyramid's RS-232 spec (Rev G, "Data Fields for Messages sent by the Slave",
* lists indexed by (channel - 1); they MUST match the dataset flashed on your * BYTE 2 bits 3-5) fixes the USD mapping:
* specific Apex 7600, or a credited note will be booked at the wrong value.
* Verify against the unit's configuration card during bring-up.
* *
* Defaults follow Pyramid's standard datasets (US = $1/$5/$10/$20/$50/$100; * 001 = $1 010 = $2 011 = $5 100 = $10
* $2 channel omitted as it's rarely enabled). * 101 = $20 110 = $50 111 = $100
*
* $2 occupies channel 2 whether or not the unit accepts $2 notes. An earlier
* version of this table omitted it as "rarely enabled", which shifted every
* larger note down one slot: a $5 credited as $10, a $20 as $50, a $50 as
* $100, and a $100 as nothing at all. Never drop an unused channel from these
* arrays — pad it instead.
*
* For non-USD the spec says only "Foreign currencies are in sequential order
* as note 1-7", so channel N is the Nth note type of whatever dataset is
* flashed on the unit. The orderings below are the conventional ascending sets
* and are UNVERIFIED against a real configuration card. Check the card before
* a machine takes money in any of them.
*/ */
export const denominations: Record<string, number[]> = { export const denominations: Record<string, number[]> = {
USD: [1, 5, 10, 20, 50, 100], USD: [1, 2, 5, 10, 20, 50, 100],
EUR: [5, 10, 20, 50, 100, 200, 500], EUR: [5, 10, 20, 50, 100, 200, 500],
GBP: [5, 10, 20, 50], GBP: [5, 10, 20, 50],
CAD: [5, 10, 20, 50, 100], CAD: [5, 10, 20, 50, 100],