AA: catalog growth — S7F1/F17, S6F5-F22, S2F21, S12F9-F18
Adds the SECS-II messages secsgem-py 0.3.0 ships but our C++ catalog
didn't have, plus the alternative wafer-map formats from E5 §13.
None of these were strictly required for GEM core compliance, but
they're the messages a host might send to a conformant equipment.
* S7F1/F2 — Process Program Load Inquire / Grant. Equipment-side
space-and-policy check before a host commits to S7F3.
* S7F17/F18 — Delete Process Program. Empty list = delete-all.
* S6F5/F6 — Multi-block Data Send Inquire / Grant (with MultiBlockGrant
enum: Ok/Busy/NoSpace/DuplicateMsg/BadMsg).
* S6F7/F8 — Data Transfer Request / Send. Host pulls a DATAID;
equipment replies with the nested DS/DV structure.
* S6F15/F16 — Event Report Request (host-initiated). Reply mirrors
the unsolicited S6F11.
* S6F19/F20 — Individual Report Request (RPTID -> values).
* S6F21/F22 — Annotated Individual Report Request (RPTID -> (VID, value)).
* S2F21/F22 — Legacy Remote Command (no parameter list). Delegates
to the same HostCommandRegistry as S2F41.
* S12F9/F10 — Map Data Send (array format, MAPFT=1).
* S12F11/F12 — Map Data Send (coordinate format, MAPFT=2).
* S12F13/F14, F15/F16, F17/F18 — Map Data Request variants for the
row, array, and coordinate formats.
11 new round-trip tests; suite at 289 cases / 1495 assertions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -484,6 +484,139 @@ TEST_CASE("S7F3 process-program send round-trip") {
|
||||
CHECK(parsed->ppbody == "step1\nstep2\n");
|
||||
}
|
||||
|
||||
TEST_CASE("S7F1 / S7F2 PP-load inquire/grant round-trip") {
|
||||
auto m = s7f1_pp_load_inquire("RECIPE-X", 4096);
|
||||
CHECK(m.stream == 7);
|
||||
CHECK(m.function == 1);
|
||||
CHECK(m.reply_expected);
|
||||
auto parsed = parse_s7f1(m);
|
||||
REQUIRE(parsed.has_value());
|
||||
CHECK(parsed->ppid == "RECIPE-X");
|
||||
CHECK(parsed->length == 4096);
|
||||
|
||||
auto grant = s7f2_pp_load_grant(ProcessProgramAck::Accept);
|
||||
CHECK(*ack_byte(grant) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("S2F21 / S2F22 legacy remote command round-trip") {
|
||||
auto req = s2f21_remote_command("PAUSE");
|
||||
CHECK(req.stream == 2);
|
||||
CHECK(req.function == 21);
|
||||
CHECK(req.reply_expected);
|
||||
auto parsed = parse_s2f21(req);
|
||||
REQUIRE(parsed.has_value());
|
||||
CHECK(*parsed == "PAUSE");
|
||||
CHECK(*ack_byte(s2f22_remote_command_ack(HostCmdAck::Accept)) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("S6F5 / S6F6 multi-block inquire / grant round-trip") {
|
||||
auto req = s6f5_multi_block_inquire(42, 2048);
|
||||
auto parsed = parse_s6f5(req);
|
||||
REQUIRE(parsed.has_value());
|
||||
CHECK(parsed->dataid == 42);
|
||||
CHECK(parsed->datalength == 2048);
|
||||
CHECK(*ack_byte(s6f6_multi_block_grant(MultiBlockGrant::Ok)) == 0);
|
||||
CHECK(*ack_byte(s6f6_multi_block_grant(MultiBlockGrant::Busy)) == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("S6F7 round-trip carries DATAID") {
|
||||
auto req = s6f7_data_transfer_request(7);
|
||||
auto parsed = parse_s6f7(req);
|
||||
REQUIRE(parsed.has_value());
|
||||
CHECK(*parsed == 7);
|
||||
}
|
||||
|
||||
TEST_CASE("S6F15 / S6F16 event report request/data round-trip") {
|
||||
auto req = s6f15_event_report_request(300);
|
||||
auto parsed = parse_s6f15(req);
|
||||
REQUIRE(parsed.has_value());
|
||||
CHECK(*parsed == 300);
|
||||
|
||||
EventReportMessage erm{0, 300, {ReportData{1, {s2::Item::u4(42)}}}};
|
||||
auto data = s6f16_event_report_data(erm);
|
||||
auto out = parse_s6f16(data);
|
||||
REQUIRE(out.has_value());
|
||||
CHECK(out->ceid == 300);
|
||||
REQUIRE(out->reports.size() == 1);
|
||||
CHECK(out->reports[0].rptid == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("S6F19 / S6F20 individual report round-trip") {
|
||||
auto req = s6f19_individual_report_request(7);
|
||||
auto parsed = parse_s6f19(req);
|
||||
REQUIRE(parsed.has_value());
|
||||
CHECK(*parsed == 7);
|
||||
auto reply = s6f20_individual_report_data({s2::Item::u4(99), s2::Item::ascii("ok")});
|
||||
auto out = parse_s6f20(reply);
|
||||
REQUIRE(out.has_value());
|
||||
CHECK(out->size() == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("S6F21 / S6F22 annotated report round-trip") {
|
||||
auto req = s6f21_annotated_report_request(7);
|
||||
auto parsed = parse_s6f21(req);
|
||||
REQUIRE(parsed.has_value());
|
||||
CHECK(*parsed == 7);
|
||||
std::vector<AnnotatedValue> rows{{1, s2::Item::u4(42)}, {2, s2::Item::ascii("hi")}};
|
||||
auto reply = s6f22_annotated_report_data(rows);
|
||||
auto out = parse_s6f22(reply);
|
||||
REQUIRE(out.has_value());
|
||||
REQUIRE(out->size() == 2);
|
||||
CHECK((*out)[0].vid == 1);
|
||||
CHECK((*out)[1].vid == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("S12F9 / S12F10 array map round-trip") {
|
||||
MapDataArray a{"WAFER-1", 0x01, ReferencePoint{0, 0}, std::string("\x00\x01\x02", 3)};
|
||||
auto m = s12f9_map_data_send_array(a);
|
||||
auto parsed = parse_s12f9(m);
|
||||
REQUIRE(parsed.has_value());
|
||||
CHECK(parsed->mid == "WAFER-1");
|
||||
CHECK(parsed->idtyp == 0x01);
|
||||
CHECK(parsed->binlt.size() == 3);
|
||||
CHECK(*ack_byte(s12f10_map_data_send_array_ack(MapDataAck::Accept)) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("S12F11 / S12F12 coordinate map round-trip") {
|
||||
MapDataCoord c{"WAFER-1", 0x01,
|
||||
{{ReferencePoint{1, 2}, std::string("\x05", 1)},
|
||||
{ReferencePoint{3, 4}, std::string("\x06", 1)}}};
|
||||
auto m = s12f11_map_data_send_coord(c);
|
||||
auto parsed = parse_s12f11(m);
|
||||
REQUIRE(parsed.has_value());
|
||||
CHECK(parsed->mid == "WAFER-1");
|
||||
REQUIRE(parsed->entries.size() == 2);
|
||||
CHECK(parsed->entries[0].xypos.x == 1);
|
||||
CHECK(parsed->entries[1].xypos.y == 4);
|
||||
CHECK(*ack_byte(s12f12_map_data_send_coord_ack(MapDataAck::FormatError)) == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("S12F13/F14 + F15/F16 + F17/F18 request shapes round-trip") {
|
||||
MapDataIdentifier id{"WAFER-1", 0x01};
|
||||
CHECK(parse_s12f13(s12f13_map_data_request_row(id))->mid == "WAFER-1");
|
||||
CHECK(parse_s12f15(s12f15_map_data_request_array(id))->idtyp == 0x01);
|
||||
MapDataCoordRequest cr{"WAFER-1", 0x01, 0xFF};
|
||||
auto parsed_cr = parse_s12f17(s12f17_map_data_request_coord(cr));
|
||||
REQUIRE(parsed_cr.has_value());
|
||||
CHECK(parsed_cr->sdbin == 0xFF);
|
||||
}
|
||||
|
||||
TEST_CASE("S7F17 / S7F18 delete PP round-trip") {
|
||||
auto m = s7f17_delete_pp_send({"R1", "R2", "R3"});
|
||||
auto parsed = parse_s7f17(m);
|
||||
REQUIRE(parsed.has_value());
|
||||
REQUIRE(parsed->size() == 3);
|
||||
CHECK((*parsed)[0] == "R1");
|
||||
CHECK((*parsed)[2] == "R3");
|
||||
|
||||
auto empty = parse_s7f17(s7f17_delete_pp_send({}));
|
||||
REQUIRE(empty.has_value());
|
||||
CHECK(empty->empty());
|
||||
|
||||
CHECK(*ack_byte(s7f18_delete_pp_ack(ProcessProgramAck::Accept)) == 0);
|
||||
CHECK(*ack_byte(s7f18_delete_pp_ack(ProcessProgramAck::PpidNotFound)) == 4);
|
||||
}
|
||||
|
||||
TEST_CASE("S2F25 / S2F26 loopback diagnostic round-trip") {
|
||||
// Arbitrary binary payload — host sends, equipment echoes back.
|
||||
const std::string payload("\x00\x01\x02\xFE\xFF some text", 14);
|
||||
|
||||
Reference in New Issue
Block a user