Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

儀器資料閘道器(C# / .NET 10)

tests

把精密儀器產出的報表檔,經過來源端品質閘後寫進資料庫的小型服務。

In brief — A gateway that ingests analytical-instrument reports through a source-side quality gate before they reach the database. Pluggable transport layer (file drop / TCP-IP / serial / vendor SDK), ten quality rules encoding methodological preconditions rather than generic validation, and a three-way verdict instead of pass/fail. C# / .NET 10.

你想看 去這裡
十條品質規則怎麼寫(每條背後的儀器學依據) src/Instrument.Core/QualityGate.cs
通訊層抽象與四種接法(含 TCP 黏包切分) src/Instrument.Core/Sources/
測試怎麼測反向案例(31 項中只有 2 項測乾淨資料) tests/Instrument.Tests/QualityGateTests.cs
跨廠商欄位別名對映 src/Instrument.Core/ReportParser.cs
schema 設計與 UPSERT 冪等 src/Instrument.Core/MeasurementStore.cs

監看資料夾 → 解析多廠商格式 → 品質閘判定 → SQLite(含 UPSERT 冪等)→ 異常清單供人工複核。

      通訊層(可抽換)                共用下游
┌──────────────────────────┐
│ 檔案投放  FileDropSource  │──┐
│ TCP 監聽  TcpSource       │──┤
│ 串列埠    SerialPortSource│──┼──> 解析(廠商別名對映)
│ 廠商 SDK  VendorSdkSource │──┤            │
│ OPC UA    OpcUaSource ※   │──┘            ▼
└──────────────────────────┘      來源端品質閘(10 條規則)
                                             │
                             ┌───────────────┼───────────────┐
                             ▼               ▼               ▼
                           Pass          Conditional       Reject
                             │               │               │
                             ▼               ▼               ▼
                     analysis_ready      入庫但帶限制      入庫但不供分析
                       (下游只查這個)      條件           +異常清單

※ OPC UA 為契約定義,未實作(見下方誠實邊界)。

通訊層為什麼要抽換

現場的儀器接法差異極大——老機台只剩 RS-232、新機台走 TCP 或 OPC UA、 有些封閉機台只能靠廠商 SDK 回呼,還有一大票只肯把報表丟到共用資料夾。 但接進來之後的事情完全一樣:解析 → 品質閘 → 入庫。

所以 IInstrumentSource 只回傳「一段原始文字 + 來源識別」,不回傳解析後的物件: 通訊層只負責把位元組收完整,判讀是別人的責任。換來源時下游一行都不用改, 不同機台走不同路時,多開幾條管線指向同一個資料庫即可。

各條路的真實坑

接法 現場真正卡住的地方
檔案投放 不是監看,而是檔案還沒寫完就被讀到。儀器軟體是開檔→逐段寫→關檔,Created 事件在開檔當下就觸發。這裡用「檔案大小連續兩次相同」判定寫入完成,比固定 sleep 可靠。
TCP TCP 是位元組串流,不是訊息佇列。一次讀取可能收到半份報表,也可能一次收到兩份。必須與工作站端約定訊息邊界(結束標記+長度上限),否則資料一定黏在一起或被截斷。
串列埠 埠號、鮑率、資料位元、同位、停止位元、流量控制六項要與儀器端逐一對齊,錯一項就是亂碼。這正是與網管、設備商對整合參數時實際在核對的東西。
廠商 SDK 難處不在技術而在授權與版本:SDK 綁授權碼、只支援特定 Windows 版本、儀器軟體一升級 API 就變。這些要在採購階段談進合約。
OPC UA 最常卡在憑證互信——雙方要把對方憑證加進信任清單,沒做時客戶端往往只回一句 BadSecurityChecksFailed,完全不指向真正原因。

為什麼品質閘要放在來源端

進了資料庫再清是清不出來的。舉三個這支程式實際在擋的例子:

陷阱 為什麼下游看不出來
GPC 校正曲線過期 資料庫裡就是一組分子量數字,看起來完全正常。但 GPC 量的是相對聚苯乙烯標準品的相對值,管柱老化後曲線漂移,跨批次比較就會得到假的差異。
DSC 未註明升溫速率 Tg 依升溫速率而變(越快量到越高)。少了速率欄位,這個 Tg 沒辦法跟任何其他數據比——這正是不同實驗室 Tg 對不起來的主因之一。
報表 PDI 與 Mw/Mn 不一致 兩個都是「合理」的數字,但兜不起來,通常代表匯出時取了不同積分區間或欄位對應錯位。只有同時拿到三個欄位才驗得出來。

這些都不是通用的資料驗證(非空、型別、範圍),而是方法學前提——每條規則背後都是一個「這台儀器的數字在什麼條件下才有意義」的事實。

三級判定,而不是二分法

大部分驗證框架只有「過/不過」。儀器數據需要第三種:

  • Pass — 通過全部規則,進 analysis_ready 檢視供下游使用
  • Conditional — 數值可用但有限制條件(例:校正曲線過期,僅限同曲線內比較)。入庫、標註限制、不進分析檢視。直接丟掉會損失可用資料,直接放行會製造假結論。
  • Reject — 違反物理必然性或方法前提(例:Mw < Mn、Tg ≥ Tm),不得進入分析。

原始值一律不刪除、不修正,只在旁邊標註判定與原因。任何「清理」都必須可回溯。

跨廠商欄位對映才是整合的真痛點

同一個物理量,各廠商叫不同名字。Agilent 寫 Calibration ID、Waters 寫 Calib. File、舊機台寫 CalID。解析邏輯本身很單純,別名對映表才是這個元件的核心資產——它就是「來源端資料規格」的具體形式。

未對映到的欄位會被列出來(未對映欄位:Atmosphere、Operator),不靜默吞掉——因為今天沒用到的欄位,可能就是明天要追的線索。

實跑結果

已產生 40 份儀器報表至 data/incoming(其中 6 份含刻意缺陷,seed=42)

解析成功 40 份、失敗 0 份
未對映欄位(需擴充別名表):Atmosphere、Operator

── 品質閘結果 ──
  Pass           27  (67.5%)
  Conditional     9  (22.5%)
  Reject          4  (10.0%)

可供分析(analysis_ready 檢視):27 筆

模擬器植入的 6 個缺陷全數被攔下,另外揪出 3 筆過期校正曲線造成的 Conditional。

執行

dotnet test                                                          # 31 個測試

# 產生模擬報表,批次擷取
dotnet run --project src/Instrument.Simulator -- data/incoming 40 42
dotnet run --project src/Instrument.Gateway   -- --source file --dir data/incoming

# 常駐監看資料夾
dotnet run --project src/Instrument.Gateway   -- --source file --dir data/incoming --watch

# TCP 監聽,等工作站推送
dotnet run --project src/Instrument.Gateway   -- --source tcp --port 5560

# 串列埠(需實機或虛擬串列埠)
dotnet run --project src/Instrument.Gateway   -- --source serial --serial /dev/ttyUSB0 --baud 9600

批次模式為預設,方便 CI 與重現;--watch--source tcp|serial 為常駐串流模式, 對應實際部署的背景服務(Windows 上即 Windows Service)。

TCP 實測

推送三份報表,前兩份刻意黏在同一次寫入送出:

  ✓ 127.0.0.1:49364 → Pass
  ✗ 127.0.0.1:49364 → Reject|Mw(20000) < Mn(42000),違反物理定義…; PDI 0.476 < 1…
  ✗ 127.0.0.1:49364 → Reject|無校正曲線識別碼;GPC 分子量是相對值,無曲線即無意義

黏包被正確切成三筆,來源識別記錄為連線端點,方便追回是哪台工作站送的。

設計決策

  • UPSERT 而非 INSERT:以 (source_file, sample_id, measured_at) 為唯一鍵。儀器工作站常重複匯出同一份報表,重跑閘道器不該產生重複列——整條管線可安全重放(已實測:重放後筆數不變)。
  • 直接寫 SQL 而非 ORM:schema 的意圖(唯一鍵、索引、analysis_ready 檢視)在程式碼裡看得見。
  • 品質閘參數外顯QualityGateOptions):校正有效天數、PDI 容許差、DSC 標準速率與樣品重量區間,依實驗室 SOP 調整不必改程式。
  • 測試測反向案例:31 個測試裡,只有 2 個測「乾淨資料會通過」,其餘都在測髒資料會不會被擋下來、且擋在正確的等級

