XyDromatics VNA Migration
Archive Totals
DICOM C-STORE SCP
Host
Recent Studies
| Patient ID | Accession | Study Date | Modality | Study UID | Series | Instances |
|---|---|---|---|---|---|---|
| Loading… | ||||||
Study Search
Results
| Patient ID | Accession | Study Date | Modality | Description | Study UID | Series | Instances |
|---|---|---|---|---|---|---|---|
| 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.
| Name | AE Title | Host | Port | Direction | TLS | Enabled | Actions |
|---|---|---|---|---|---|---|---|
| Loading… | |||||||
Add Node
Advanced — presentation-context filters (optional)
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.
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
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.
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).
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)
(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.
(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.
Advanced TLS (mTLS / custom CA / insecure skip)
(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.
Federated Search
Run a single C-FIND-STUDY query across many source PACSes in parallel. Results are merged by Study Instance UID and tagged with the sources that returned each study, so you can see at a glance which legacy systems carry a given study.
Use this for cross-network reconciliation before opening the Migration Wizard — select source PACSes, set a query, then "Send selected to Wizard" to pre-fill batch rows.
Sources to search
Query
Per-source status
Merged results
Migration Dashboard
Control
Recent jobs
Loading…
Migration Analytics
Totals
Studies per day
Status histogram
Modality distribution
Per-source throughput
Expanded Analytics (DCR-2026-302)
—
—
—
—
—
—
—
—
All Expanded Analytics charts below respect the window + the Filter bar above (Study date / Source / Modality).
Pre-flight skipped studies that were already in the destination, saving bandwidth + retrieval time.
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
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
Per-batch summaries
Reconciliation results
DICOM Validation
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
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
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
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
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
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
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
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.
Status counts
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).
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
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.