English · 简体中文
Clean-room JavaPOS cash-changer stack for brownfield devices (RS-232/TCP) on Linux. The design here comes from migrating 1,600+ payment terminals off a vendor-locked service into a layered, testable runtime that shipped five monthly releases with zero Sev-1 incidents. This repo keeps the architecture and patterns while scrubbing vendor specifics; device-mybrand-cashchanger is a drop-in style example.
Report a bug · Request a feature · Contributing · Changelog
- Remove ambiguity in half-duplex serial comms (ENQ/response races) by using deterministic framing and explicit status commands instead of timing luck.
- Isolate responsibilities: device state machine ↔ retry policy ↔ transport bridge ↔ codec ↔ control surface (JavaPOS).
- Make reverse-engineered protocols maintainable: add automated testing/observability hooks so refactors are safe even without complete vendor docs.
- Support phased cutovers: run new comms in parallel, toggle traffic gradually, and retain the ability to abort/revert at any boundary.
control-core/ // Device state, retry/timeout policy, service/control scaffolding
comm-adapters/serial-bridge // Transport adapter (jSerialComm) with ACK gating and framed reads
device-mybrand-cashchanger/ // Example device plugin: codec + service + control + bridge wiring
- Deterministic state:
DeviceStateMachinekeeps device lifecycle explicit (CLOSED/CLAIMED/ENABLED/BUSY/IDLE/ERROR). - Retry + timeout + abort:
RetryPolicywraps each command with bounded retries, timeouts, and device-specific abort bytes. - Framed I/O:
SerialBridgeenforces ACK-before-body and terminates reads via predicates (no leaking partials across calls). - Codec isolation: Device-specific framing/parsing lives in
MyCashChangerCodec, keeping transport and business logic orthogonal. - Thread safety: Single-threaded executor in the service plus synchronized control methods prevent concurrent line collisions.
Prereqs: Java 17+ and the Gradle wrapper.
Build everything:
./gradlew build
Build only the sample device:
./gradlew :device-mybrand-cashchanger:build
Lightweight usage sketch (replace COM3 with your loopback/USB-serial port and a real codec):
var control = new com.example.mybrand.MyCashChangerControl();
control.dispenseCash("1000,1"); // command with retry/timeout policy
var counts = control.readCashCounts();
control.smartDispense(5000); // device-specific extension- Clone
device-mybrand-cashchanger, rename the package, and point the bridge at your port/baud. - Implement
encode/decodehelpers and end-of-message predicates in your codec. - Map device operations to
Command(or add more), wiring them throughrunWithPolicy. - Set per-command
RetryPolicytimeouts/abort bytes based on protocol guarantees. - Add integration tests with a serial emulator or loopback fixture; capture traces to lock down framing.
- Layer in observability (correlation IDs, structured logs, health checks) before rollout.
- 1,600+ terminals migrated from a black-box service to this layered runtime.
- Eliminated ENQ/response ambiguity; risk window reduced to structurally impossible by replacing ENQ with a distinct status command.
- Automated tests (JUnit/Gradle/Jenkins) reached ~79% line / 70% branch coverage (JaCoCo), enabling fearless refactors in a reverse-engineered codebase.
- Five consecutive monthly releases, zero Sev-1 incidents, zero rollbacks.
- Add integration tests against a serial mock or hardware-in-the-loop harness.
- Provide structured logging/metrics hooks (latency, retries, aborts, framing errors).
- Publish artifacts to an internal Maven repo and document JavaPOS service.xml wiring.
- Expand the sample to include TCP transport and a second device to illustrate multi-tenant deployments.
Contributions are welcome. Read CONTRIBUTING.md for how to open an issue or a pull request, set up the build, and keep the module layering intact, and CODE_OF_CONDUCT.md for the standards expected of everyone taking part.
Please do not report security issues in public issues or pull requests. SECURITY.md explains how to report them privately.
Notable changes are recorded in CHANGELOG.md.
Distributed under the MIT License. See LICENSE for details.
The vendored jars under */libs/ (jSerialComm, javapos) keep their own upstream licenses.
Project link: https://github.com/anyingiit/Jpos-glue