專案結構

src/Instrument.Core/
  Sources/                 通訊層:檔案投放/TCP/串列埠/廠商 SDK/OPC UA 契約
  Models.cs                量測模型與三級判定
  ReportParser.cs          跨廠商欄位別名對映
  QualityGate.cs           10 條來源端品質規則
  MeasurementStore.cs      SQLite persistence(UPSERT 冪等、analysis_ready 檢視)
  IngestPipeline.cs        來源 → 解析 → 品質閘 → 入庫
src/Instrument.Gateway/    閘道器主程式(批次/串流,來源可由參數選擇)
src/Instrument.Simulator/  儀器工作站模擬器(含刻意缺陷)
tests/Instrument.Tests/    xUnit 31 項:品質閘 18、解析器 7、通訊來源 6

誠實邊界

各通訊來源的完成度不同,逐條列清楚:

來源 狀態
檔案投放 ✅ 已實作並實測(含寫入完成判定、鎖定重試)
TCP ✅ 已實作並實測(含黏包切分、超長防護、連線中斷處理)
廠商 SDK ✅ 整合點以委派注入,已用測試替身驗證;實際 SDK 為廠商私有無法散布
串列埠 ⚠️ 程式碼完整、編譯通過,但未經實機驗證(開發機無串列裝置)。需 USB-to-Serial 轉接器或虛擬串列埠對測
OPC UA 僅定義契約與連線參數,未實作。需 OPC Foundation 函式庫、憑證信任鏈與實機端點才能驗證;寫一個跑得動但沒對過實機的版本只會製造「已完成」的假象

其他限制:

  • 報表格式參考實際儀器軟體的匯出樣式撰寫,但不是任何廠商的真實檔案。
  • SQLite 為單機情境;產線環境會換成 SQL Server/PostgreSQL,並需要重試佇列、背壓與斷線續傳。
  • 未做:認證授權、Web UI、告警通知、歷史趨勢圖、OPC UA 斷線重連與 Session 重建。

這個專案想證明的事

資料工程師寫得出這支程式,但編不出裡面的資料陷阱——要知道校正曲線會過期、Tg 依速率而變、PDI 該和 Mw/Mn 對得起來,得先在儀器前面站過幾年。

About

Instrument data gateway in C#/.NET: source-side quality gate encoding methodological preconditions, pluggable transport layer (file/TCP/serial/vendor SDK), three-way verdict instead of pass-fail

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages