XyDromatics VNA Migration

Archive Totals

DICOM C-STORE SCP

Host

Recent Studies

Patient IDAccessionStudy DateModality Study UIDSeriesInstances
Loading…

Study Search

Results

Patient IDAccessionStudy DateModality DescriptionStudy UID SeriesInstances
Enter a search criterion and click Search.

DICOM Peers

Remote modalities and PACS systems the Repository interacts with. Sources (inbound ◀) are peers allowed to send us studies via C-STORE. Destinations (outbound ▶) are peers we forward studies to when answering C-MOVE requests from a Query/Retrieve SCU. A single peer can be both.

NameAE TitleHostPort DirectionTLSEnabledActions
Loading…

Add Node

Advanced — presentation-context filters (optional)
Leave empty to accept whatever the peer proposes (the common case). Non-empty = allow-list: inbound (source role) only accepts PCs whose SOP/TS appear here; outbound (dest role) only proposes these to the peer. Values are raw DICOM UIDs, one per line or comma-separated.

Role Matrix

The role matrix — including the shared admin / operator / viewer triad and any custom roles — is managed on a dedicated page. Changes persist in role_matrix.json inside the data directory.

Installed License

Loading…

Upload License

Select a .lic file issued by the Synthology License Generator. The file is staged, decrypted, and — on successful parse — replaces the active license. Feature gating is re-evaluated on the next request; no restart is required.

Authentication & SCP

fo-dicom DicomServiceOptions.MaxPDULength. 256 KB reduces association round-trips for migration transfers; pre-association negotiation will clamp if a peer doesn’t support it. 0 = use fo-dicom stock 16384. Restart the service to apply.

Max simultaneous inbound DICOM associations across all listeners. 0 = unlimited (legacy / lab only). Default 10.

Max simultaneous outbound DICOM associations issued FROM this host (C-MOVE / C-STORE / C-FIND to source PACSes). 0 = unlimited. Default 10. Restart to apply.

Webhook Authentication

Gates the POST /api/batch_sources/webhook seed endpoint that external systems hit to feed migration batches. When the toggle is off the endpoint is anonymous (back-compat default). API keys + optional HMAC + optional IP allowlist cover the three usual second-factors for inbound webhooks. The curl example you hand to integrators lives in External Batch Sources → REST/Webhook.

API keys

SynthGateway Support Agent — Identity (DCR-2026-197)

Sets the per-host identity that the in-process SupportAgent uses to connect to SynthGateway. Stored in . Changes require a SupportAgent service restart on this host.

Archive Catalog Database

Current engine
Configured

Changing the archive catalog DB engine or connection string requires a service restart. The SCP will refuse to start until the catalog is reachable.

Health

SCP Status

Archive Counts

Source PACS Catalog

Loading…

Sources

Read-only view filtered to nodes whose role is SourceOnly or Both. Phase 2H.2 adds the retrieval-job orchestration that consumes these source AEs for batch C-FIND / C-MOVE retrieval from legacy PACS.

Loading…

External Batch Sources

Channels that feed migration batches into this host's retrieval pipeline without operator intervention. Each accepted seed becomes a MigrationRetrievalJob batch and flows through the same C-FIND/C-MOVE state machine, idempotent pre-flight (DCR-2026-274), pass-attempt provenance (DCR-2026-273), and post-migration reconciliation (DCR-2026-264) as wizard-built batches.

Loading channels…

REST / Webhook

DCR-2026-258 + DCR-2026-292 auth

POST a BatchSeed JSON body to the endpoint below. The dispatcher deduplicates by (channel, external_id) — repeat calls with the same external_id return the prior batch_id rather than re-queueing. Empty external_id always creates a new batch.



            
            
          

HL7 MLLP (ORM/ADT)

DCR-2026-259

Listens on the existing MLLP port for ORM^O01 (orders) and ADT^A08 (patient updates). Each accepted message becomes a BatchSeed dispatched through the same pipeline as REST/Webhook. Per-sender mapping resolves MSH-3 SendingApp / MSH-4 SendingFacility → DICOM source-node-id; unmapped senders fall through to the catch-all (or are inbox-only when no catch-all is set).

Per-sender mappings

File-drop watcher (CSV/JSON)

DCR-2026-260

Polls a configured directory for CSV / JSON worklist files (operators drop via cron / SFTP / batch jobs). Each parsed file becomes one BatchSeed. Successful files move to {dir}/processed/; failed files move to {dir}/failed/ with a sidecar .error.txt explaining the parse failure.

Accepted formats & sample files

CSV (header row required)

accession_number,patient_id
ACC-2026-0001,MRN001
ACC-2026-0002,MRN002

