Semiconductor Manufacturing · Industrial IoT · .NET

FabBridgeEngine

A production-grade C#/.NET 8 microservice bridge that ingests SECS-II equipment events over HSMS, translates cryptic collection-event IDs into meaningful MES states, persists them, and streams them live to a dashboard — built up a five-rung fidelity ladder from in-process simulation to a real SECS/GEM stack.

.NET 8 / C# SECS-II · HSMS · GEM System.Threading.Channels Blazor Server + SignalR Dapper + SQL Server MQTT secs4net Docker xUnit · 28 tests

What it is

Fab equipment speaks a 1980s binary protocol (SECS-II) over TCP (HSMS). A factory's MES ("brain") doesn't care about cryptic event numbers like CEID 201 — it cares about states like RUNNING, ALARM, MAINTENANCE. FabBridgeEngine is the adapter between those two worlds.

The whole system exists to perform one transformation — SecsEvent → EquipmentStateChange — ingesting events fast, translating them, and fanning the result out to wherever it needs to go. Everything else (channel, worker, four sinks, dashboard) is plumbing around that core, and a set of well-placed interfaces lets the system grow at its edges, never its core.

Architecture

A decoupled ingestion pipeline: a bursty producer feeds a bounded in-memory channel; a steady worker drains it, translates each event, and fans the result out to independent sinks with per-sink fault isolation.

flowchart TB
  EQ["Factory Equipment / GEM Tool"] -->|"SECS-II over HSMS (S6F11)"| RCV["Equipment Source
(IEquipmentSource)"] RCV -->|"SecsEvent"| CH[("Bounded Channel
backpressure, FIFO")] CH -->|"await foreach"| TR["Translation Worker
CEID → MES State"] TR --> FAN{{"Fan-out
IStateChangeSink[]"}} FAN --> SQL[("SQL Server
Dapper · stored proc
audit trail")] FAN --> HUB["SignalR Hub"] FAN --> MQTT["MQTT Broker
retained per-tool"] HUB --> DASH["Blazor Live
Status Dashboard"] MQTT --> SUBS["Downstream subscribers
MES · analytics · dashboards"] classDef core fill:#0f766e,stroke:#0b5c55,color:#fff; classDef sink fill:#2563eb,stroke:#1e50c0,color:#fff; class RCV,CH,TR,FAN core; class SQL,HUB,MQTT sink;

The core translation

A raw SecsEvent carries an equipment id, a numeric CEID, and a bag of report values. The translator maps the CEID to a meaningful state — as data, not code (an injected dictionary), so a new tool is a config change, not a code change. Unknown CEIDs degrade to Unknown rather than throwing, because a 24/7 bridge must never crash on an unexpected event.

CEIDMeaningMES State
101process startedRunning
102process completedIdle
201alarm setAlarm
202alarm clearedIdle
301entered maintenanceMaintenance
302maintenance doneIdle
401comms/heartbeat lostDown
otherunrecognizedUnknown

Translation is intentionally lossy (many CEIDs → one state), so every state change keeps its originating SourceCeid for the audit trail — you can always reconstruct why a tool became Idle.

The fidelity ladder

The equipment side was built as a ladder you climb — each rung adds one layer of real-world fidelity (transport, framing, a broker, the real protocol) while the entire downstream pipeline stays identical. Every rung is just a different IEquipmentSource, selectable by config.

1logic

In-process simulator

A fake fab tool generates a realistic, jittered lifecycle (start → run → occasional alarm → complete → maintenance) straight into the channel. Exercises the pipeline logic with zero infrastructure.

2bytes

SECS-II framing

Length-prefixed binary frames — the idea that makes any TCP transport work: a boundary marker so the receiver knows where each message ends.

3socket

Real TCP transport feature/tcp-hsms

A TCP equipment server streams framed events; a client reassembles them across arbitrary packet boundaries (ReadExactly), with reconnect. Exercises sockets, fragmentation, connection lifecycle, and backpressure spanning the network.

4pipeline

MQTT broker feature/mqtt-pipeline

Translated states publish to an MQTT broker (retained, one topic per tool) so the dashboard is no longer the only consumer — a real distributed pipeline. Added as one more sink, zero changes to the worker.

5protocol

Real SECS/GEM via secs4net feature/secs4net

Genuine HSMS with the full Select handshake, real SECS-II Item trees, and an actual S6F11 → S6F12 request/ack exchange. Standard-conformant — it would talk to a real tool. One HSMS connection = one tool.

The equipment lifecycle generator is written once and reused across rungs 1, 3, and 5 — driving an in-memory queue, a raw socket, and a real GEM link unchanged — purely because it writes to an IEventProducer seam.

Design principles

The recurring thesis, made concrete in code.

Dependencies point inward

The Core domain library depends on nothing — SQL, TCP, MQTT, and SECS/GEM are outer details plugged into Core's interfaces. That's why Core's tests run in milliseconds with no infrastructure.

Interfaces at every seam

IEquipmentSource, IEventChannel (split into producer/consumer), and IStateChangeSink let each end be swapped in isolation. The worker never changed while SQL, SignalR, TCP, MQTT, and SECS/GEM were all added around it.

Bounded channel = backpressure

A bounded queue applies backpressure instead of growing memory without limit — for a 24/7 bridge, slowing down is acceptable; losing events or an OOM crash is not.

Fault isolation & graceful degradation

Each sink publish is isolated: a transient DB blip can't stop live dashboard updates. Unknown events and dropped links are handled, never fatal. Reconnect loops distinguish shutdown from failure.

Stack & structure

Technology

  • .NET 8 / C# — records, async streams, System.Threading.Channels
  • Blazor Server + SignalR — live, push-based status tiles
  • Dapper + SQL Server (Docker) — stored-procedure telemetry writes
  • MQTT (Mosquitto) — retained per-tool state for downstream consumers
  • secs4net 3.1 — real HSMS/SECS-II protocol stack
  • xUnit — 28 tests incl. real loopback socket & HSMS integration

Project layout

src/
  Core/         # pure domain (no deps)
  Persistence/  # Dapper + SQL
  Transport/    # TCP/HSMS framing (rung 3)
  Messaging/    # MQTT publish (rung 4)
  SecsGem/      # real secs4net (rung 5)
  Dashboard/    # Blazor + SignalR
  Host/         # console runner
tests/       # Core · Transport · Messaging · SecsGem

Run any rung from config

Equipment__Mode=Simulated  dotnet run --project src/Dashboard   # rung 1
Equipment__Mode=Tcp        dotnet run --project src/Dashboard   # rung 3
Equipment__Mode=SecsGem    dotnet run --project src/Dashboard   # rung 5

Verification

Every layer is tested at the right altitude — pure logic in isolation, and real sockets / real HSMS where the hard bugs live.

28
tests, all green
11
Core (domain)
9
Transport (TCP/framing)
3
Messaging (MQTT)
5
SecsGem (HSMS)

Highlights: frame reassembly tested at one byte per read (worst-case fragmentation); a real loopback socket loop; and a genuine HSMS Select handshake exchanging S6F11/S6F12.