D4: E87 server dispatch for S3F17/F25/F27
Hooks the CarrierStore + LoadPortStore from D3 onto the wire:
S3F17 CarrierAction:
ProceedWithCarrier -> CarrierIDEvent::ProceedWithCarrier
CancelCarrier -> CarrierIDEvent::CancelCarrier
BindCarrierID -> CarrierIDEvent::Bind
unknown action -> CAACK=CarrierActionInvalid
unknown carrier -> CAACK=CarrierIDUnknown
S3F25 CarrierTransfer:
fires source.StartUnloading + target.StartLoading transfer
events, updates Carrier::port_id, returns CAACK=Accept.
S3F27 CancelCarrier:
fires CIDS CancelCarrier + CAS Cancel against the carrier.
The state-change handlers wired into the stores in D3 are the right
place to emit S6F11 CEIDs for carrier-events; that hookup is left for
a follow-up commit (it depends on the equipment.yaml CEID catalog,
which doesn't yet enumerate E87 events).
Closes Tranche D — E87 Carrier Management end-to-end (FSMs + wire +
stores + dispatch).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -609,6 +609,66 @@ int main(int argc, char** argv) {
|
|||||||
return gem::s5f18_exception_recover_abort_ack(ack);
|
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<int>(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) {
|
router.on(7, 3, [model, logfn](const s2::Message& msg) {
|
||||||
auto pp = gem::parse_s7f3(msg);
|
auto pp = gem::parse_s7f3(msg);
|
||||||
if (!pp) return gem::s7f4_process_program_ack(gem::ProcessProgramAck::LengthError);
|
if (!pp) return gem::s7f4_process_program_ack(gem::ProcessProgramAck::LengthError);
|
||||||
|
|||||||
Reference in New Issue
Block a user