دراسة جهاز Dymind DH36 (CBC) من المانوال + دراسة الباك إند بعمق → خطة عملية لاستقبال داتا الأجهزة ورفعها للسيستم تلقائياً، وقابلة للتوسّع لأي جهاز.
جهاز 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 اللي هنبنيه.
الجهاز بيتكلم «لغة محلية» (HL7/TCP على شبكة المعمل) — مش HTTPS ومش بيعرف يوصل للسحابة. فمحتاجين طبقة وسيطة تترجم وترفع:
الميدل وير بيتعمل modular — «driver» لكل بروتوكول (HL7 / ASTM / Serial / File). الجهاز الجديد = driver + mapping جديد فقط، من غير أي تغيير في السيستم السحابي. يعني نبدأ بـ DH36 ونوسّع لأي محلل (كيمياء، هرمونات، مزرعة...) بنفس البنية.
درست الباك إند بعمق — البنية التحتية لاستقبال نتائج الأجهزة موجودة ومبنية:
| المكوّن | موجود؟ | التفصيل |
|---|---|---|
جدول الأجهزة 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 + نسخ البرنامج على جهاز ويندوز بالمعمل).
HL7 ORU^R01 عبر TCP على المنفذ 5600.POST /lis/machine-results (HTTPS) ومعاه توكن الخدمة.Entered).داتا الجهاز بتدخل كـ«نتيجة مبدئية» (Entered) مش «معتمدة» — لازم تعدّي على عين الفني في شاشة المراجعة قبل ما تتطلع للمريض. ده بيحمي من أي قراءة غلط من الجهاز.
لكل بارامتر، الميدل وير يعمل 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) للمطابقة اليدوية.
يلعب دور «LIS workstation»: يستمع على المنفذ (5600) ويقبل اتصال الجهاز. يدعم أكتر من جهاز (منفذ/جلسة لكل جهاز).
parser للبروتوكول. نبدأ بـ HL7/MLLP (Dymind). مبني modular عشان نضيف ASTM/Serial/File لأجهزة تانية.
يبعت كل بارامتر لـ /lis/machine-results عبر HTTPS بتوكن الخدمة، مع إعادة محاولة.
لما الجهاز يستعلم عن عينة → الميدل وير يسحب /work-orders ويرد للجهاز بطلبات التحليل (HL7).
لو النت قطع، يخزّن النتائج محلياً ويعيد الرفع لما يرجع — مفيش نتيجة بتضيع.
نبض دوري لـ /heartbeat + سجل اتصالات، عشان شاشة الأجهزة تبيّن online/offline.
الـ«LIS workstation» عادةً جهاز Windows على شبكة المعمل. اخترنا Python وبنينا البرنامج فعلاً:
اتكتب بـمكتبة Python القياسية فقط (stdlib) — يعني صفر pip install. ينسخ على أي ويندوز فيه Python 3.8+ ويشتغل على طول، من غير نت أو تنصيب حزم (ميزة كبيرة في شبكات المعامل المعزولة). كل المكوّنات الستة فوق متبنية + اختُبرت.
الجهاز بيقول «WBC»، السيستم عنده تحليل اسمه «White Blood Cells» بـ id رقم. لازم نعرّف الجسر ده لكل جهاز في شاشة machine-test-mappings:
كود الجهاز machine_test_code | التحليل عندنا investigation_id | الوحدة |
|---|---|---|
| WBC | White Blood Cells | 10³/µL |
| RBC | Red Blood Cells | 10⁶/µL |
| HGB | Hemoglobin | g/dL |
| PLT | Platelets | 10³/µL |
| MCV / MPV / … | … باقي بارامترات CBC | … |
من غير الماببينج، النتيجة بتوصل بس مش بتعرف تتطابق → بتروح صندوق «غير المطابَق». فده أول حاجة بنجهّزها لكل جهاز.
/machine-results + heartbeat. اختُبر end-to-end على الباك إند الحقيقي. DONElis.auto_apply_machine_results، ونسخ البرنامج على جهاز ويندوز بالمعمل. الخطوة الجاية/work-orders — يقلل الإدخال اليدوي ويمنع تحليل عينة بالغلط. الباك إند جاهز؛ يتبقى Query handler في الميدل وير. القسم 11البرنامج اتكتب بالكامل بـ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 # محاكي جهاز
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).
DH36 بيبعت النتيجة وخلاص (اتجاه واحد). لكن أجهزة زي Maglumi (Snibe — هرمونات/مناعة) وMispa (Agappe — كيمياء/مناعة) بتشتغل بنمط «الاستعلام» (Host Query): الجهاز بيسأل السيستم الأول عن التحاليل المطلوبة للعينة، يشتغل، وبعدين يبعت.
GET /lis/machines/{id}/work-orders، يفلتر بالباركود، ويرجّع قائمة أكواد التحاليل للجهاز.| — | أحادي الاتجاه (DH36) | ثنائي الاتجاه (Maglumi / Mispa) |
|---|---|---|
| قبل التحليل | لا شيء — الفني يحدد التحاليل على الجهاز يدوياً | الجهاز يستعلم ويجيب التحاليل تلقائياً بالباركود |
| بعد التحليل | يبعت النتيجة | يبعت النتيجة (نفس المسار) |
| الإدخال اليدوي | أكتر | أقل + أمان أعلى (مفيش تحليل عينة بالغلط) |
| اللي بنضيفه في الميدل وير | — | Query handler (يرد على الاستعلام) |
| الجهاز | البروتوكول | الوصلة | الدرايفر المطلوب |
|---|---|---|---|
| DH36 (Dymind) | HL7 | TCP/IP | جاهز dymind_hl7 |
| Maglumi (Snibe) | HL7 (Query/Result) — أو ASTM | TCP/IP | HL7 + query handler |
| Mispa (Agappe) | ASTM (LIS2‑A2) | Serial (RS‑232) أو TCP | درايفر ASTM جديد |
GET /lis/machines/{id}/work-orders بيرجّع الطلبات المعلّقة مترجمة لأكواد الجهاز ومرتبة بالأولوية (STAT الأول). وCloudClient.work_orders() في الميدل وير متكتوبة بالفعل. والمعمارية modular (درايفر لكل بروتوكول).
① 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.