Header column aliases (case-insensitive): accession_number / accession / acc, study_instance_uid / study_uid, patient_id / mrn.

JSON (object form)

{
  "external_id": "epic-overnight-2026-05-23",
  "batch_name": "Epic overnight",
  "accession_numbers": ["ACC-2026-0001", "ACC-2026-0002"],
  "study_instance_uids": [],
  "patient_id": "",
  "force_full_repull": false
}

JSON also accepts: a bare array of accession-number strings (["ACC-001", "ACC-002"]) for the simplest case. Per-file fields override the defaults above.

Advanced header / key mapping (custom CSV columns + JSON keys)

Operator-typed aliases are checked FIRST (in the listed order), then the legacy defaults. Matching is case-insensitive. Aliases never replace defaults — they prepend.

FHIR poller (ImagingStudy / ServiceRequest)

DCR-2026-261

Polls a configured FHIR R4 base URL every PollIntervalMinutes (default 5) for new ImagingStudy + ServiceRequest resources. Uses _lastUpdated=gt{cursor} so each sweep only fetches resources changed since the prior poll. Accession is extracted from identifier.type.coding.code = "ACSN"; DICOM Study UID from identifier.system = "urn:dicom:uid".

Advanced identifier mapping (custom accession / study UID / patient ID paths)

Defaults match the pre-DCR-299 behavior (v2.0203 ACSN type code, urn:dicom:uid system, subject.identifier.value patient path). Add lines for vendor-prefixed identifier systems or custom code-system slugs.

Advanced TLS (mTLS / custom CA / insecure skip)
Cursor state (advance after each successful sweep; reset to force backfill):
Last ImagingStudy:(never) Last ServiceRequest:(never)

Direct DB connector (read-only)

DCR-2026-262

Use when the legacy PACS has no API. Operator authors a parameterized SELECT query that returns one row per study-to-migrate; the connector executes it on a cadence, binds @since to the persisted cursor, and dispatches one BatchSeed per row.

Cursor state: (never)

SOAP web service

DCR-2026-294

For legacy hospital systems exposing SOAP web services (DICOMweb precursors, IHE PIX/PDQ v3, HL7v3 wrappers, vendor proprietary endpoints). Operator authors a full SOAP envelope with {since} / {limit} / {wssecurity_header} placeholders. The poller POSTs on a cadence, parses the response via XPath, and dispatches one BatchSeed per row.

Quick start:
Advanced TLS (mTLS / custom CA / insecure skip)
Cursor state: (never)

Recent seeds

Loading…

Migration Wizard

Build one or more retrieval batches. Each batch picks its own source PACS, batch name, and list of accessions or study UIDs. The background MigrationRetrievalService runs C-FIND + C-MOVE for every entry in every batch.

Multi-batch tip (Phase 2J.2): use "Add another batch" to queue several batches in one submit. Batches can target different source PACS — useful for hospital-network consolidations that span multiple legacy systems.

Bulk import from delimited text or file

Paste CSV/TSV/pipe-delimited text, or upload a file (.csv / .tsv / .psv / .txt; 5 MB max). Pick the separator + column that holds the accession or study UID, then append the parsed values into the lists below.

Migration Dashboard

Loading…
Batches
Loading…

Control

Recent jobs

Loading…

Migration Analytics

Totals

loading…

Studies per day

Status histogram

Modality distribution

Per-source throughput

Expanded Analytics (DCR-2026-302)

Executive Summary
Studies migrated
Instances moved
Completion rate
Hours saved (vs manual)
Distinct sources
Distinct batches
First seen
Last completed
Window

All Expanded Analytics charts below respect the window + the Filter bar above (Study date / Source / Modality).

Throughput over time
Status mix
Studies + images by source
Channel mix (intake by channel)
PassAttempt distribution (DCR-2026-273)
Idempotent skip rate (DCR-2026-274)

Pre-flight skipped studies that were already in the destination, saving bandwidth + retrieval time.

Time-to-complete percentiles
Source health scorecard
Drill-down

DICOM Normalization

Runs first in the ingest pipeline (before Validation / MissingData / Reconciliation / TagMapping / QualityGate / Discovery). Brings non-PS3.x-compliant DICOM from direct legacy-PACS retrieval into shape so downstream engines have correct input.

Leave Enabled = off when ingesting via the Migration Engine route (data already normalized upstream). Turn on when ingesting direct from a legacy PACS via C-MOVE or file drop.

Policies

Stats since boot

loading…

Study Locations

Every federated search hit upserts a (study UID, source PACS) row into this catalog. Browse the accumulated history without re-running C-FIND. Use the Tie-Breaker Rules pane to control which source the federated-search pane picks as "Candidate" when a study lives in multiple legacy systems.

Records

Source Tie-Breaker Rules

