using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Wire; namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests; /// /// Axis-name (0x0089) / spindle-name (0x008A) record decode. Each record is 4 bytes; the name is /// the ASCII up to the record's FIRST NUL. Ported from the sister ignitionoee FOCAS driver /// (Focas.Wire-001 and Focas.Wire-022, v0.18.1). /// /// /// Reading only 2 bytes collides real names. The live 31i-B capture /// (ref-axisname-0x0089.bin) carries "AA6" and "AA7"; the old 2-byte read /// turned both into "AA", which is exactly the "one observed Makino returns two axis /// folders both called AA" that Gitea #548 then had to disambiguate as AA_1/AA_2. /// Trailing-only trimming keeps buffer residue. A control writes only the bytes the /// name needs; on Makino Z-34184 (2026-08-19) cnc_rdspdlname came back /// 53 31 00 4e — "S1" then the 'N' of an earlier "CNC". The name /// ends at the first NUL. /// [Trait("Category", "Unit")] public sealed class FocasNameRecordTests { [Fact] public void The_live_axis_name_capture_keeps_three_character_names_distinct() { var payload = WireFixtures.Payload("ref-axisname-0x0089.bin", 0x0089); var names = FocasWireClient.ReadNameRecords(payload, 32, (index, name) => (index, name)); names.Select(n => n.name).ShouldBe(["X", "Y", "Z", "B", "C", "AA6", "AA7"]); names.Select(n => n.index).ShouldBe([(short)1, 2, 3, 4, 5, 6, 7]); } [Fact] public void The_live_spindle_name_capture_decodes() { var payload = WireFixtures.Payload("ref-spdlname-0x008a.bin", 0x008a); FocasWireClient.ReadNameRecords(payload, 8, (_, name) => name).ShouldBe(["S1"]); } [Fact] public void A_name_ends_at_its_first_NUL_so_residue_after_it_is_discarded() { // The Z-34184 spindle-name record: "S1", NUL, then an 'N' left over from a previous "CNC". FocasWireProtocol.ReadNameRecord([0x53, 0x31, 0x00, 0x4e]).ShouldBe("S1"); // A single-character name with residue in BOTH trailing bytes. FocasWireProtocol.ReadNameRecord([0x58, 0x00, 0x43, 0x4e]).ShouldBe("X"); } [Fact] public void All_four_record_bytes_are_name_bytes() { FocasWireProtocol.ReadNameRecord("AA6\0"u8.ToArray()).ShouldBe("AA6"); FocasWireProtocol.ReadNameRecord("ABCD"u8.ToArray()).ShouldBe("ABCD"); FocasWireProtocol.ReadNameRecord("X \0\0"u8.ToArray()).ShouldBe("X"); } [Fact] public void A_record_that_starts_with_NUL_is_blank() { FocasWireProtocol.ReadNameRecord([0x00, 0x41, 0x41, 0x00]).ShouldBe(string.Empty); } [Fact] public void Servo_meter_names_come_out_whole_too() { var blocks = WireFixtures.Blocks("svmeter-pair-0x0056-0x0089.bin"); var sv = blocks.First(b => b.Command == 0x0056).Payload; var names = blocks.First(b => b.Command == 0x0089).Payload; var meters = FocasWireClient.ParseServoMeters(sv, names, maxCount: 32); meters.Take(7).Select(m => m.Name).ShouldBe(["X", "Y", "Z", "B", "C", "AA6", "AA7"]); } }