SiLA2 in fünf Minuten
SiLA 2 ist der Laborautomatisierungs-Standard für Gerätesteuerung über gRPC + TLS. Die Begriffe, die ihr braucht:
| Begriff | Bedeutung |
|---|---|
| SiLA Server | Das Gerät bzw. seine Steuersoftware — hier das ICM FeliX (ICM.FeliX.Host) |
| Feature | Eine benannte Fähigkeit, beschrieben als XML (FDL). Der FeliX hat zwei: das Pflicht-Feature SiLAService und com.endress/liquidhandling/LiquidHandlingController/v1 |
| Observable Command | Lang laufende Aktion: sofortige CommandExecutionUUID, Fortschritt als Stream, Ergebnis separat |
| Intermediate Response | Zwischenmeldung eines laufenden Commands — der FeliX nutzt sie für Bedienhinweise |
| Untrusted-Server-Modus | TLS mit selbstsigniertem Zertifikat; der Client vertraut der Server-CA explizit |
ExecuteProtocol nimmt ein ganzes Protokoll als JSON entgegen und
orchestriert den Geräte-Lifecycle selbst (INIT davor, WORKFLOW_SHUTDOWN danach).
Euer Client schickt nur Body-Schritte.Setup 10 min
1 · FeliX starten (läuft er schon, weiter zu Schritt 2):
cd "/Users/marcelweissgerber/Downloads/icm-lh-development 2"
docker compose -f docker-compose.local.yml -f docker-compose.local.mac.yml up -d
Ports: 5108 REST/UI · 5109 CASE-gRPC · 5111 SiLA2 (TLS).
Kontrolle: localhost:5108/sila-console —
dort seht ihr später jeden Call eures Clients live.
2 · CA-Zertifikat holen — der Trust-Anker für den untrusted-Modus (der Server generiert es beim ersten Start selbst):
docker cp icm-felix-local:/opt/icm/ca.crt .
3 · Python-Umgebung — die offizielle SiLA2-Referenzimplementierung:
python3 -m venv silaenv && source silaenv/bin/activate
pip install sila2
_sila._tcp-Advertisements — Clients mit Discovery brauchen den Datei-Umweg nicht.Verbinden + Discovery 10 min
from sila2.client import SilaClient
ca = open("ca.crt", "rb").read()
client = SilaClient("127.0.0.1", 5111, root_certs=ca)
print(client.SiLAService.ServerName.get()) # ICM FeliX SiLA2 Server
print(client.SiLAService.ServerUUID.get()) # f91b13d1-… (stabil über Neustarts)
print(client.SiLAService.ImplementedFeatures.get())
GetFeatureDefinition und generiert daraus den
Client. client.LiquidHandlingController existiert ab jetzt einfach.Checkpoint: Beide FQFIs erscheinen, und in der /sila-console tauchen eure
Calls mit Peer-Adresse und user-agent: grpc-python… auf.
Der Action-Katalog 5 min
Welche Aktionen ExecuteActivity versteht, sagt die Property
ActivitySchema — identisch mit GET /api/activity/schema, eine Quelle für REST und SiLA:
import json
lhc = client.LiquidHandlingController
schema = json.loads(lhc.ActivitySchema.get())
print(schema["schemaVersion"], len(schema["actions"])) # 2.11 38
Jede Action bringt Parameter-Namen, Datentypen und kopfspezifische Volumen-Ranges mit — euer Client validiert Protokolle, bevor das Gerät sie sieht.
Observable Commands verstehen 10 min
import time
cmd = lhc.ExecuteActivity("WAIT", json.dumps([{"Key": "waitTime", "Value": "1"}]), True)
print(cmd.execution_uuid) # sofort da — die Aktion läuft im Hintergrund
while not cmd.done: time.sleep(0.3)
result = cmd.get_responses() # ChecklistJson mit dem Step-Ergebnis
Die Observable-Command-Trias: ① sofortige Bestätigung mit UUID · ② Info-Stream
(intern, speist cmd.done) · ③ Ergebnis-Abruf, erst wenn fertig.
Ganze Protokolle: ExecuteProtocol 15 min
Der eigentliche Arbeitsmodus — nur Body-Schritte, den Rest macht der Server:
protocol = {"steps": [
{"activity": "MOVE", "parameters": [{"Key": "deckPosition", "Value": "7"},
{"Key": "zReference", "Value": "Topmost"}]},
{"activity": "WAIT", "parameters": [{"Key": "waitTime", "Value": "1"}]},
]}
cmd = lhc.ExecuteProtocol(json.dumps(protocol), True) # True = simuliert
while not cmd.done: time.sleep(0.5)
for step in json.loads(cmd.get_responses().StepResultsJson):
print(step["Step"], step["Activity"], step["Success"])
1 INIT True · 2 MOVE True · 3 WAIT True · 4 WORKFLOW_SHUTDOWN True
Der Server validiert vor dem ersten Gerätekommando: unbekannte Typen und
Lifecycle-Actions im Body werden synchron als ValidationError abgelehnt.
Bedienhinweise: Server pausiert, ihr quittiert 20 min
USER_NOTIFICATION und USER_INPUT sind normale Body-Schritte. Der Server
pausiert dort (Gerät bleibt initialisiert!), meldet über den Intermediate-Stream —
euer Client quittiert per ConfirmUserPrompt:
import threading
def handle_prompts():
for intermediate in cmd.subscribe_to_intermediate_responses():
prompt = json.loads(intermediate.PromptJson)
print("BEDIENHINWEIS:", prompt["Message"])
lhc.ConfirmUserPrompt(str(cmd.execution_uuid), prompt["PromptId"], "OK")
threading.Thread(target=handle_prompts, daemon=True).start()
Schritt 3 USER_NOTIFICATION: OK — Quittung 'OK' um 2026-08-06T07:36:46.335…+00:00
Sila:UserPromptTimeoutSeconds, Default 300 s)
→ ProtocolFailed, Teardown läuft trotzdem.Fehlerbilder lesen 10 min
SiLA-Fehler sind typisiert und kommen in Python als Exceptions an:
| Fehler | Wann | Wo |
|---|---|---|
ValidationError | Unbekannte Activity, Lifecycle im Body, kaputtes JSON | synchron beim Aufruf |
DefinedExecutionError: ProtocolFailed | Step-Fehler (999999 µL → „outside [0.1, 1000.0]"), Prompt-Timeout | bei get_responses() |
DefinedExecutionError: UnknownPrompt | ConfirmUserPrompt mit falscher/abgelaufener Id | synchron |
Der fertige Client 10 min
Alles zusammen — CLI, Schema-Vorab-Validierung, interaktive Quittung (oder
--auto für Skripte), Ergebnis-Report: tools/masterclass/felix_client.py
(~100 Zeilen, im Repo).
cd tools/masterclass
docker cp icm-felix-local:/opt/icm/ca.crt .
python felix_client.py beispiel-protokoll.json # interaktiv
python felix_client.py beispiel-protokoll.json --auto LOT-42 # unbeaufsichtigt
Verbunden: ICM FeliX SiLA2 Server (UUID f91b13d1-6142-47e7-b806-0f49723b6fc7)
Schema v2.11: alle 4 Schritte bekannt
>>> BEDIENHINWEIS (Schritt 3): Bitte Spalte 1 aus dem RoboTipTray entfernen. → quittiert 'OK'
>>> BEDIENHINWEIS (Schritt 5): Bitte die Lot-Nummer erfassen. → quittiert 'LOT-42'
Schritt 1 INIT: OK … Schritt 6 WORKFLOW_SHUTDOWN: OK
Live-Lab: Protokoll bauen und WIRKLICH ausführen
Dieser Editor spricht echtes SiLA2 (gRPC-Web) mit einem FeliX auf der Testing-Umgebung — simuliert, aber derselbe Server-Code wie am Gerät. Kurs-Token eingeben, verbinden, Steps zusammenklicken, ausführen. Bedienhinweise erscheinen als Dialog und eure Quittung landet zeitgestempelt im Ergebnis.
Troubleshooting & Wie weiter
UNAVAILABLE/ Connection refused → läuft der Container? SiLA-Port ist 5111.- TLS-Fehler →
ca.crtneu aus dem Container kopieren. - /sila-console zeigt nichts → Seite lädt sich nach Container-Neustart selbst neu; Clear-Button leert den Monitor.
- Sequenz-Regeln → Aspirate braucht Kopf + Tips; die Sim-Vorbereitung macht das INIT-Komposit von
ExecuteProtocolautomatisch. - Wie weiter → Auth/Lock der SiLA-Fläche ist offen (Atlas /r/9205) — vor Produktivbetrieb Pflicht. Discovery via mDNS statt Datei-CA. Epic zur SiLA2-Welle: /r/9197.
ICM FeliX · Branch feature/sila2-server · verifiziert 2026-08-06 ·
kanonische Quelle: documentation/masterclass-sila2-python-client.md im Repo