ربط أجهزة المعمل بالسيستم — خطة الـ Middleware (الجسر الوسطي)

دراسة جهاز Dymind DH36 (CBC) من المانوال + دراسة الباك إند بعمق → خطة عملية لاستقبال داتا الأجهزة ورفعها للسيستم تلقائياً، وقابلة للتوسّع لأي جهاز.

التاريخ: 4 يونيو 2026  •  المرجع: DH36 LIS.pdf — Section 5.5  •  الموديول: LIS / Machines  •  الحالة: الميدل وير اتبنى بـPython واختُبر end-to-end ✓

1 إيه اللي فهمناه من مانوال الجهاز (DH36)

جهاز Dymind DH36 محلل دم آلي (CBC). من قسم 5.5 LIS Connection في المانوال، طريقة الربط واضحة ومحددة:

العنصرالقيمة / التفصيل
وسيلة الاتصالكابل شبكة TCP/IP — الجهاز واللي بيستقبل منه لازم على نفس الـ subnet
المنفذ (Port)5600 افتراضي (قابل للتغيير)
البروتوكول«Description of LIS Communication Protocol for Dymind Hematology Analyzers» — بروتوكول HL7 فوق TCP (إطار MLLP)
المطابقة (Matched by)Sample ID = رقم العينة / الباركود — ده مفتاح الربط بنتيجتنا
اتجاه واحد / اتجاهينيدعم Bidirectional LIS/HIS: الجهاز يقدر يستعلم عن طلبات العينة قبل التحليل (Query)، وكمان يرسل النتيجة
خيارات إضافيةAuto-communication، إعادة الإرسال بعد التعديل، ACK + timeout، صيغة الرسم PNG، إرسال الهيستوجرام

🔑 النقطة الجوهرية في المانوال

الجهاز لا يتصل بالسيرفر السحابي مباشرة. هو بيتصل بحاجة المانوال بيسمّيها «LIS workstation» — جهاز/برنامج على الشبكة المحلية للمعمل يستمع على IP+Port، يستقبل منه النتيجة بالبروتوكول بتاعه. ده بالظبط الدور اللي هيلعبه الـ Middleware اللي هنبنيه.

2 الفكرة الكبيرة — ليه «ميدل وير» ومش ربط مباشر؟

الجهاز بيتكلم «لغة محلية» (HL7/TCP على شبكة المعمل) — مش HTTPS ومش بيعرف يوصل للسحابة. فمحتاجين طبقة وسيطة تترجم وترفع:

الأجهزةDH36 وغيره
TCP/HL7 محلي
Middlewareعلى شبكة المعمل
يستمع + يترجم + يرفع
السيستم (السحابة)HTTPS API
match + auto-apply
الفنيValidation Worklist
يراجع ويعتمد

الميزة: نمط واحد يربط أي جهاز

الميدل وير بيتعمل modular — «driver» لكل بروتوكول (HL7 / ASTM / Serial / File). الجهاز الجديد = driver + mapping جديد فقط، من غير أي تغيير في السيستم السحابي. يعني نبدأ بـ DH36 ونوسّع لأي محلل (كيمياء، هرمونات، مزرعة...) بنفس البنية.

3 مفاجأة سارّة: السيستم بتاعنا جاهز ~90% بالفعل

درست الباك إند بعمق — البنية التحتية لاستقبال نتائج الأجهزة موجودة ومبنية:

