Nuclear Medicine RIS/PACS Integration
NM integration: NM-RIS, radiopharmacy info system, DICOM MWL/MPPS for NM scanners, dose registry, isotope coding map.
Status: Design β implementation enters module loop after the general RIS adapter (docs/ris-adapter-loop/) ships.
Prerequisite: The HL7v2 RIS adapter (see docs/architecture/ris-adapter-design.md) provides the shared transport layer (MLLP client, dispatch log, config). This document extends it with NM-specific message profiles, radiopharmacy workflow, dose tracking integration, and NM-PACS bridging.
1. Scope
ΰΈ£ΰΈΰΈΰΈ£ΰΈ±ΰΈΰΈΰΈ²ΰΈ£ΰΉΰΈΰΈ·ΰΉΰΈΰΈ‘ΰΉΰΈ’ΰΈΰΈΰΉΰΈΰΈ‘ΰΈΉΰΈ₯ΰΈΰΈ±ΰΈΰΈ£ΰΈ°ΰΈΰΈ Nuclear Medicine Information System (NM-RIS), Radiopharmacy Information System (RPIS), ΰΉΰΈ₯ΰΈ° PACS ΰΈΰΈ΅ΰΉΰΈΰΈ²ΰΈΰΉΰΈ£ΰΈΰΈΰΈ’ΰΈ²ΰΈΰΈ²ΰΈ₯ΰΉΰΈΰΉΰΈΰΈ²ΰΈΰΈΰΈ’ΰΈΉΰΉ β covering TOR Β§3.15.7 (NM integration), Β§4.1.11β4.1.14 (imaging RIS/PACS), and NM-specific requirements for dose tracking, radiopharmaceutical dispensing, and safety compliance.
1.1 In scope
| # | Capability | Protocol | Direction |
|---|---|---|---|
| NM-01 | NM imaging order β hospital NM-RIS | HL7v2 ORM^O01 + ZRI segment | Outbound |
| NM-02 | NM study result / interpretation β NM-RIS | HL7v2 ORU^R01 + OBX (SUV, uptake) | Outbound |
| NM-03 | Radiopharmaceutical dispense request β RPIS | HL7v2 RDE^O11 | Outbound |
| NM-04 | Radiopharmaceutical administration record β RPIS | HL7v2 RAS^O17 | Outbound |
| NM-05 | Inbound NM order status update β NM-RIS | HL7v2 ORM^O01 (status change) | Inbound |
| NM-06 | Inbound NM study result β NM-RIS/PACS | HL7v2 ORU^R01 | Inbound |
| NM-07 | Inbound radiopharmaceutical dispense confirmation β RPIS | HL7v2 RDS^O13 | Inbound |
| NM-08 | DICOM Modality Worklist (MWL) β populate NM scanner | DICOM C-FIND SCU | Outbound |
| NM-09 | DICOM MPPS β scan completion notification | DICOM N-CREATE/N-SET | Inbound |
| NM-10 | Dose accumulation report β Dose Registry | FHIR R4 Observation (deferred) | Outbound |
| NM-11 | Per-tenant NM integration config + audit log | SQL + Admin UI | β |
1.2 Out of scope (deferred)
- DICOMweb WADO-RS study retrieval (PACS viewer integration β separate PACS viewer module)
- Cross-facility NM study transfer (TOR Β§4.1.15 β defer per RIS-Q7)
- Cyclotron / generator supplier ordering API (supply chain, not clinical)
- FHIR ImagingStudy + Observation reverse-mapper (post-HL7v2 stabilisation, same approach as RIS adapter Β§10)
2. Architecture
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β medOS Nuclear Medicine Subsystem β
β β
β imagingRequest (dept=NUCLEAR_MEDICINE) β
β nuclear_med_doses (Supabase) β
β dose_safety_alerts (Supabase) β
β radioisotope_library (Supabase) β
βββββββββββββββββββββββββ¬βββββββββββββββββββββββββββ
β Moleculer events
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β nm-dispatcher.service.ts (NEW) β
β Extends ris-dispatcher with NM-specific logic: β
β β’ Radiopharmacy dose request (RDE^O11) β
β β’ Dose administration event (RAS^O17) β
β β’ NM-enriched ORM with ZRI segment β
β β’ Dose accumulation push to registry β
βββββββββ¬βββββββββββββ¬ββββββββββββββ¬ββββββββββββββββ
β β β
ββββββββββββββ β ββββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββββββββββββββββ βββββββββββββββββββββββββ βββββββββββββββββββββββββββ
β medos-to-orm-nm.mapper β β medos-to-rde.mapper β β medos-to-ras.mapper β
β (ORM^O01 + ZRI) β β (RDE^O11 dispense) β β (RAS^O17 admin record) β
βββββββββββββ¬βββββββββββββ ββββββββββββ¬βββββββββββββ βββββββββββββ¬ββββββββββββββ
β β β
βββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
βΌ
ββββββββββββββββββββββββββββ
β mllp.client.ts (SHARED) β β from RIS adapter
β Outbound MLLP TCP β
βββββββββββββ¬βββββββββββββββ
β TCP
βββββββββββββ΄ββββββββββββββββββββββββββββββββββββ
β β
βΌ βΌ
ββββββββββββββββββββββββ βββββββββββββββββββββββββββββ
β Hospital NM-RIS β β Radiopharmacy Info System β
β (MLLP server) β β (RPIS β MLLP server) β
ββββββββββββββββββββββββ βββββββββββββββββββββββββββββ
Inbound flow (NM-RIS / RPIS β medOS):
NM-RIS / RPIS ββORM/ORU/RDSβββΆ mllp.listener.ts (port 2575, SHARED)
β
βΌ
ββββββββββββββββββββββββββββββββββββββ
β hl7v2-parser.service.ts (SHARED) β
ββββββββββββββββ¬ββββββββββββββββββββββ
βΌ
ββββββββββββββββββββββββββββββββββββββ
β Routing by message type + prefix: β
β ORM + NM-prefix β imagingRequest β
β ORU + NM-prefix β nm result β
β RDS β dose dispense β
ββββββββββββββββ¬ββββββββββββββββββββββ
βΌ
Moleculer actions:
imagingRequest.update
nuclearMed.doseDispensed
nuclearMed.resultFiled
DICOM integration (scanner worklist + completion):
ββββββββββββββββββββββββββββ
β NM Scanner (PET/CT, β
β SPECT/CT, Gamma Camera) β
βββββββββββ¬βββββββββββββββββ
β DICOM C-FIND (MWL query)
βΌ
ββββββββββββββββββββββββββββββββββββ
β dicom-mwl-scp.service.ts (NEW) β
β Serves scheduled NM worklist β
β from imagingRequest entities β
ββββββββββββββββββββββββββββββββββββ
β
β DICOM N-CREATE / N-SET (MPPS)
βΌ
ββββββββββββββββββββββββββββββββββββ
β dicom-mpps-scp.service.ts (NEW)β
β Receives scan-started / β
β scan-completed events β β
β updates imagingRequest status β
ββββββββββββββββββββββββββββββββββββ
3. Files to add / extend
3.1 HL7v2 NM-specific mappers
| File | Status | LOC est. | Purpose |
|---|---|---|---|
services/interoperability/.../modules/hl7v2/mappers/medos-to-orm-nm.mapper.ts |
NEW | ~250 | mapNmRequestToOrm(req, dose, isotope): string β ORM^O01 with NM-specific ZRI custom segment (radioisotope, dose, route, decay-corrected activity) |
services/interoperability/.../modules/hl7v2/mappers/medos-to-rde.mapper.ts |
NEW | ~200 | mapDoseToRde(dose: NuclearMedDose, isotope: Radioisotope): string β RDE^O11 radiopharmaceutical encoded order (RXE segment with isotope code, calibrated activity, calibration time, route) |
services/interoperability/.../modules/hl7v2/mappers/medos-to-ras.mapper.ts |
NEW | ~180 | mapDoseAdminToRas(dose: NuclearMedDose): string β RAS^O17 administration record (RXA segment with administered dose, time, site, lot number) |
services/interoperability/.../modules/hl7v2/mappers/medos-to-oru-nm.mapper.ts |
NEW | ~200 | mapNmResultToOru(req, result, measurements): string β ORU^R01 with NM-specific OBX segments (SUVmax, SUVmean, uptake ratio, ROI measurements) |
services/interoperability/.../modules/hl7v2/mappers/rds-to-medos.mapper.ts |
NEW | ~150 | Inbound RDS^O13 β nuclearMed.doseDispensed β dispense confirmation from RPIS (lot, calibrated activity, expiry, QC pass/fail) |
services/interoperability/.../modules/hl7v2/mappers/orm-to-medos.mapper.ts |
EXTEND | +40 | Add NM-prefix routing: when placer order number matches NM-*, route to imagingRequest with dept=NUCLEAR_MEDICINE + populate dose metadata |
services/interoperability/.../modules/hl7v2/mappers/oru-to-medos.mapper.ts |
EXTEND | +60 | NM result routing: parse quantitative OBX (SUV, uptake) into nuclear_med_doses.measured_dose + imagingRequest.result fields |
3.2 NM dispatcher service
| File | Status | LOC est. | Purpose |
|---|---|---|---|
services/interoperability/.../modules/nm-ris/nm-dispatcher.service.ts |
NEW | ~300 | Moleculer service extending RIS dispatcher pattern. Subscribes to: imagingRequest.created (dept=NM), nuclearMed.dosePrepared, nuclearMed.doseAdministered, nuclearMed.resultReported. Routes to appropriate mapper, sends via shared mllp.client, logs to nm_dispatch_log. |
services/interoperability/.../modules/nm-ris/nm-ris.module.ts |
NEW | ~30 | Module registration |
services/interoperability/.../modules/nm-ris/nm-ris-config.dto.ts |
NEW | ~60 | NM-RIS + RPIS endpoint config DTO (extends RIS config with RPIS-specific fields) |
3.3 DICOM integration (MWL + MPPS)
| File | Status | LOC est. | Purpose |
|---|---|---|---|
services/interoperability/.../modules/dicom/dicom-mwl-scp.service.ts |
NEW | ~250 | DICOM Modality Worklist SCP β responds to C-FIND from NM scanners with scheduled imagingRequest entries (patient demographics, study description, accession number, scheduled isotope/dose) |
services/interoperability/.../modules/dicom/dicom-mpps-scp.service.ts |
NEW | ~200 | DICOM MPPS SCP β receives N-CREATE (scan started) and N-SET (scan completed) β updates imagingRequest.status to in-progress / completed + records actual dose/duration |
services/interoperability/.../modules/dicom/dicom.module.ts |
NEW | ~30 | Module registration |
services/interoperability/.../modules/dicom/dicom-config.dto.ts |
NEW | ~50 | DICOM AE title, port, calling AE whitelist |
3.4 Migrations
| File | Status | LOC est. | Purpose |
|---|---|---|---|
infrastructure/medbase/migrations/<date>_nm_dispatch_log.sql |
NEW | ~100 | nm_dispatch_log β extends ris_dispatch_log pattern with NM-specific columns (radioisotope_code, dose_mbq, dose_round, lot_number) |
infrastructure/medbase/migrations/<date>_nm_ris_config.sql |
NEW | ~80 | nm_ris_config β per-tenant NM-RIS + RPIS endpoint config (extends ris_config with RPIS host/port, DICOM AE title, MWL SCP port) |
infrastructure/medbase/migrations/<date>_nm_integration_seeds.sql |
NEW | ~120 | Seed data: HL7v2 coding system mappings (radioisotope β LOINC/SNOMED, NM procedure β CPT/ICD-10-PCS), DICOM modality codes |
3.5 Tests
| File | Status | LOC est. | Purpose |
|---|---|---|---|
services/interoperability/.../modules/hl7v2/__tests__/medos-to-orm-nm.spec.ts |
NEW | ~180 | NM ORM mapper: ZRI segment, decay correction, multi-dose rounds |
services/interoperability/.../modules/hl7v2/__tests__/medos-to-rde.spec.ts |
NEW | ~150 | RDE mapper: RXE segment, calibration time math, isotope coding |
services/interoperability/.../modules/hl7v2/__tests__/medos-to-ras.spec.ts |
NEW | ~120 | RAS mapper: RXA segment, lot tracking, waste dose |
services/interoperability/.../modules/hl7v2/__tests__/rds-to-medos.spec.ts |
NEW | ~120 | Inbound dispense confirmation parsing |
services/interoperability/.../modules/nm-ris/__tests__/nm-dispatcher.spec.ts |
NEW | ~180 | End-to-end dispatch: event β mapper β MLLP mock β log |
services/interoperability/.../modules/dicom/__tests__/dicom-mwl-scp.spec.ts |
NEW | ~150 | MWL C-FIND response generation |
services/interoperability/.../modules/dicom/__tests__/dicom-mpps-scp.spec.ts |
NEW | ~120 | MPPS N-CREATE/N-SET status updates |
Total estimate: ~3,000 LOC including tests (NM-specific mappers + DICOM layer + dispatcher + migrations).
4. HL7v2 Message Templates
4.1 Outbound ORM^O01 β NM imaging order (with ZRI custom segment)
MSH|^~\&|MEDOS|{sending_facility}|{nm_ris_app}|{nm_ris_facility}|{timestamp}||ORM^O01|{msg_id}|P|2.5
PID|1||{patient.hn}^^^MEDOS^MR||{patient.last_name}^{patient.first_name}||{patient.dob}|{patient.gender}|||{patient.address}||{patient.phone}
PV1|1|{patient_class}|{encounter.location}||||{ordering_doctor.id}^{ordering_doctor.last_name}^{ordering_doctor.first_name}
AL1|1|DA|{allergy_code}^{allergy_name}|{severity}||{reaction}
ORC|NW|NM-{imagingRequest._id}|||SC|{priority}|||{request.created_at}|||{requester.id}^{requester.last_name}^{requester.first_name}
OBR|1|NM-{imagingRequest._id}||{procedure.code}^{procedure.name}^MEDOS|{priority}|{request.created_at}|||||A||{clinical_indication}||||{accession_number}
ZRI|1|{radioisotope.code}^{radioisotope.name}^SNOMED|{ordered_dose_mbq}|MBq|{administration_route.code}^{administration_route.name}|{calibration_datetime}|{decay_corrected_activity_mbq}|{dose_round}|{total_rounds}|{preparation_instructions}
ZRI segment definition (custom, medOS-to-NM-RIS):
| Field | Name | Type | Description |
|---|---|---|---|
| ZRI-1 | Set ID | SI | Sequential (1 for single-dose, 1β¦N for multi-round) |
| ZRI-2 | Radioisotope | CWE | SNOMED code + name (e.g., 349358000^Technetium-99m^SCT) |
| ZRI-3 | Ordered Dose | NM | Activity in MBq at calibration time |
| ZRI-4 | Dose Unit | CWE | MBq (default) or mCi |
| ZRI-5 | Administration Route | CWE | IV, oral, inhaled, etc. |
| ZRI-6 | Calibration DateTime | DTM | When the dose was calibrated |
| ZRI-7 | Decay-Corrected Activity | NM | Activity at scheduled admin time (calculated) |
| ZRI-8 | Dose Round | NM | Current round number (1-based) |
| ZRI-9 | Total Rounds | NM | Total planned rounds for this study |
| ZRI-10 | Preparation Instructions | TX | Free text (prep, fasting, hydration, etc.) |
4.2 Outbound RDE^O11 β Radiopharmaceutical dispense request
MSH|^~\&|MEDOS|{sending_facility}|{rpis_app}|{rpis_facility}|{timestamp}||RDE^O11|{msg_id}|P|2.5
PID|1||{patient.hn}^^^MEDOS^MR||{patient.last_name}^{patient.first_name}||{patient.dob}|{patient.gender}|||{patient.address}
ORC|NW|NM-{imagingRequest._id}-DOSE-{dose_round}|||SC|||{request.created_at}|||{requester.id}
RXE|1|{radioisotope.code}^{radioisotope.name}^SNOMED|{ordered_dose_mbq}||MBq|{administration_route}|{calibration_datetime}|{scheduled_admin_time}||{special_instructions}
RXR|{route.code}^{route.name}||{site.code}^{site.name}
4.3 Outbound RAS^O17 β Dose administration record
MSH|^~\&|MEDOS|{sending_facility}|{rpis_app}|{rpis_facility}|{timestamp}||RAS^O17|{msg_id}|P|2.5
PID|1||{patient.hn}^^^MEDOS^MR||{patient.last_name}^{patient.first_name}||{patient.dob}|{patient.gender}
ORC|RE|NM-{imagingRequest._id}-DOSE-{dose_round}
RXA|1|1|{admin_start_time}|{admin_end_time}|{radioisotope.code}^{radioisotope.name}^SNOMED|{measured_dose_mbq}|MBq|{administration_route}|{admin_site}|{administering_provider.id}^{provider.last_name}^{provider.first_name}|{lot_number}|{expiry_date}|{manufacturer}
RXR|{route.code}^{route.name}||{site.code}^{site.name}
OBX|1|NM|WASTE-DOSE^Waste Dose^MEDOS||{waste_dose_mbq}|MBq|||||F
OBX|2|NM|RESIDUAL^Residual Activity^MEDOS||{residual_activity_mbq}|MBq|||||F
4.4 Outbound ORU^R01 β NM study result with quantitative measurements
MSH|^~\&|MEDOS|{sending_facility}|{nm_ris_app}|{nm_ris_facility}|{timestamp}||ORU^R01|{msg_id}|P|2.5
PID|1||{patient.hn}^^^MEDOS^MR||{patient.last_name}^{patient.first_name}||{patient.dob}|{patient.gender}
OBR|1|NM-{imagingRequest._id}||{procedure.code}^{procedure.name}^MEDOS||{request.created_at}|||||{result_status}||{clinical_indication}||||{accession_number}|||{result_status}
OBX|1|TX|FINDINGS^Findings^MEDOS||{report.findings}|||N|||F
OBX|2|TX|IMPRESSION^Impression^MEDOS||{report.impression}|||N|||F
OBX|3|NM|SUV-MAX^SUVmax^MEDOS||{suv_max}|{SUV}|||||F
OBX|4|NM|SUV-MEAN^SUVmean^MEDOS||{suv_mean}|{SUV}|||||F
OBX|5|NM|UPTAKE-RATIO^Uptake Ratio^MEDOS||{uptake_ratio}||||||F
OBX|6|NM|ROI-1^{roi_name}^MEDOS||{roi_measurement}|{roi_unit}|||||F
OBX|7|CWE|RADIOISOTOPE^Radioisotope Used^MEDOS||{radioisotope.code}^{radioisotope.name}^SNOMED||||||F
OBX|8|NM|ADMIN-DOSE^Administered Dose^MEDOS||{actual_dose_mbq}|MBq|||||F
OBX|9|DTM|ADMIN-TIME^Administration Time^MEDOS||{admin_datetime}||||||F
OBX|10|NM|SCAN-DURATION^Scan Duration^MEDOS||{duration_minutes}|min|||||F
4.5 Inbound RDS^O13 β Radiopharmaceutical dispense confirmation from RPIS
Expected from RPIS when the radiopharmacist has prepared and QC-passed the dose:
MSH|^~\&|{rpis_app}|{rpis_facility}|MEDOS|{medos_facility}|{timestamp}||RDS^O13|{msg_id}|P|2.5
PID|1||{patient.hn}^^^MEDOS^MR
ORC|RE|NM-{imagingRequest._id}-DOSE-{dose_round}|||CM
RXD|1|{radioisotope.code}^{radioisotope.name}^SNOMED|{dispensed_activity_mbq}|MBq|{calibration_time}|{lot_number}|{expiry}|{manufacturer}|{qc_result}
4.6 Cancel control
Reuse ORM^O01 with ORC|CA|NM-{imagingRequest._id}... β same as RIS adapter Β§4.3, but with placer order number prefix NM-.
5. Configuration Model
-- Extends ris_config with NM-specific endpoints
CREATE TABLE IF NOT EXISTS nm_ris_config (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
-- NM-RIS endpoint (may be same as general RIS or separate)
nm_ris_enabled BOOLEAN NOT NULL DEFAULT FALSE,
nm_ris_host TEXT,
nm_ris_port INT DEFAULT 2575,
nm_ris_sending_app TEXT NOT NULL DEFAULT 'MEDOS',
nm_ris_sending_facility TEXT,
nm_ris_receiving_app TEXT,
nm_ris_receiving_facility TEXT,
-- Radiopharmacy Information System (RPIS) endpoint
rpis_enabled BOOLEAN NOT NULL DEFAULT FALSE,
rpis_host TEXT,
rpis_port INT DEFAULT 2576,
rpis_sending_app TEXT NOT NULL DEFAULT 'MEDOS',
rpis_sending_facility TEXT,
rpis_receiving_app TEXT,
rpis_receiving_facility TEXT,
-- DICOM Modality Worklist SCP
mwl_enabled BOOLEAN NOT NULL DEFAULT FALSE,
mwl_ae_title TEXT NOT NULL DEFAULT 'MEDOS_NM_MWL',
mwl_port INT DEFAULT 4242,
mwl_allowed_calling_aets TEXT[] DEFAULT '{}', -- whitelist of scanner AE titles
-- DICOM MPPS SCP
mpps_enabled BOOLEAN NOT NULL DEFAULT FALSE,
mpps_ae_title TEXT NOT NULL DEFAULT 'MEDOS_NM_MPPS',
mpps_port INT DEFAULT 4243,
-- Dose tracking
dose_unit_preference TEXT NOT NULL DEFAULT 'MBq' CHECK (dose_unit_preference IN ('MBq','mCi')),
auto_decay_correction BOOLEAN NOT NULL DEFAULT TRUE,
send_dose_to_registry BOOLEAN NOT NULL DEFAULT FALSE,
dose_registry_endpoint TEXT, -- FHIR endpoint for national dose registry
-- MLLP shared settings
mllp_timeout_ms INT NOT NULL DEFAULT 30000,
retry_attempts INT NOT NULL DEFAULT 3,
retry_backoff_ms INT NOT NULL DEFAULT 1000,
encoding TEXT NOT NULL DEFAULT 'UTF-8',
hl7_version TEXT NOT NULL DEFAULT '2.5',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE(tenant_id)
);
COMMENT ON TABLE nm_ris_config IS
'Per-tenant Nuclear Medicine integration config β NM-RIS, RPIS, DICOM MWL/MPPS, dose registry';
Admin UI: /admin/integrations/nm-ris-config β extends the RIS config page with NM-specific tabs.
6. Dispatch Log
-- NM-specific dispatch log (extends ris_dispatch_log pattern)
CREATE TABLE IF NOT EXISTS nm_dispatch_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
direction TEXT NOT NULL CHECK (direction IN ('outbound','inbound')),
target_system TEXT NOT NULL CHECK (target_system IN ('nm_ris','rpis','dicom_mwl','dicom_mpps','dose_registry')),
imaging_request_id TEXT, -- mongo _id reference
dose_id UUID, -- FK to nuclear_med_doses.id
message_type TEXT NOT NULL, -- 'ORM^O01', 'ORU^R01', 'RDE^O11', 'RAS^O17', 'RDS^O13', 'C-FIND', 'N-CREATE', 'N-SET'
message_control_id TEXT NOT NULL, -- MSH-10 (HL7v2) or DICOM message ID
raw_message TEXT, -- HL7v2 string or DICOM dataset summary
-- NM-specific audit fields
radioisotope_code TEXT, -- SNOMED code of radioisotope involved
dose_mbq NUMERIC(12,4), -- dose activity at time of message
dose_round INT, -- which round (1-based)
lot_number TEXT, -- radiopharmaceutical lot (from dispense)
qc_result TEXT CHECK (qc_result IN ('pass','fail','pending',NULL)),
status TEXT NOT NULL CHECK (status IN ('pending','sent','acked','naked','timeout','error')),
ack_code TEXT, -- AA / AE / AR
error_message TEXT,
attempt INT NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
responded_at TIMESTAMPTZ
);
CREATE INDEX idx_nm_dispatch_log_imaging_request ON nm_dispatch_log (imaging_request_id);
CREATE INDEX idx_nm_dispatch_log_dose ON nm_dispatch_log (dose_id);
CREATE INDEX idx_nm_dispatch_log_status_pending ON nm_dispatch_log (status) WHERE status IN ('pending','error');
CREATE INDEX idx_nm_dispatch_log_target ON nm_dispatch_log (target_system, created_at DESC);
7. State Machines
7.1 NM Order Dispatch (ORM)
NM imagingRequest created (dept=NUCLEAR_MEDICINE)
β
βββ nm_dispatch_log row (target=nm_ris, type=ORM^O01, status=pending)
β
βΌ
nm-dispatcher composes ORM^O01 + ZRI via medos-to-orm-nm.mapper
β
βΌ
Send via mllp.client β NM-RIS
β
βββ[ACK AA]βββΆ status=acked β imagingRequest.status='accepted'
βββ[ACK AE]βββΆ status=naked β log error, manual review
βββ[timeout]βββΆ retry up to N β status=timeout
βββ[exception]ββΆ status=error
7.2 Radiopharmacy Workflow
NM imagingRequest accepted
β
βΌ
nm-dispatcher sends RDE^O11 to RPIS (dispense request)
β
βββ nm_dispatch_log row (target=rpis, type=RDE^O11)
β
βΌ
RPIS prepares radiopharmaceutical + QC
β
βΌ
RPIS sends RDS^O13 to medOS (dispense confirmation)
β
βΌ
rds-to-medos.mapper parses β nuclearMed.doseDispensed action
β
βββ Updates nuclear_med_doses: status='dispensed', lot_number, qc_result, calibrated_activity
βββ Updates dose_safety_alerts if accumulated dose nears limit
β
βΌ
Dose administered by technologist (recorded in medOS)
β
βΌ
nm-dispatcher sends RAS^O17 to RPIS (administration record)
β
βββ nuclear_med_doses.status='administered'
βββ patient_dose_accumulations updated (via existing Postgres trigger)
βββ dose_safety_alerts evaluated (via existing trigger)
7.3 DICOM Scanner Integration
NM imagingRequest scheduled (status='scheduled')
β
βΌ
NM Scanner sends DICOM C-FIND (MWL query)
β
βΌ
dicom-mwl-scp responds with matching worklist entries
(patient demographics, study, accession, isotope, dose)
β
βΌ
Technologist selects study on scanner console
β
βΌ
Scanner sends N-CREATE (MPPS: scan started)
β
βΌ
dicom-mpps-scp β imagingRequest.status='in-progress'
β
βΌ
Scan completes
β
βΌ
Scanner sends N-SET (MPPS: scan completed, actual dose, duration)
β
βΌ
dicom-mpps-scp β imagingRequest.status='completed'
β
βββ Updates nuclear_med_doses: actual scan duration, post-admin residual
βββ nm_dispatch_log row (target=dicom_mpps, type=N-SET, status=acked)
8. Radioisotope Coding System Mapping
For HL7v2 interoperability, medOS maps its internal radioisotope_library to standard coding systems:
| medOS Radioisotope | SNOMED CT | LOINC (dose observation) | Half-life | Common Procedures |
|---|---|---|---|---|
| Tc-99m | 349358000 | 10419-2 | 6.01 h | Bone scan, thyroid, renal, cardiac SPECT, DMSA, DTPA, MAG3, HIDA, lung V/Q |
| I-131 | 55750003 | 10420-0 | 8.02 d | Thyroid therapy, thyroid uptake, MIBG therapy |
| I-123 | 70076005 | 10421-8 | 13.22 h | Thyroid imaging, MIBG diagnostic |
| F-18 FDG | 422855000 | 10422-6 | 109.77 min | PET/CT oncology, cardiac PET, brain PET |
| Ga-68 | 423413002 | 10423-4 | 67.71 min | DOTATATE PET/CT (neuroendocrine), PSMA PET/CT (prostate) |
| Tl-201 | 14673006 | 10424-2 | 72.91 h | Myocardial perfusion (legacy) |
| In-111 | 422562002 | 10425-9 | 67.32 h | WBC scan (infection), octreotide scan |
| Lu-177 | 423688004 | β | 6.65 d | DOTATATE therapy, PSMA therapy |
| Ra-223 | β | β | 11.43 d | Bone metastases therapy (Xofigo) |
| Y-90 | 423316001 | β | 64.05 h | Radioembolization (SIRT), radiosynovectomy |
These mappings are seeded in nm_integration_seeds.sql and used by the ORM/ORU/RDE/RAS mappers for the CWE-typed radioisotope fields.
9. Dose Safety Integration Points
The NM integration layer connects to the existing dose safety system (nuclear_med_doses β patient_dose_accumulations β dose_safety_alerts β acknowledgement_requests):
| Event | Source | Integration Action |
|---|---|---|
| Dose dispensed by RPIS | Inbound RDS^O13 | Update nuclear_med_doses.status='dispensed', calibrated_activity, lot_number; triggers accumulation recalc |
| Dose administered | Outbound RAS^O17 | nuclear_med_doses.status='administered'; Postgres trigger fires β patient_dose_accumulations updated β dose_safety_alerts evaluated |
| Critical safety alert | dose_safety_alerts Postgres trigger |
acknowledgement_requests row inserted (template: nuclear_medicine.safety.alert_critical) β AcknowledgementInbox FAB notifies radiopharmacist |
| Scan completed | Inbound DICOM MPPS N-SET | Update nuclear_med_doses with actual scan parameters; residual activity logged |
| Dose registry push | patient_dose_accumulations threshold |
If send_dose_to_registry=true, push FHIR Observation to configured registry endpoint |
10. Open Questions
| # | Question | When | Default if no answer |
|---|---|---|---|
| NM-Q1 | Does the hospital run a separate NM-RIS or is NM handled within the general RIS? | Pre-impl | Assume separate; if same, NM-RIS config points to same endpoint as ris_config with NM- prefix routing |
| NM-Q2 | Does the hospital have a standalone Radiopharmacy Information System (RPIS)? | Pre-impl | If no RPIS, skip RDE/RAS/RDS messages; dose tracking stays internal to medOS |
| NM-Q3 | DICOM library choice: dcmjs (pure JS) vs fo-dicom (C# via edge) vs pynetdicom (Python sidecar)? |
Stage-04 | dcmjs for MWL/MPPS SCP β keeps everything in Node.js, same runtime as interoperability service |
| NM-Q4 | Custom ZRI segment vs OBR-31 (reason for study) for radioisotope/dose fields? | Mapper impl | ZRI custom segment (more structured); OBR-31 is free-text and loses structured data. Document in conformance profile. |
| NM-Q5 | Should decay correction use calibration time from RPIS (RDS) or from medOS nuclear_med_doses.calibration_time? |
Mapper impl | Prefer RPIS-provided calibration time (more accurate); fall back to medOS if RPIS not connected |
| NM-Q6 | Dose unit: always MBq or configurable per-tenant (MBq vs mCi)? | Config | Configurable via nm_ris_config.dose_unit_preference; mappers convert using 1 mCi = 37 MBq |
| NM-Q7 | National dose registry format: FHIR Observation or proprietary? | Deferred | FHIR R4 Observation with radiation-dose profile; implementation deferred until a specific registry is identified |
11. Dependency on RIS Adapter
The NM integration reuses these components from the general RIS adapter (docs/architecture/ris-adapter-design.md):
| Component | Reuse Type | Notes |
|---|---|---|
mllp.client.ts |
Direct reuse | Same outbound MLLP TCP client; NM dispatcher passes NM-RIS/RPIS endpoints |
mllp.listener.ts |
Direct reuse | Same inbound listener on port 2575; message routing extended for RDS^O13 |
hl7v2-parser.service.ts |
Direct reuse | Parser is message-type-agnostic |
hl7v2-ack.builder.ts |
Direct reuse | ACK/NAK format is the same |
ris_dispatch_log pattern |
Pattern reuse | NM has its own nm_dispatch_log with extra columns but same lifecycle |
ris_config pattern |
Pattern reuse | NM has its own nm_ris_config with RPIS + DICOM fields |
| Order number prefix routing | Extend | NM- prefix in placer order number distinguishes NM from general imaging (IMG-/RAD-) |
Implementation order: RIS adapter first (shared transport), then NM integration (NM-specific mappers + RPIS + DICOM).
12. Module Loop Entry Point
# In a fresh Claude session (after RIS adapter loop completes):
"Run the medOS module-implementation loop for the **nm-ris-integration** module
in interactive mode. Read docs/module-loop/00-MASTER.md and
docs/architecture/nuclear-medicine-ris-pacs-integration.md.
Pre-flight:
- Branch: stay on current branch.
- State file: docs/nm-ris-integration-loop/_progress.json
- Spec source: docs/architecture/nuclear-medicine-ris-pacs-integration.md
- Prerequisite: RIS adapter (docs/ris-adapter-loop/) must be complete β
mllp.client.ts + ris-dispatcher exist and pass tests.
Stage 01 should audit:
1. The shared HL7v2 module (services/interoperability/.../modules/hl7v2/)
2. The existing NM data models (nuclear_med_doses, radioisotope_library, etc.)
3. The RIS adapter output (mllp.client, ris-dispatcher)
Then proceed through stages 02-10 per the module loop framework.
"
Estimated effort: 2β3 days (larger than the RIS adapter due to RPIS messages + DICOM MWL/MPPS layer).
13. Cross-References
- docs/architecture/ris-adapter-design.md β prerequisite RIS adapter (shared transport)
- docs/architecture/nuclear-medicine-remaining-backend-work.md β Β§1 (RIS adapter) is the prerequisite; Β§2 (URGENT dispatch) ships separately
- docs/architecture/imaging-acknowledgement-shapes.md β
nuclear_medicine.safety.alert_criticaltemplate - docs/architecture/acknowledgement-system.md β AcknowledgementRequest entity used by dose safety alerts
- docs/architecture/cds-vital-signs-rules.md β CDS rules can fire on dose observations (extension point)
- Existing NM schema:
infrastructure/medbase/migrations/20260503_imaging_nm_phase2.sql - Existing NM frontend:
web/src/services/nuclearMedicine.service.ts,web/src/types/nuclear-medicine.types.ts - Existing HL7v2 module:
services/interoperability/src/api/interoperability/modules/hl7v2/ - Existing FHIR ImagingStudy:
services/public-api/.../modules/fhir/resources/imagingStudy.controller.ts