一 先建立不可猜測的換算契約
本例的 raw=253 不能只看數字就猜成 25.3 °C。輸入契約要列出 raw_type、來源量程、單位、倍率、偏移、方向與已縮放標誌。本案例明訂 raw 是未縮放的整數,倍率 0.1 °C/count,偏移 0 °C;所以 engineering_celsius=253×0.1=25.3 °C。若上游已把 253 轉成 25.3,再套一次 0.1,就會錯成 2.53 °C。
每筆資料帶 unit、scale、offset、source_version 與 scaled_flag。scaled_flag=false 的值才進入 raw→工程值步驟;true 表示已完成指定縮放,不能再套同一鏈。這個旗標不是靠目前數值推導,因為 raw=253、已縮放=253.0 都可能是合法值。來源版本改變時,必須重新確認倍率與偏移,不能沿用舊快取。
本案例換算鏈分兩段:第一段把 raw 253 轉成 25.3 °C;第二段使用明訂的華氏公式 F=C×9/5+32,得到 25.3×1.8+32=77.54 °F。華氏值是顯示或報表單位,不能回寫成新的 raw 後再重複轉換。每一段都保留 input_value、input_unit、output_value、output_unit 與 formula_version。
| 欄位 | 案例值 | 意義 | 錯誤風險 |
|---|---|---|---|
| raw | 253 | 來源未縮放整數 | 誤當工程值 |
| scale | 0.1 °C/count | 倍率 | 重複套用 |
| offset | 0 °C | 偏移 | 漏加或加兩次 |
| engineering_celsius | 25.3 °C | 第一段輸出 | 單位遺失 |
| display_fahrenheit | 77.54 °F | 第二段輸出 | 拿來當raw |
換算契約還要指定資料方向與更新週期。若 raw 在每秒更新一次,source_timestamp 應跟著每筆變化;若 HMI 每 500 ms 讀取同一值,不應把重複讀取當成新樣本。批次或歷史系統可用 sample_id 去重,但不能藉由時間間隔猜測是否已縮放。
換算函式要拒絕缺少契約的輸入。若收到 253 但 unit=unknown 或 scaled_flag 缺失,狀態應為 ContractMissing;不要以「看起來像 0.1 倍」自動判斷。這會讓錯誤早些暴露,也能避免同一數值在不同來源被套用不同倍率。
二 逐步計算與反向檢查
先保存 raw=253,再計算 celsius=253×0.1+0=25.3。若需回到 raw,使用 raw_estimate=(celsius-offset)/scale=(25.3-0)/0.1=253;這個反算只在值未被四捨五入或飽和時成立。顯示成 25.30 °C 不代表多了精度,原始 raw 仍是追溯權威。
再計算 fahrenheit=25.3×9/5+32=45.54+32=77.54 °F。若報表只保留 77.5,日後反算會有誤差;因此保存未格式化的工程值與顯示值。若要將 °F 輸入轉回 °C,應使用 C=(F-32)×5/9,並標示這是另一個方向的公式,不能把 32 當成一般偏移在所有單位鏈中重複套用。
換算鏈可用中間物件表示:RawValue(253, raw_unit, v1)→Celsius(25.3, v1)→Fahrenheit(77.54, display_v2)。每個節點都有來源與版本,若 v1 的倍率從 0.1 改為 0.01,新資料走新鏈,歷史結果不應靜默重算。重算要建立 derived_version,並保留舊結果。
| 步驟 | 公式 | 輸入 | 輸出 |
|---|---|---|---|
| 1 | raw×0.1+0 | 253 raw | 25.3 °C |
| 2 | C×9/5+32 | 25.3 °C | 77.54 °F |
| 反算1 | (C-0)/0.1 | 25.3 °C | 253 raw |
| 反算2 | (F-32)×5/9 | 77.54 °F | 25.3 °C |
若 raw 使用有號 16 位整數,先核對負值與零點,不要把 253 的正數案例外推為所有量程。假設 raw=-100、scale=0.1、offset=0,工程值是 -10 °C;若顯示單位改成 °F,結果是 14 °F。這些計算都要帶著 unit 與版本,避免正負號在轉型時消失。
驗收至少測試 raw=0、raw=253、raw=1000 及超量程值。raw=0 在本契約得到 0 °C,不代表任何其他來源的零點;raw=1000 得 100 °C。若來源規定量程 0–1000 counts,raw=1001 應先標 RangeRejected,不應先轉成 100.1 °C 再由顯示層截斷。
三 避免重複倍率與混合單位
最常見錯誤是兩個模組都以為自己負責縮放。通訊模組把 253 變成 25.3 後,HMI 綁定又依 tag 名稱套 0.1,得到 2.53。解法是讓資料結構明確標示 scaled_flag=true、unit=°C、scale_applied=v1,後續模組只允許 °C→°F 的轉換,不允許再走 raw→°C。
偏移也要避免重複。若 raw=4000、scale=0.1、offset=-40,工程值是 360 °C;若 offset 已在設備內扣除,應在契約標示 offset_applied=true。不能因畫面顯示正常就把 -40 再加一次或漏掉一次,應用反算 raw 與來源校準點驗證。
單位不能只放在變數名稱。命名 temp_253 不能說明是 raw count、°C 還是 °F;資料封包內要帶 unit code 或契約版本。若來源把 25.3 寫成「253」並宣稱 unit=0.1°C,數值與單位必須一起保存,不能把字串外觀當作單位證據。
| 錯誤情況 | 實際鏈 | 錯誤結果 | 排查 |
|---|---|---|---|
| 重複倍率 | 253→25.3°C→×0.1 | 2.53°C | 看scaled_flag |
| 漏偏移 | raw×0.1,漏offset | 偏移錯 | 核對校準版本 |
| 重複華氏 | 25.3°C→77.54°F→再套1.8 | 錯誤 | 限制輸出單位 |
| 單位遺失 | 253無unit | 不可判斷 | ContractMissing |
| 版本混用 | v1 raw配v2 scale | 數值漂移 | 保存source_version |
多段鏈的每一步都可做單元測試:raw→°C、°C→°F、°F→°C,以及已縮放輸入的拒絕測試。輸入 25.3 °C、scaled_flag=true 時,測試應直接進第二段得到 77.54 °F;若函式要求 raw 卻收到工程值,應回 ContractMismatch,不應默默重新解釋。
若系統同時支援顯示、控制與歷史,建議控制值使用明確工程單位欄位,顯示值另存格式化字串,歷史則存 raw 與工程值。不要讓 HMI 格式化結果回流成控制輸入,除非另有明確反算與權限流程。
四 範圍 精度與故障排查
排查異常值時按鏈逐層核對:先查 raw 是否是本批來源,再查 unit、scale、offset、scaled_flag、source_version,接著重算工程值,最後才查 °F 顯示格式。若 253 變成 2.53,優先看倍率是否重複;若 25.3 變成 77.54 以外的值,查華氏公式或單位標記;若只差 0.01,查最後一次四捨五入位置。
範圍檢查要寫在每一段的輸出規格。raw 0–1000、°C 0–100、°F 32–212 是本案例的自訂範圍,不能套到所有感測器。超範圍資料保存 raw 與 reason,不要夾到邊界後標成正常。若工程值有有限精度,需保存 conversion_precision,不能把顯示兩位小數當成量測精度。
離線驗收可使用四筆:raw=0→0 °C→32 °F,raw=253→25.3 °C→77.54 °F,raw=1000→100 °C→212 °F,raw=1001→RangeRejected。再另送 25.3,標成 scaled_flag=true 且 unit=°C,確認只做 °C→°F;若程式仍套 raw 倍率,測試應失敗並記錄 DoubleScale。
| 症狀 | 先查 | 預期判斷 | 修正 |
|---|---|---|---|
| 結果2.53°C | scaled_flag/scale | DoubleScale | 跳過raw段 |
| 結果77.54°F | 中間unit | 正確兩段鏈 | 保存中間值 |
| 只差0.1 | rounding位置 | 格式化過早 | 最後一次格式化 |
| 超量程變100 | range順序 | 被夾限 | 保留Rejected |
| 歷史前後不同 | source_version | 版本混用 | derived_version |
當兩個來源都送出 253,不能只依數值合併。來源 A 可能是 0.1°C/count,來源 B 可能是 0.01°C/count;必須依 source_id、unit、scale_version 建立不同鏈。排查報表要列出來源契約,讓工程師能看出數值相同但物理意義不同。
本文以算式、契約與驗收資料說明轉換鏈。實際資料型別、字串格式、持久化與單位代碼仍須查選定控制器和感測器手冊,不能用通用欄位名稱取代廠商規格。
五 驗收 FAQ 與來源
本題基準鏈為 raw=253、scale=0.1、offset=0,得到 25.3 °C,再以 F=C×9/5+32 得 77.54 °F。所有中間單位、倍率、偏移、來源版本與 scaled_flag 均要保留;缺契約、超範圍或重複倍率都應拒收或標錯,不得用數值猜單位。
FAQ1:看到 raw=253 可以直接乘 0.1 嗎?答:只有輸入契約明訂 raw 未縮放、scale=0.1 且版本相符才可以;缺少 scaled_flag 或 unit 時應標 ContractMissing。
FAQ2:為何不能用 77.54 反推出 raw?答:華氏顯示值可能已四捨五入或來自另一版本;反算前要知道公式、精度、單位與來源版本,原始 raw 才是追溯權威。
FAQ3:偏移已在設備套用,應用程式還要加嗎?答:不能重複套用。契約要標 offset_applied 與版本,應用程式只執行尚未完成的鏈。
FAQ4:超量程可以夾到 100 °C 嗎?答:除非規格把夾限定義為有效替代值,否則保留原始值並標 RangeRejected,不能用夾限掩蓋來源故障。
參考:NIST SI units 官方資料:單位名稱與量值表述參考,非 PLC 轉換 API。
參考:Python decimal 官方文件:十進位精度與捨入參考,非 PLC 型別規格。