feat(client)+feat(daemon): eq.names, @eq.command, E90/E157 RPCs
Python client:
- eq.names.event.* / .alarm.* / .command.* / .var.* / .constant.* —
autocomplete-able, typo-safe name lookup backed by the Describe RPC
(lazy, cached; AttributeError on bad name with close-match hints)
- @eq.command decorator — binds a handler by function name, validated
against the equipment's real command set at decoration time
- eq.report_substrate() — E90 wafer milestone reporting
- eq.report_module() — E157 module state reporting (auto-create)
Daemon (C++ service):
- ReportSubstrate RPC — drives E90 location + processing FSMs
- ReportModule RPC — drives E157 module FSM (auto-create on first report)
- ack_from_outcome() helper — consistent Ack mapping for read_sync results
Proto: SubstrateReport, ModuleReport, EquipmentDescription,
SpoolFlushRequest, TerminalMessage; Describe, FlushSpool,
SendTerminalMessage RPCs
Tests: C++ FSM test (journey + ghost rejection + E157 illegal jump);
interop coverage for names API and E90/E157 round-trip
Docs: ch42 RPC table + Python example updated; ch16 daemon-path section added
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -94,11 +94,36 @@ class EquipmentStub(object):
|
||||
request_serializer=equipment__pb2.CarrierState.SerializeToString,
|
||||
response_deserializer=equipment__pb2.Ack.FromString,
|
||||
)
|
||||
self.ReportSubstrate = channel.unary_unary(
|
||||
'/secsgem.v1.Equipment/ReportSubstrate',
|
||||
request_serializer=equipment__pb2.SubstrateReport.SerializeToString,
|
||||
response_deserializer=equipment__pb2.Ack.FromString,
|
||||
)
|
||||
self.ReportModule = channel.unary_unary(
|
||||
'/secsgem.v1.Equipment/ReportModule',
|
||||
request_serializer=equipment__pb2.ModuleReport.SerializeToString,
|
||||
response_deserializer=equipment__pb2.Ack.FromString,
|
||||
)
|
||||
self.WatchHealth = channel.unary_stream(
|
||||
'/secsgem.v1.Equipment/WatchHealth',
|
||||
request_serializer=equipment__pb2.Empty.SerializeToString,
|
||||
response_deserializer=equipment__pb2.Health.FromString,
|
||||
)
|
||||
self.Describe = channel.unary_unary(
|
||||
'/secsgem.v1.Equipment/Describe',
|
||||
request_serializer=equipment__pb2.Empty.SerializeToString,
|
||||
response_deserializer=equipment__pb2.EquipmentDescription.FromString,
|
||||
)
|
||||
self.FlushSpool = channel.unary_unary(
|
||||
'/secsgem.v1.Equipment/FlushSpool',
|
||||
request_serializer=equipment__pb2.SpoolFlushRequest.SerializeToString,
|
||||
response_deserializer=equipment__pb2.Ack.FromString,
|
||||
)
|
||||
self.SendTerminalMessage = channel.unary_unary(
|
||||
'/secsgem.v1.Equipment/SendTerminalMessage',
|
||||
request_serializer=equipment__pb2.TerminalMessage.SerializeToString,
|
||||
response_deserializer=equipment__pb2.Ack.FromString,
|
||||
)
|
||||
|
||||
|
||||
class EquipmentServicer(object):
|
||||
@@ -227,7 +252,30 @@ class EquipmentServicer(object):
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def ReportCarrier(self, request, context):
|
||||
"""E87 — carrier-based tools
|
||||
"""E87 — carrier-based tools. WAITING announces a physically-arrived
|
||||
carrier (creates it; idempotent — re-announce updates the slot map);
|
||||
IN_ACCESS / COMPLETE drive the access FSM. The host's S3F17 decisions
|
||||
(ProceedWithCarrier / CancelCarrier) come back on the Subscribe stream
|
||||
as CarrierAction.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def ReportSubstrate(self, request, context):
|
||||
"""E90 — substrate (wafer) tracking. Report each milestone of a wafer's
|
||||
journey; the daemon drives the E90 FSMs and emits the standard CEIDs to
|
||||
the host. ARRIVED creates the substrate. Only for tools that track
|
||||
individual substrates.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def ReportModule(self, request, context):
|
||||
"""E157 — module process tracking. Report a module's execution state; the
|
||||
daemon drives the E157 FSM and emits the standard CEIDs. The module is
|
||||
auto-created on first report. Only for tools with tracked modules.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
@@ -243,6 +291,30 @@ class EquipmentServicer(object):
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def Describe(self, request, context):
|
||||
"""Everything this equipment is configured with, by name — for tooling,
|
||||
diagnostics, and client-side validation/autocomplete.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def FlushSpool(self, request, context):
|
||||
"""Flush the spool: purge=true discards queued messages, purge=false drains
|
||||
them toward the host (requires a SELECTED session).
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def SendTerminalMessage(self, request, context):
|
||||
"""Equipment-initiated operator message to the host (S10F1). Fails with
|
||||
CANNOT_DO_NOW when no host is connected and stream 10 isn't spoolable.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
|
||||
def add_EquipmentServicer_to_server(servicer, server):
|
||||
rpc_method_handlers = {
|
||||
@@ -301,11 +373,36 @@ def add_EquipmentServicer_to_server(servicer, server):
|
||||
request_deserializer=equipment__pb2.CarrierState.FromString,
|
||||
response_serializer=equipment__pb2.Ack.SerializeToString,
|
||||
),
|
||||
'ReportSubstrate': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.ReportSubstrate,
|
||||
request_deserializer=equipment__pb2.SubstrateReport.FromString,
|
||||
response_serializer=equipment__pb2.Ack.SerializeToString,
|
||||
),
|
||||
'ReportModule': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.ReportModule,
|
||||
request_deserializer=equipment__pb2.ModuleReport.FromString,
|
||||
response_serializer=equipment__pb2.Ack.SerializeToString,
|
||||
),
|
||||
'WatchHealth': grpc.unary_stream_rpc_method_handler(
|
||||
servicer.WatchHealth,
|
||||
request_deserializer=equipment__pb2.Empty.FromString,
|
||||
response_serializer=equipment__pb2.Health.SerializeToString,
|
||||
),
|
||||
'Describe': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.Describe,
|
||||
request_deserializer=equipment__pb2.Empty.FromString,
|
||||
response_serializer=equipment__pb2.EquipmentDescription.SerializeToString,
|
||||
),
|
||||
'FlushSpool': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.FlushSpool,
|
||||
request_deserializer=equipment__pb2.SpoolFlushRequest.FromString,
|
||||
response_serializer=equipment__pb2.Ack.SerializeToString,
|
||||
),
|
||||
'SendTerminalMessage': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.SendTerminalMessage,
|
||||
request_deserializer=equipment__pb2.TerminalMessage.FromString,
|
||||
response_serializer=equipment__pb2.Ack.SerializeToString,
|
||||
),
|
||||
}
|
||||
generic_handler = grpc.method_handlers_generic_handler(
|
||||
'secsgem.v1.Equipment', rpc_method_handlers)
|
||||
@@ -528,6 +625,40 @@ class Equipment(object):
|
||||
options, channel_credentials,
|
||||
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
||||
|
||||
@staticmethod
|
||||
def ReportSubstrate(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(request, target, '/secsgem.v1.Equipment/ReportSubstrate',
|
||||
equipment__pb2.SubstrateReport.SerializeToString,
|
||||
equipment__pb2.Ack.FromString,
|
||||
options, channel_credentials,
|
||||
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
||||
|
||||
@staticmethod
|
||||
def ReportModule(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(request, target, '/secsgem.v1.Equipment/ReportModule',
|
||||
equipment__pb2.ModuleReport.SerializeToString,
|
||||
equipment__pb2.Ack.FromString,
|
||||
options, channel_credentials,
|
||||
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
||||
|
||||
@staticmethod
|
||||
def WatchHealth(request,
|
||||
target,
|
||||
@@ -544,3 +675,54 @@ class Equipment(object):
|
||||
equipment__pb2.Health.FromString,
|
||||
options, channel_credentials,
|
||||
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
||||
|
||||
@staticmethod
|
||||
def Describe(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(request, target, '/secsgem.v1.Equipment/Describe',
|
||||
equipment__pb2.Empty.SerializeToString,
|
||||
equipment__pb2.EquipmentDescription.FromString,
|
||||
options, channel_credentials,
|
||||
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
||||
|
||||
@staticmethod
|
||||
def FlushSpool(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(request, target, '/secsgem.v1.Equipment/FlushSpool',
|
||||
equipment__pb2.SpoolFlushRequest.SerializeToString,
|
||||
equipment__pb2.Ack.FromString,
|
||||
options, channel_credentials,
|
||||
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
||||
|
||||
@staticmethod
|
||||
def SendTerminalMessage(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(request, target, '/secsgem.v1.Equipment/SendTerminalMessage',
|
||||
equipment__pb2.TerminalMessage.SerializeToString,
|
||||
equipment__pb2.Ack.FromString,
|
||||
options, channel_credentials,
|
||||
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
||||
|
||||
Reference in New Issue
Block a user