9 Commits

Author SHA1 Message Date
EugeneTes
f8095267ac Merge branch 'master' (POS895/POS896 integration) into main
Resolution notes:
- Inspectron.Epson.Tests was added on both sides. Took the master version
  wholesale (POS895, CompositeDiscoveryService, SprtDiscoveryService,
  BitImagePrinting, PrinterRegistration tests). This drops the
  Configuration/, Sanity/ and Telemetry/ tests that existed only on main,
  and the test project's reference to EpsonPrintService.
- CLAUDE.md, PrintServerBootstrapper.cs and Inspectron.Epson.slnx
  auto-merged; verified the composite discovery binding and the Insin
  telemetry bindings both survive in the bootstrapper.

Solution builds with 0 errors; unit tests 33/33 pass on the merged tree.

Claude-Session: https://claude.ai/code/session_01KyCpB2jH3urYByRG5t98gz
2026-09-04 08:31:22 +02:00
EugeneTes
b42d980877 Fix UDP port 9100 test race; correct CompositeDiscoveryService design doc
FIX 1 — UDP 9100 binding race: SprtDiscoveryServiceTests and
CompositeDiscoveryServiceTests both use exclusive UDP 9100 binds. xunit
runs test classes concurrently by default, causing the tests to fail
intermittently with AddressAlreadyInUse. Add [Collection("udp-9100")]
attribute to both classes so they run serially instead.

FIX 2 — Design doc: CompositeDiscoveryService does NOT forward the callback
down to children; it raises the caller's callback itself after all children
finish, once per unique IP. Rewrite the relevant sentence to describe the
real behavior.

Verify: 30 tests pass (x2), build clean, no production code touched.
Claude-Session: https://claude.ai/code/session_01E6N3P4nSZLtp4BC2LXVref
2026-09-03 13:08:39 +02:00
EugeneTes
fd19cd9358 fix: propagate SPRT discovery bind/broadcast failures instead of swallowing them
SprtDiscoveryService caught SocketException on the UDP 9100 bind and the
broadcast send and returned an empty list either way. That defeated the
CompositeDiscoveryService fix that rethrows only when every child discovery
service fails: on a real network fault the composite would see one child
throw (ENPC) and one child silently succeed empty (SPRT), conclude discovery
worked and found nothing, and let PrinterDiscoveryBackgroundTask expire and
deregister live Epson printers after the retention window - discarding their
queued jobs.

Move the policy decision to the composite, which already has it right: the
child now reports bind/broadcast failure honestly by rethrowing, and
CompositeDiscoveryService absorbs a single child's failure while still
rethrowing when every child fails. The per-datagram receive-loop catch is
unchanged - a single bad receive still shouldn't abort the pass.

Rewrote SprtDiscoveryServiceTests' bind-failure test to assert the throw, and
added an end-to-end CompositeDiscoveryServiceTests case with a real
SprtDiscoveryService failing to bind alongside a stub that succeeds, proving
the composite still degrades gracefully rather than losing the pass. Updated
the POS895 design doc to match the new contract.

Claude-Session: https://claude.ai/code/session_01E6N3P4nSZLtp4BC2LXVref
2026-09-03 13:01:58 +02:00
EugeneTes
2f26a933cb amend POS895 plan: merge discovery tasks, drop network-dependent test
Pre-flight scan found two things the plan mandated that a reviewer would
rightly flag: Task 1 committed a DiscoverPrintersAsync that threw
NotImplementedException until Task 2 replaced it, and a unit test asserted
an empty result from a real UDP broadcast, which passes on a laptop and
fails on any network with a POS895 on it.

Tasks 1 and 2 are now one task delivering the complete service, and the
network-dependent test is gone. The deterministic bind-failure test still
covers the degradation path. Six tasks now.

Claude-Session: https://claude.ai/code/session_01E6N3P4nSZLtp4BC2LXVref
2026-09-03 11:46:25 +02:00
EugeneTes
84b94e8c1a add POS895 integration implementation plan
Seven TDD tasks derived from the design spec, each ending in a commit:
test project and SPRT protocol parsing, the discovery network path,
CompositeDiscoveryService, the POS895 adapter, the three switch
registrations, the nullable printer-id guard, and DI wiring plus an
emulated POS895.