When a federated search finds the same Study UID at multiple source PACSes, the resolver applies these per-source-pair rules to pick a Candidate retrieval source. Rules are advisory: the federated-search pane decorates rows with the candidate but the operator still confirms the source in the wizard.

Default behavior when no rule is configured for a pair = first-found wins (unchanged from Phase 2J.3). Add a rule for any pair where you want different behavior.

Configured rules

Test a rule

Plug in two sources and synthetic counts; preview which one the resolver would pick under the currently-saved rule for that pair.

Post-Migration Reconciliation

Auto-fired whenever a batch reaches all-terminal (every job complete / failed / cancelled). For each completed job, queries source PACS via C-FIND and compares against the local archive (series + instance counts). Studies with a delta land here for triage.

Enable auto-requeue on partial per batch to have the service spawn a fresh single-shot retrieval job for missing/partial studies automatically (max_attempts=1 to prevent loops). DCR-2026-272 (filed approved + parked) extends this with instance-level C-MOVE for the second pass.

Background service

loading…

Per-batch summaries

loading…

Reconciliation results

loading…

DICOM Validation

Loading…

Rules

Loading…

Editor

Schema: id, name, description, tag (DICOM keyword), rule_type (format / enum / length / regex / derived / custom), enabled, on_fail (Correct / Flag / Reject / Warn / Ignore), valid_values, correction_map, pattern, format (DA / TM / AS), max_length, min_length, derivation_method (compute_age / sop_to_modality), derivation_source.

Test rules against sample tags

JSON dict of sample DICOM tags. Engine evaluates rules in memory; no persisted state touched.

Missing Data Rules

Loading…

Rules

Loading…

Editor

Schema: id, name, description, tag, enabled, priority, policy (Default / Derive / Lookup / Flag / Reject / Ignore), default_value (also format-template source), derivation_method (keyword_extract / compute_age / sop_to_modality / copy_tag / format_template), derivation_source, derivation_dictionary, lookup_table, lookup_key, condition_tag, condition_value.

Keyword Dictionaries

Used by keyword_extract derivation. Edit a dictionary's entries below — keys are output values, values are arrays of keywords that match (case-insensitive, longest-match-wins).

Loading…

Test rules against sample tags

Sample tags with blank values are the targets — fill in the auxiliary tags so derivations have something to work with.

Patient Reconciliation

Loading…

Config

enabled, auto_merge_threshold (≥ this → auto-apply mapped MRN), flag_threshold (≥ this and < auto → push to manual queue), strip_leading_zeros, mrn_prefix, mrn_suffix, name_similarity_min, dob_exact_match, dob_tolerance_days, require_sex_match, unmatched_policy (flag / reject / accept), match_order (array of strategy names in priority order).

Known patients (in-memory registry)

Registry seeds via Seed from archive (one-shot QueryStudies grouped by PatientID, denormalized name only; DOB/Sex backfill as ingest hits accept policy).

Loading…

Manual reconciliation queue

Loading…

Test reconcile against a sample patient

Discovery Scanner

Loading…

Control

When a scan is running, every inbound C-STORE / DICOMweb STOW dataset is observed and counted (no pipeline change). On Complete, the scanner generates per-tag stats + auto-suggested crosswalk mappings + a list of data-quality issues. Populate crosswalks pre-fills the Phase 2E tables with discovered values (blank mappings; operator fills the destinations).

Issues

No completed scan.

Per-tag analysis

No completed scan.

Tag Mapping Rules

Rules

Loading…

Editor

Schema: id, name, priority (lower = earlier), enabled, description, conditions[] (tag, operator: is_blank/is_not_blank/equals/not_equals/contains/not_contains/starts_with/ends_with/matches_wildcard, value, case_insensitive), actions[] (tag, action_type: set/copy/prepend/append/regex/clear/lookup, value, value2, lookup_table). Phase 2C = CRUD only. Engine apply is Phase 2D.

Test rules against sample tags

JSON dict of { "KeywordOrHex": "value", ... }. Keywords use the same fallback chain as the engine (preset -> DicomTag.Parse hex -> fo-dicom keyword). Engine applies every enabled rule in priority order against a per-rule clone of this dataset; the table below lists rules that matched + their changes.

Crosswalk Tables

Loading…

Tables

Loading…

Editor

Schema: name (filename slug; no spaces), display_name, description, dicom_tag (the DICOM keyword whose changes auto-record into this table — e.g. AccessionNumber), hl7_field (informational), auto_capture (bool), case_sensitive (bool), default_value, entries (object: { "old": "new", ... }; wildcards * / ? supported).

CSV import

Imports into the currently-loaded table.

Two-column CSV: old_value,new_value per line. Header rows starting with old / source / from / # are skipped.