المكوّنموجود؟التفصيل
جدول الأجهزة lab_machinesجاهزفيه interface_type (hl7/tcp/serial/file)، connection_settings (ip/port JSON)، connection_status، آخر اتصال/نتيجة
نقطة الاستقبال POST /lis/machine-resultsجاهزبتستقبل النتيجة، بتطابق تلقائياً، وبتطبّقها على نتيجة المريض
المطابقة التلقائيةجاهزباركود → عينة (lab_samples) + (الجهاز + كود التحليل) → التحليل عبر lab_machine_test_mappings
التطبيق التلقائي على النتيجةجاهزلو إعداد lis.auto_apply_machine_results مفعّل → بيملأ lab_results ويعلّمها source=machine, status=Entered
الاتجاهين (Work Orders)جاهزGET /lis/machines/{id}/work-orders — بيرجع الطلبات المعلّقة للجهاز مرتبة بالأولوية (STAT/Urgent الأول)
نبض الجهاز POST /machines/{id}/heartbeatجاهزلمراقبة حالة الاتصال (online/offline)
سجل الاتصالات + صندوق غير المطابَقجاهزcommunication-logs + machine-results/unmatched + match/approve/reject
شاشة الأجهزة + الـ Mapping (الواجهة)جاهزشاشة Machines فيها إعداد tcp/hl7 + ip/port، وشاشة ربط أكواد الجهاز بالتحاليل
الميدل وير المحلي (Python)تم بناؤه + اختُبربرنامج Python (stdlib بس) — اتجرّب end-to-end على الباك إند الحقيقي ✓ (تفاصيل في القسم 10)

✅ الخلاصة

مش محتاجين نعيد بناء أي حاجة في السيستم. الـ API والمطابقة والتطبيق التلقائي والواجهة — كلها موجودة. والميدل وير المحلي دلوقتي اتبنى بـPython واختُبر — فاللي باقي بس إعداد (تعريف الجهاز + الـ mappings + تفعيل auto-apply + نسخ البرنامج على جهاز ويندوز بالمعمل).

4 رحلة النتيجة خطوة بخطوة (One-way)

قاعدة أمان مهمة

داتا الجهاز بتدخل كـ«نتيجة مبدئية» (Entered) مش «معتمدة» — لازم تعدّي على عين الفني في شاشة المراجعة قبل ما تتطلع للمريض. ده بيحمي من أي قراءة غلط من الجهاز.

5 العقد الفني — إيه اللي الميدل وير بيبعته بالظبط

لكل بارامتر، الميدل وير يعمل POST بالشكل ده (نفس الحقول اللي الـ endpoint بيقبلها فعلياً):

POST https://moon-erp.elbaset.com/api/lis/machine-results
X-Authorization: Bearer <service-account-token>
Content-Type: application/json
{
  "machine_id": 7,                 // أي جهاز (DH36)
  "barcode": "LR-2026-00259-1",    // = Sample ID من الجهاز
  "test_code": "WBC",            // كود البارامتر عند الجهاز
  "raw_result": "7.4",           // القيمة الخام
  "parsed_result": "7.4",        // اختياري
  "unit": "10^3/uL",            // اختياري
  "raw_data": { "flags":"N", "hl7_segment":"OBX|1|NM|WBC..." }
}
// الرد: 201 + meta:{ matched:true, auto_applied:true }

الباك إند بيرجع matched (اتطابق ولا لأ) وauto_applied (اتطبّق على نتيجة المريض ولا استنى مراجعة). لو الباركود مش موجود → النتيجة تروح «صندوق غير المطابَق» (unmatched) للمطابقة اليدوية.

6 مكوّنات الميدل وير اللي هنبنيه

① TCP Listener

يلعب دور «LIS workstation»: يستمع على المنفذ (5600) ويقبل اتصال الجهاز. يدعم أكتر من جهاز (منفذ/جلسة لكل جهاز).

② Protocol Driver

parser للبروتوكول. نبدأ بـ HL7/MLLP (Dymind). مبني modular عشان نضيف ASTM/Serial/File لأجهزة تانية.

③ Cloud Uploader

يبعت كل بارامتر لـ /lis/machine-results عبر HTTPS بتوكن الخدمة، مع إعادة محاولة.

④ Bidirectional Query

لما الجهاز يستعلم عن عينة → الميدل وير يسحب /work-orders ويرد للجهاز بطلبات التحليل (HL7).