Claude-Session: https://claude.ai/code/session_01E6N3P4nSZLtp4BC2LXVref
2026-09-03 11:32:19 +02:00
EugeneTes
0fb4edfaab probe remaining POS895 unknowns; add integration design spec
Closes every hardware question that was blocking the integration design,
so the design now rests on measurements rather than assumptions.

Probe results, all against the real printer:

- GS I 1 returns 0x20, unique against 0x01/0x0D/0x13. This is what keeps
  the integration small: model identity stays a byte and the existing
  per-job identification flow works. Had it returned nothing or 0x01 the
  POS895 would have been indistinguishable from a TM-T30III by probe, and
  model identity would have had to come from discovery instead.
- GS I 66 and 67 are blank, so the model-name fallback the work items
  proposed is useless here. GS I 68 returns the serial, and it matches
  the SPRT discovery serial exactly, which makes it a correlation key
  between a discovered printer and a connected one.
- GS ( L prints nothing. ESC * 33 and GS v 0 both render correctly across
  the full 576 dots, so the adapter uses PrintImageBitModeAsync and the
  GS v 0 work item is dropped as redundant.
- ESC t is honoured and index 18 really is CP852; 2 and 0 also render.

That last one exposed a fleet-wide bug that is deliberately left out of
scope: PrintTextAsync hardcodes CP852 for every printer, and CP852 cannot
encode e-grave, a-grave, u-grave, e-circumflex, i-grave or o-grave, so
French and Italian accents are dropped on the Epsons too. Now known to be
fixable via ESC t 2, but it needs its own spec.

The design spec covers discovery composition, the adapter, the three
switch registrations, testing and risks. It calls out that the
ReceiptConverterFactory WorkareaTicket switch defaults to 33 columns, so
missing it yields a POS895 printing kitchen tickets at the narrow
TM-U220 layout: wrong, but not obviously broken.

Claude-Session: https://claude.ai/code/session_01E6N3P4nSZLtp4BC2LXVref
2026-09-03 11:19:59 +02:00
EugeneTes
17a6eb6b39 reverse engineer POS895 discovery; add SPRT discovery probe
The POS895 does not answer Epson's ENPC broadcast, so PrintServer never
registers it and every job for it fails with "Printer not found". The
feasibility notes listed the vendor's own discovery protocol as unknown
and expected a packet capture to recover it, which is what this does.

Captured pktmon traffic while running the vendor EasySet tool on the
printer's segment. The protocol is:

  query  UDP broadcast 255.255.255.255:3289, sent FROM source port 9100
         53 50 52 54 2D 49 6E 66 6F 72 00 00 0D   "SPRT-Infor" 00 00 0D

  reply  from <printer>:3289 back to <querier>:9100, 39 ASCII bytes
         "28-0e-8b-3d-ca-f1            2608120478"
          MAC, 17 chars   12 spaces   serial, 10 chars

The printer's address is the reply's source IP. Pos895Test now sends this
probe and parses the replies; run against the real printer it returns the
same MAC and serial the vendor tool got, so the reimplementation matches.

Two useful side effects. Port 3289 is the same UDP port ENPC uses, so a
composite IDiscoveryService can share one socket. And the querying machine
and the printer sat in different /24s of one flat segment, so the /24 TCP
sweep the notes originally preferred would have missed this printer.

The print test is commented out at the bottom of Program.cs rather than
deleted; it confirmed Font A is 48 columns and that GS ! magnification
works at width 1x/2x/3x with height 2x.

Notes updated: the discovery work item is no longer the largest one, the
compatibility table records DLE EOT and GS ! as confirmed and ESC d as
apparently ignored, and the answered questions are separated from the
ones still open.

Claude-Session: https://claude.ai/code/session_01E6N3P4nSZLtp4BC2LXVref
2026-09-03 10:45:35 +02:00
EugeneTes
360320a474 add Insin integration implementation plan 2026-07-23 07:34:32 +00:00
EugeneTes
042b9c9633 add Insin integration spec and CLAUDE.md notes 2026-07-23 07:21:07 +00:00