Quality Gates

Loading…

Gates

Loading…

Editor

Schema: id, name, description, enabled, priority (lower = earlier), on_fail (Pass / Warn / Hold / Reject), check_type (required / not_empty / min_length / matches regex / value_in / not_blank_when), tag (DICOM keyword), value (predicate parameter), valid_values (array, only for value_in).

Test gates against sample tags

JSON dict of sample DICOM tags. Gates evaluate against this synthetic dataset; no persisted state touched.

Rejection Queue

Loading…

Rejected studies

Datasets that failed an in-flight transform (Validation / MissingData / Reconciliation / QualityGate) are persisted here. Retry re-feeds the saved bytes through the full pipeline so fixed rules immediately apply.

Loading…

Report Migration

Inbound HL7 ORU reports are matched to archived studies by accession number, then forwarded to a destination VNA sibling per its configured policy. Records destined for an unconfigured destination are blocked until the operator picks a forwarding policy.

loading…

Status counts

loading…

Records

Forwarding destinations

HL7 Configuration

Host-level HL7 plumbing for the Migration sibling. Listener and inbox-DB changes take effect on next service restart; file-drop and HTTP-ingest toggles also require restart because the background services bind at startup. Per-sender ORM/ADT mapping (which MSH-3 / MSH-4 routes to which DICOM source-node) lives in External Batch Sources → HL7 MLLP.

MLLP Listener

TCP MLLP receiver per HL7 v2.x. Default OFF; port 2575 is IANA-registered hl7 for non-TLS MLLP. Plain TCP only in this release.

Inbox Catalog

Connection string configured

HL7 inbox catalog database. Defaults to the archive catalog engine (same connection string) unless explicitly overridden. Engine + connection string changes require service restart.

Leave blank to keep current value.

File Drop Watcher

Watches {dataDir}/<relative-path> for *.hl7 / *.txt files. Picks up, parses, routes through the HL7 sink, and moves to processed/ or failed/. No subfolder recursion. Toggle requires service restart.

HTTP Ingest & Retention

POST /api/hl7/ingest accepts multipart upload or raw text paste. Enable toggle and max upload size apply immediately. Retention days are read live by the manual purge endpoint.

HL7 Field Mapping

Per-sender override of where in the wire payload we extract accession, MRN, patient name, birth date, sex, and DICOM Study UID. Defaults match the legacy hard-coded behavior (OBR-3.1 | OBR-2.1 accession, PID-3.1 MRN, OBR-29.1 | ZDS-1.1 study UID). Add a row for any sender that puts fields in non-default positions.

Path syntax reference
SEG-Nwhole field N of segment SEG (first repeat) SEG-N.Cfield N, component C (1-based) SEG-N.C.Sfield N, component C, subcomponent S SEG-N#Rfield N, repeat R (1-based) SEG-N#R.Cfield N, repeat R, component C P1|P2|...fallback chain; first non-empty wins

Z-segments (vendor-specific custom segments like ZDS, ZRD, ZPM) work the same as named segments. Examples: OBR-4.1 (vendor puts accession in OBR-4), PID-3#2.1 (second MRN repeat), ZRD-1.1 (vendor Z-segment study UID).

Per-sender mapping rows

Paste & Preview Tester

Paste a raw HL7 v2 message; the tool parses + picks the matching per-sender mapping (or defaults) + extracts every field. Useful for verifying a new override before saving + for debugging "why is accession empty when the message clearly has one?"

Transit Audit

Per-instance received-vs-sent tag diff across the ingest pipeline (Normalization / Validation / MissingData / Reconciliation / TagMapping / QualityGate). DCR-2026-309/310.

Loading…

Each row = one SOPInstanceUID. Click a row to expand its per-tag diff. Color key: unchanged changed filled (from missing-data dict) cleared added removed

Migration Test Tool

Loading…

Generates the configured number of synthetic studies for the chosen modality and feeds every instance through the local IInstanceReceiver — exercising every transform (Validation / MissingData / Reconciliation / TagMapping / Crosswalk auto- capture / QualityGate) + the archive write. Synthetic sourceAe = MIGRATION_TEST so audit can distinguish test from real-PACS source. Capped at 1000 studies per run.

Last run result

No runs yet.

DB Migration

Stream catalog rows from a source database to a target. Both endpoints must be reachable + the schema must already exist on the target (run the host's schema-init separately).

v1 limits: Postgres ↔ Postgres fully supported. SqlServer + Oracle target stores will throw at UpsertInstance until those impls ship in separate DCRs; the job surface reports the throw message as last_error. PatientBirthDate + PatientSex are not preserved in v1 (denormalized study row carries only PatientId + Name).

New job

Jobs

No jobs yet.