⑤ Local Buffer + Retry

لو النت قطع، يخزّن النتائج محلياً ويعيد الرفع لما يرجع — مفيش نتيجة بتضيع.

⑥ Heartbeat + Logs

نبض دوري لـ /heartbeat + سجل اتصالات، عشان شاشة الأجهزة تبيّن online/offline.

التقنية المختارة: Python (تم التنفيذ)

الـ«LIS workstation» عادةً جهاز Windows على شبكة المعمل. اخترنا Python وبنينا البرنامج فعلاً:

🐍 ليه Python — والمميزة الكبيرة

اتكتب بـمكتبة Python القياسية فقط (stdlib) — يعني صفر pip install. ينسخ على أي ويندوز فيه Python 3.8+ ويشتغل على طول، من غير نت أو تنصيب حزم (ميزة كبيرة في شبكات المعامل المعزولة). كل المكوّنات الستة فوق متبنية + اختُبرت.

7 خطوة لا غنى عنها: الـ Mapping

الجهاز بيقول «WBC»، السيستم عنده تحليل اسمه «White Blood Cells» بـ id رقم. لازم نعرّف الجسر ده لكل جهاز في شاشة machine-test-mappings:

كود الجهاز machine_test_codeالتحليل عندنا investigation_idالوحدة
WBCWhite Blood Cells10³/µL
RBCRed Blood Cells10⁶/µL
HGBHemoglobing/dL
PLTPlatelets10³/µL
MCV / MPV / …… باقي بارامترات CBC

من غير الماببينج، النتيجة بتوصل بس مش بتعرف تتطابق → بتروح صندوق «غير المطابَق». فده أول حاجة بنجهّزها لكل جهاز.

8 خطة التنفيذ على مراحل

  1. Phase 1 ✓الاستقبال أحادي الاتجاه (DH36) — تم. الميدل وير اتبنى بـPython: TCP listener + MLLP/HL7 parser + buffer + رفع لـ /machine-results + heartbeat. اختُبر end-to-end على الباك إند الحقيقي. DONE
  2. Phase 3 ✓الموثوقية — تمت ضمن البناء. Local buffer/retry (SQLite outbox)، heartbeat، سجل، ودعم تعدّد الأجهزة (منفذ لكل جهاز) — كلها متضمّنة بالفعل في النسخة المبنية.
  3. Phase 0التجهيز (سحابي — إعداد فقط). «service account» بتوكن، تعريف DH36 في شاشة Machines (interface=hl7, ip/port)، إدخال الـ mappings لبارامترات CBC، تفعيل lis.auto_apply_machine_results، ونسخ البرنامج على جهاز ويندوز بالمعمل. الخطوة الجاية
  4. Phase 2الاتجاهين (Query) — لأجهزة Maglumi / Mispa. الجهاز يستعلم عن طلبات العينة بالباركود قبل التحليل عبر /work-orders — يقلل الإدخال اليدوي ويمنع تحليل عينة بالغلط. الباك إند جاهز؛ يتبقى Query handler في الميدل وير. القسم 11
  5. Phase 4التوسّع لأي جهاز (درايفر ASTM). إضافة درايفر ASTM (LIS2‑A2) للأجهزة زي Mispa وأجهزة الكيمياء/المناعة، بنفس البنية وعقد الرفع، من غير لمس الباك إند.

10 اللي اتبنى فعلاً + إثبات الاختبار

البرنامج اتكتب بالكامل بـPython (stdlib) في مجلد lis-middleware/ واتجرّب على الباك إند الحقيقي:

📁 هيكل البرنامج

# lis-middleware/
run.py            # التشغيل
run.bat           # لويندوز
config.example.json
lis_middleware/
  server.py       # TCP listener
  mllp.py         # إطار MLLP
  hl7.py          # parse + ACK
  cloud.py        # رفع HTTPS
  buffer.py       # outbox (SQLite)
  app.py          # المنظّم
  drivers/
    dymind_hl7.py # درايفر DH36
