syntax = "proto3"; package secsgem.v1; // ============================================================================= // SECS/GEM Equipment API // // The daemon *is* one piece of SEMI equipment: it owns the HSMS link to the // host (MES) and speaks GEM on your behalf. Your tool software connects as a // client and only ever does two things: // // • tell the equipment about itself — set variables, fire events, raise alarms // • react to what the host asks for — receive commands/jobs, answer them // // Everything SECS lives inside the daemon: message framing, report definitions, // the GEM state machines, timers, spooling. You need no SEMI knowledge to use // this API. Items are addressed by the human names from your equipment config // (e.g. "chamber_pressure"), never by numeric SVID / CEID / ALID. // // CAPABILITY TIERS — wire up only what your equipment is: // • Universal — variables, events, alarms, control state, commands. Every tool. // • Carriers — E87 carrier/load-port flows. Only carrier-based equipment. // • Recipes — S7 process-program transfer. Only recipe-driven equipment. // • Jobs — E40 process jobs. Only job-based process/front-end equipment. // If a tier doesn't apply, you simply never receive its HostRequest variants and // never call its report RPCs. // ============================================================================= service Equipment { // ---- Universal: report state to the host -------------------------------- // Update one or more status/data variables by name. The daemon remembers the // values, so the host always sees the latest when it polls (S1F3). rpc SetVariables(VariableUpdate) returns (Ack); // Read back what the daemon currently holds (useful on tool restart/reconnect). rpc GetVariables(VariableQuery) returns (VariableSnapshot); // Fire a collection event by name. The daemon assembles the configured report // and sends S6F11. Values in `data` override current values for this one event. rpc FireEvent(Event) returns (Ack); // Raise (S5F1 set) or clear an alarm by name. rpc SetAlarm(Alarm) returns (Ack); rpc ClearAlarm(Alarm) returns (Ack); // ---- Universal: control state ------------------------------------------- // Current GEM control state (ONLINE/LOCAL/REMOTE/OFFLINE). rpc GetControlState(Empty) returns (ControlState); // Request a transition — e.g. an operator panel taking the tool OFFLINE for // maintenance, or back ONLINE. The daemon applies E30 rules and may decline. rpc RequestControlState(ControlStateRequest) returns (Ack); // ---- Universal: react to the host --------------------------------------- // Subscribe to everything the host asks of this equipment. The daemon streams // HostRequest messages for as long as the call stays open. // // Delivery contract (v1): // - firehose: every subscriber receives every host request; // - NO buffering: a command arriving while no client is subscribed is // answered with its declarative ack from the equipment config (the // pre-daemon behaviour) and is NOT replayed on reconnect — the daemon // never tells the host "will finish later" for work no tool will do; // - when a Command does arrive here, the host has ALREADY been answered // with S2F42 HCACK=4; report the real outcome via FireEvent/SetAlarm. rpc Subscribe(SubscribeRequest) returns (stream HostRequest); // Report the outcome of a Command delivered on the stream, quoting its `id`. // NOTE the contract (SEMI-conformant, non-blocking): the daemon has ALREADY // answered the host with S2F42 HCACK=4 ("accepted, will finish later") when // it pushed the command onto the stream — the host's transaction is closed. // CompleteCommand therefore correlates/audits the command lifecycle; the // host learns the real outcome via the events/alarms you fire (FireEvent / // SetAlarm), exactly as E30 intends. A synchronous gating mode (tool decides // the HCACK before S2F42 goes out) is a possible v2 extension. rpc CompleteCommand(CommandResult) returns (Ack); // ---- Jobs / Carriers: report progress of work the host asked for -------- // Keyed by the durable id (job_id / carrier_id), not a per-message id — these // are long-lived objects you report against as the physical work proceeds. rpc ReportProcessJob(ProcessJobState) returns (Ack); // E40 — job-based tools rpc ReportCarrier(CarrierState) returns (Ack); // E87 — carrier-based tools // ---- Diagnostics -------------------------------------------------------- // Live daemon/link status: distinguishes "host went offline" from "cable // unplugged" from "spool filling up". Streams a snapshot on every change. rpc WatchHealth(Empty) returns (stream Health); } // ---- Values ---------------------------------------------------------------- // A SECS-II value in plain terms. The daemon converts it to the wire format // declared for the target item in your config — you never choose U4/F4/ASCII. message Value { oneof kind { string text = 1; sint64 integer = 2; double real = 3; bool boolean = 4; bytes binary = 5; List list = 6; // SECS-II nested list } } message List { repeated Value items = 1; } message Empty {} // ---- Universal: tool -> equipment ------------------------------------------ message VariableUpdate { map values = 1; // name -> value } message VariableQuery { repeated string names = 1; // empty = all configured variables } message VariableSnapshot { map values = 1; } message Event { string name = 1; // collection-event name map data = 2; // optional per-fire variable values } message Alarm { string name = 1; // alarm name } message SubscribeRequest { string client = 1; // optional label for logging/diagnostics } // ---- Control state --------------------------------------------------------- message ControlState { State state = 1; enum State { EQUIPMENT_OFFLINE = 0; ATTEMPT_ONLINE = 1; HOST_OFFLINE = 2; ONLINE_LOCAL = 3; ONLINE_REMOTE = 4; } } message ControlStateRequest { ControlState.State desired = 1; } // ---- Host -> tool stream --------------------------------------------------- // Everything the host initiates arrives here. Match on the populated variant; // ignore the variants for tiers your equipment doesn't implement. message HostRequest { oneof request { Command command = 1; // S2F41/F21/F49 — universal ControlStateChange control_state = 2; // control state transitioned — universal ConstantChange constant = 3; // host set an equipment constant — universal ProcessProgram process_program = 4; // S7 recipe downloaded — recipe tools CarrierAction carrier = 5; // E87 carrier at a port — carrier tools ProcessJob process_job = 6; // E40 job to run/stop — job tools } } // A remote command (S2F41 / S2F21 / S2F49). Answer with CompleteCommand. message Command { string id = 1; // correlation id — echo in CommandResult string name = 2; // RCMD, e.g. "START" map params = 3; // CPNAME -> CPVAL } // Control state changed (host went online/offline, operator toggled local/remote). message ControlStateChange { ControlState.State state = 1; } // The host wrote an equipment constant (S2F15). React if it tunes a process // parameter you care about; the daemon already stored the new value. message ConstantChange { string name = 1; Value value = 2; } // The host downloaded a recipe (S7F3). Load it into your process engine. message ProcessProgram { string ppid = 1; // recipe id bytes body = 2; // recipe contents (opaque to the daemon) } // E87: a carrier needs attention at a load port. Reply with ReportCarrier. message CarrierAction { string carrier_id = 1; // CARRIERID uint32 port = 2; // load-port number Action action = 3; enum Action { VERIFY_ID = 0; // read & confirm the carrier's identity PROCEED = 1; // begin access (open / map slots) CANCEL = 2; } } // E40: the host wants this process job run (or stopped). Report progress with // ReportProcessJob. `recipe` + `carriers` tell you what to run and on what. message ProcessJob { string job_id = 1; // PRJobID string recipe = 2; // PPID to run Action action = 3; repeated string carriers = 4; // material: carrier ids bound to this job enum Action { START = 0; STOP = 1; PAUSE = 2; RESUME = 3; ABORT = 4; } } // ---- Tool -> equipment: replies & progress reports ------------------------- message CommandResult { string id = 1; // the Command.id you are answering Ack ack = 2; // outcome (maps to HCACK on the wire) } // Advance an E40 process job as the physical work proceeds; the daemon drives // the E40 FSM and emits the matching S6F11 / S16F9 to the host. message ProcessJobState { string job_id = 1; State state = 2; enum State { SETTING_UP = 0; PROCESSING = 1; COMPLETE = 2; ABORTED = 3; } } // Report a carrier's identity, slot map, and state as the tool reads them. message CarrierState { string carrier_id = 1; uint32 port = 2; State state = 3; repeated bool slots = 4; // slot map: true = wafer present enum State { WAITING = 0; IN_ACCESS = 1; COMPLETE = 2; } } // ---- Diagnostics ----------------------------------------------------------- message Health { LinkState link = 1; uint32 spool_depth = 2; // queued messages waiting for the host ControlState.State control_state = 3; enum LinkState { DISCONNECTED = 0; // no TCP CONNECTED = 1; // TCP up, not yet SELECTED SELECTED = 2; // HSMS selected — actively talking to the host } } // ---- Acknowledgement ------------------------------------------------------- // Mirrors SEMI HCACK exactly. For non-command RPCs, only ACCEPT vs an error // code matters; `message` carries human detail ("no variable named 'presure'"). message Ack { Code code = 1; string message = 2; enum Code { ACCEPT = 0; // HCACK 0 INVALID_COMMAND = 1; // HCACK 1 CANNOT_DO_NOW = 2; // HCACK 2 PARAMETER_INVALID = 3; // HCACK 3 ACCEPTED_WILL_FINISH_LATER = 4; // HCACK 4 REJECTED = 5; // HCACK 5 INVALID_OBJECT = 6; // HCACK 6 } }