diff --git a/apps/secs_server.cpp b/apps/secs_server.cpp index 0b386c7..3d982fe 100644 --- a/apps/secs_server.cpp +++ b/apps/secs_server.cpp @@ -609,6 +609,66 @@ int main(int argc, char** argv) { return gem::s5f18_exception_recover_abort_ack(ack); }); + // ---- E87 Carrier Management dispatch --------------------------------- + // S3F17 maps the textual CARRIERACTION string onto a CarrierIDEvent + // and fires it against the matching carrier. Unknown actions return + // CarrierActionInvalid; unknown carriers return CarrierIDUnknown. + router.on(3, 17, [model, logfn](const s2::Message& msg) { + auto req = gem::parse_s3f17(msg); + if (!req) return gem::s3f18_carrier_action_ack(gem::CarrierActionAck::ParameterInvalid); + if (!model->carriers.has(req->carrierid)) + return gem::s3f18_carrier_action_ack(gem::CarrierActionAck::CarrierIDUnknown); + + auto ack = gem::CarrierActionAck::Accept; + if (req->carrieraction == "ProceedWithCarrier") { + model->carriers.fire_id_event(req->carrierid, + gem::CarrierIDEvent::ProceedWithCarrier); + } else if (req->carrieraction == "CancelCarrier") { + model->carriers.fire_id_event(req->carrierid, + gem::CarrierIDEvent::CancelCarrier); + } else if (req->carrieraction == "BindCarrierID") { + model->carriers.fire_id_event(req->carrierid, gem::CarrierIDEvent::Bind); + } else { + ack = gem::CarrierActionAck::CarrierActionInvalid; + } + logfn("S3F17 CARRIER=" + req->carrierid + " action=" + req->carrieraction + + " -> S3F18 CAACK=" + std::to_string(static_cast(ack))); + return gem::s3f18_carrier_action_ack(ack); + }); + + // S3F25 — host instructs equipment to move a carrier between ports. + // We record the new port binding on the Carrier and fire the source + // port's StartUnloading + target port's StartLoading transfer events; + // application code is responsible for completing them. + router.on(3, 25, [model, logfn](const s2::Message& msg) { + auto req = gem::parse_s3f25(msg); + if (!req) + return gem::s3f26_carrier_transfer_ack(gem::CarrierActionAck::ParameterInvalid); + auto* c = model->carriers.get(req->carrierid); + if (!c) + return gem::s3f26_carrier_transfer_ack(gem::CarrierActionAck::CarrierIDUnknown); + model->load_ports.fire_transfer_event(req->source_portid, + gem::LoadPortTransferEvent::StartUnloading); + model->load_ports.fire_transfer_event(req->target_portid, + gem::LoadPortTransferEvent::StartLoading); + c->port_id = req->target_portid; + logfn("S3F25 CARRIER=" + req->carrierid + + " " + std::to_string(req->source_portid) + "->" + + std::to_string(req->target_portid)); + return gem::s3f26_carrier_transfer_ack(gem::CarrierActionAck::Accept); + }); + + // S3F27 — Cancel Carrier (single-EXID form). + router.on(3, 27, [model, logfn](const s2::Message& msg) { + auto cid = gem::parse_s3f27(msg); + if (!cid || !model->carriers.has(*cid)) + return gem::s3f28_cancel_carrier_ack(gem::CarrierActionAck::CarrierIDUnknown); + model->carriers.fire_id_event(*cid, gem::CarrierIDEvent::CancelCarrier); + model->carriers.fire_access_event(*cid, gem::CarrierAccessEvent::Cancel); + logfn("S3F27 CARRIER=" + *cid + " cancelled"); + return gem::s3f28_cancel_carrier_ack(gem::CarrierActionAck::Accept); + }); + router.on(7, 3, [model, logfn](const s2::Message& msg) { auto pp = gem::parse_s7f3(msg); if (!pp) return gem::s7f4_process_program_ack(gem::ProcessProgramAck::LengthError);