tools/
  simulate_analyzer.py # محاكي جهاز

✅ نتيجة الاختبار end-to-end

acquired auth token via login
listening … on 127.0.0.1:5600
connection from analyzer
queued 9 result(s) barcode SIM-TEST-001
↩ ACK sent → analyzer
uploaded WBC … HTTP 201
uploaded HGB … HTTP 201
… كل الـ9 بارامتر
buffer pending: 0

أكدت إن الـ9 نتائج نزلت في السحابة فعلاً (machine-results)، ونضّفت بيانات التجربة بعدها.

🧪 تقدر تجرّب من غير الجهاز

شغّل python run.py ثم python tools/simulate_analyzer.py --barcode <باركود-عينة-حقيقي> — لو الـmappings متظبطة، النتيجة هتظهر على التحليل بتاع العينة تلقائياً (matched + auto-applied).

9 اعتبارات أمان وموثوقية

11 النوع التاني: أجهزة ثنائية الاتجاه (Maglumi / Mispa)

DH36 بيبعت النتيجة وخلاص (اتجاه واحد). لكن أجهزة زي Maglumi (Snibe — هرمونات/مناعة) وMispa (Agappe — كيمياء/مناعة) بتشتغل بنمط «الاستعلام» (Host Query): الجهاز بيسأل السيستم الأول عن التحاليل المطلوبة للعينة، يشتغل، وبعدين يبعت.

رحلة العينة في الجهاز ثنائي الاتجاه

① مسح الباركودعلى الجهاز
② Queryالجهاز يسأل
③ work-ordersالسحابة ترد بالتحاليل
④ يشتغلالتحاليل المطلوبة
⑤ نتائجترفع وتتطابق

الفرق بين النوعين

أحادي الاتجاه (DH36)ثنائي الاتجاه (Maglumi / Mispa)
قبل التحليللا شيء — الفني يحدد التحاليل على الجهاز يدوياًالجهاز يستعلم ويجيب التحاليل تلقائياً بالباركود
بعد التحليليبعت النتيجةيبعت النتيجة (نفس المسار)
الإدخال اليدويأكترأقل + أمان أعلى (مفيش تحليل عينة بالغلط)
اللي بنضيفه في الميدل ويرQuery handler (يرد على الاستعلام)

البروتوكولات والربط (Transport)

الجهازالبروتوكولالوصلةالدرايفر المطلوب
DH36 (Dymind)HL7TCP/IPجاهز dymind_hl7
Maglumi (Snibe)HL7 (Query/Result) — أو ASTMTCP/IPHL7 + query handler
Mispa (Agappe)ASTM (LIS2‑A2)Serial (RS‑232) أو TCPدرايفر ASTM جديد

✅ جاهز بالفعل (نعيد استخدامه)

GET /lis/machines/{id}/work-orders بيرجّع الطلبات المعلّقة مترجمة لأكواد الجهاز ومرتبة بالأولوية (STAT الأول). وCloudClient.work_orders() في الميدل وير متكتوبة بالفعل. والمعمارية modular (درايفر لكل بروتوكول).

🔧 اللي يتضاف (Phase 2 + درايفر)

① Query handler: يرد على استعلام الجهاز بالتحاليل (HL7 DSR / ASTM Q‑records). ② درايفر ASTM: إطار ASTM (ENQ/ACK + STX/checksum) + ريكوردات H/P/O/R/Q/L — للأجهزة زي Mispa. ③ (اختياري) فلتر ?barcode= على endpoint الـwork-orders لكفاءة أعلى.

الخلاصة

إضافة Maglumi/Mispa مش بتلمس الباك إند ولا السحابة — كله بيتعمل في الميدل وير: درايفر جديد + منطق الرد على الاستعلام، بيستخدم نفس الـendpoints الجاهزة. ده بالظبط فايدة المعمارية الـmodular.