Руководство по Carino DICOM

Carino DICOM — это шлюз DICOM и устройство непрерывности работы: принимает исследования, маршрутизирует и пересылает их, отдаёт хранимое по Query/Retrieve и DICOMweb и позволяет отделению лучевой диагностики работать, когда PACS или РИС перестают отвечать. Это руководство охватывает развёртывание, повседневные задачи и то, что делать, когда они не получаются, модель безопасности, каждую службу и реальные пределы программы.

Все снимки экрана в этом руководстве сделаны с работающего экземпляра этой программы, а исследования на них придуманы для съёмки. Ни один пациент в руководстве не показан.

Содержание

  1. Что это такое и какую роль играет
  2. Начало работы
  3. Как сделать…
  4. Когда что-то идёт не так
  5. Модель безопасности и правило токена
  6. Службы, одна за другой (справочник)
  7. Это не медицинское изделие
  8. Лицензия и где получить помощь

1. Что это такое и какую роль играет

Carino DICOM стоит между оборудованием, которое создаёт изображения, и системами, которые их хранят или читают. Он не пытается заменить ваш архив; он пытается быть тем звеном, которое их связывает, и тем, что останется на ногах, когда упадёт что-то из остальных.

У него две роли, и различие между ними важно: именно оно определяет, какие службы вы включите.

Всем управляет один config.json, и всё работает без графического интерфейса. Веб-панель удобна, но не обязательна.

Порты по умолчанию

СлужбаПортПротокол
Приём (Storage SCP)11112DICOM / DIMSE
Виртуальная печать11113DICOM Print
Modality Worklist11114DICOM C-FIND
Запрос/выдача11115DICOM C-FIND/C-MOVE/C-GET
Аварийная РИС2575HL7 поверх MLLP
Панель + DICOMweb8042HTTP

Почему 11112, а не 104. Зарегистрированный порт для DICOM — 104, а он привилегированный в Linux и macOS и требует root. Значение по умолчанию этого избегает. Настройте оборудование соответственно или пробросьте 104 на 11112 в контейнере или на брандмауэре.

2. Начало работы

Выберите вариант развёртывания

ВариантДля чего подходитПоднимается после отключения питания
Настольное приложение (в трее)Рабочая станция, за которой кто-то сидит: один кабинет, одна клиника, пробный запуск.Только с «запуском при входе»
Docker / PodmanСервер или любая машина, где его нужно изолировать и переносить целиком.Да (restart: unless-stopped)
Служба systemdПостоянный ПК в углу отделения лучевой диагностики, где никто не входит в систему.Да

Настольное приложение

Скачайте пакет для вашей системы со страницы releases и откройте его. Оно сворачивается в системный трей; по клику открывается панель. Приложение пока не подписано цифровой подписью, поэтому Windows и macOS предупреждают при первом запуске — на главной странице есть точные шаги, как всё-таки его открыть.

При первом запуске оно один раз спрашивает, можно ли проверять на GitHub, не вышла ли более новая версия. Ответ «нет» — или просто закрытое окно вопроса — оставляет проверку выключенной, и так она настроена по умолчанию. Если она включена, новый релиз появляется строкой в меню трея и рядом с версией на панели Обзор; клик по любой из них открывает страницу релиза в вашем браузере. Ничего не скачивается и ничего не устанавливается само. Флажок Проверять обновления в меню трея включает и выключает это в любой момент, и есть он только в настольной сборке — в контейнерных и systemd-развёртываниях его нет.

Docker

git clone https://github.com/MiguelCarino/Carino-DICOM
cd Carino-DICOM
mkdir -p data
echo "PACS_UID=$(id -u)" >> .env
echo "PACS_GID=$(id -g)" >> .env
docker compose up -d --build
docker compose logs -f pacs

При первом запуске создаётся и печатается в журнал токен доступа. Откройте http://127.0.0.1:8042/ и вставьте его, когда панель попросит. Всё — конфигурация, исследования, журналы, индекс — лежит в ./data, и именно с него делается резервная копия.

Самый частый сбой первого запуска — каталог ./data, принадлежащий root: контейнер никогда не работает от root и падает с EACCES. Две строки echo выше предотвращают это: образ собирается под вашу учётную запись; если вы их пропустили, добавьте их и снова выполните docker compose up -d --build. На хостах с SELinux (Fedora, RHEL, Rocky) суффикс тома :z — не украшение: без него в примонтированный каталог нельзя писать, хотя владелец и права выглядят совершенно правильно.

Служба systemd (Linux)

git clone https://github.com/MiguelCarino/Carino-DICOM.git
cd Carino-DICOM
sudo packaging/systemd/install.sh

Установщик создаёт системного пользователя carino-pacs, копирует код в /opt/carino-pacs, готовит /var/lib/carino-pacs и ставит юнит… но не запускает его. Это не недосмотр: запуск PACS открывает порты, принимающие данные пациентов, и такое решение принимается после чтения конфигурации, а не до. Отредактируйте /var/lib/carino-pacs/config.json, затем:

sudo systemctl enable --now carino-pacs
systemctl status carino-pacs
journalctl -u carino-pacs -f

Повторный запуск установщика обновляет код на месте и никогда не трогает существующий config.json.

Где лежат данные

ВариантКаталог
Настольное приложение / CLI~/CarinoDICOM/ (существующие установки сохраняют ~/CarinoPACS)
Docker/data в контейнере → ./data на хосте
systemd/var/lib/carino-pacs/ (режим 0750)

config.json лежит вместе с данными, а не в /etc, потому что относительные пути в конфигурации ("./received", "./logs", "./index.db") отсчитываются от каталога, в котором лежит сам файл конфигурации. Конфигурация в /etc разбросала бы исследования пациентов по /etc.

Первый запуск, по порядку

  1. Выберите службы. Всё поставляется выключенным. Окно выбора служб в панели спрашивает, что должна запускать эта машина; включите только это.
  2. Добавьте назначения (имя, хост, порт, AE title) на вкладке Назначения в разделе Конфигурация — именно этой вкладкой раздел и открывается.
  3. Проверьте связь, прежде чем передавать хотя бы одно изображение: нажмите Echo в строке назначения. На машине без панели то же самое делает ./run.sh echo --name "Hospital PACS". Если echo не проходит, дело не в Carino — дело в сети, брандмауэре или AE title.
  4. Направьте сюда один аппарат по строке Направьте модальность на на карточке приёма и отправьте тестовое исследование (см. Как сделать…).
  5. Следите за журналом — вкладка Журнал в разделе Активность. Если что-то не сработало, это появится там и на карточке службы как Последняя проблема; эта программа скорее закричит, чем тихо откажет.
Окно выбора служб в панели, озаглавленное «Выберите службы, которые должен запускать этот ПК»: по карточке на службу — Приём, Автоотправка, Приём печати, Аварийная РИС, MWL, Запрос/выдача — у каждой отметка «Использовать» и строка о том, что её порт свободен. Отмечен только Приём, поэтому внизу написано «Выбрана 1 служба» рядом с кнопками «Не сейчас» и «Включить выбранные».
Шаг 1. Заранее отмечен только Приём — именно он делает эту машину адресатом, — поэтому внизу написано Выбрана 1 служба; ничего ещё не запущено, и ни один порт не откроется, пока вы не примените выбор. Каждая карточка называет, для чего служба нужна, так что решение принимается по описанию, а не по названию протокола.

Без графического интерфейса

./run.sh init            # создаёт config.json и его папки
./run.sh init --token    # и генерирует web.auth_token
./run.sh serve           # панель на http://127.0.0.1:8042
./run.sh receive         # только приём
./run.sh send            # только наблюдение за папкой / пересылка
./run.sh qr              # только Запрос/выдача
./run.sh mwl             # только рабочий список
./run.sh ris             # только приём заявок HL7
./run.sh print           # только виртуальная печать
./run.sh echo --host 10.0.0.5 --port 104 --aet REMOTEPACS

Каждая команда принимает -c / --config <путь>. В Windows — run.ps1.

3. Как сделать…

Короткие рецепты для задач, которые возникают в отделении. Каждый заканчивается тем, как понять, что всё получилось; если не получилось, следующая глава — список того, что проверить.

…направить аппарат сюда, чтобы он сохранял изображения

  1. Откройте Службы. Если карточка Приём говорит, что служба остановлена, нажмите Пуск.
  2. Прочитайте строку карточки Направьте модальность на: AE title, IP-адрес и порт (по умолчанию CARINODICOM и 11112). Кнопка копирования рядом копирует все три. Если у машины несколько адресов, берите тот, что в сети аппарата.
  3. На аппарате добавьте назначение для сохранения — производители называют его узлом DICOM, архивом, «Send to» или «Store» — с этими тремя значениями. Собственный AE title аппарата — это вызывающий AE; здесь его вводить не нужно.
  4. Если в Конфигурация → Настройки → Этот PACS в поле Принимать только эти AE перечислены AE title, добавьте туда AE title аппарата, иначе приём его отклонит.
  5. Запустите собственную проверку связи на аппарате (часто Verify или Echo), затем отправьте одно исследование. Оно появится в Исследования → История.

Кнопка Проверить на карточке проверяет приём с этой же машины. Если Проверить проходит, а аппарат всё равно не может отправить, проблема между ними: адрес, порт, брандмауэр или список разрешённых AE.

…направить аппарат сюда, чтобы он печатал

Для оборудования, единственный вывод которого — плёнка. Carino притворяется плёночным принтером и захватывает каждый лист, чтобы вы его идентифицировали; на бумагу ничего не печатается.

  1. Откройте Службы и нажмите Пуск на карточке Приём печати.
  2. Прочитайте её строку Направьте модальность на (по умолчанию CARINOPRINT, IP этой машины и порт 11113).
  3. На аппарате добавьте принтер DICOM — его иногда называют лазерной камерой или плёночным принтером — с этими тремя значениями. Оттенки серого предлагаются всегда; если аппарат печатает только в цвете, отметьте Принимать также цветные задания печати в Конфигурация → Настройки → Печать.
  4. Нажмите Отправить тестовую печать на карточке. Через несколько секунд в Исследования → Ожидают появится лист с пометкой TEST PRINT — safe to discard. Это доказывает, что приём работает; отбросьте лист.
  5. Напечатайте одну плёнку с аппарата. Карточка покажет Последняя печать с числом плёнок, AE title аппарата и его адресом, счётчик Захвачено вырастет, а плёнка будет ждать в Ожидают, пока вы её не утвердите (см. ниже).

Сохранять каждую снятую плёнку как (в группе Печать) определяет, чем станет плёнка после утверждения. Многие архивы и просмотрщики хранят PDF, но не показывают его; выберите Изображение (Secondary Capture), если плёнку нужно читать на той стороне.

AE title принтера, набранный на аппарате, не проверяется: с опечаткой печать всё равно пройдёт. Фильтруется только собственный AE title аппарата, и только если заполнено Принимать только эти AE. Если печать не удалась, карточка показывает Последняя проблема; в таблице неполадок печати есть каждое сообщение и способ исправления.

…направить аппарат сюда, чтобы он забирал рабочий список

  1. Убедитесь, что заявки есть. Либо РИС шлёт заявки HL7 в службу Аварийная РИС (порт 2575), либо кто-то вводит их в Заявки → Новая заявка.
  2. В Конфигурация → Модальности зарегистрируйте кабинет с точным AE title станции. Тогда адресат заявки выбирается из этого списка, а не набирается.
  3. Откройте Службы, нажмите Пуск на карточке MWL и прочитайте её строку Направьте модальность на (по умолчанию CARINOMWL и порт 11114).
  4. На аппарате укажите эти значения как поставщика рабочего списка (MWL, «HIS/RIS» или «worklist server») и выполните запрос.

Заявка без адресата видна на всех станциях; заявка с адресатом видна только на станции, чей AE title совпадает с ним в точности. Когда исследование возвращается, оно сопоставляется с заявкой по номеру исследования, и заявка закрывается сама.

…утвердить ожидающую плёнку

Снятые плёнки, а также PDF и изображения, найденные рядом с исследованием, ждут в Исследования → Ожидают. Счётчик 📎 рядом с Исследованиями показывает, сколько их.

  1. Откройте Просмотр и посмотрите на лист.
  2. Если у пациента есть открытая заявка, нажмите Сопоставить с заявкой… и выберите её. ФИО, ID пациента, номер исследования и само исследование берутся из заявки, а заявка закрывается как захваченная.
  3. Иначе введите данные сами. ID пациента и ФИО пациента обязательны; номер исследования, дату и описание очень желательно заполнить.
  4. Если элемент пришёл рядом с исследованием, а вы сменили пациента, выберите, к чему он относится: Прикрепить к исходному исследованию — только если это исследование действительно принадлежит этому пациенту, иначе Новое исследование. Неверный выбор положит лист одного пациента в исследование другого.
  5. Нажмите Утвердить. Элемент превращается в DICOM и пересылается, как любое другое исследование.

Отменить нельзя: после утверждения исходный файл исчезает из «Ожидают», а копия DICOM уже в пути. Ошибку исправляют в архиве. Отбросить убирает элемент, ничего не отправляя.

…исправить застрявшую пересылку

Откройте Исследования → Застряли (счётчик ⚠ рядом с Исследованиями). Строки сгруппированы по исследованию — с пациентом, номером исследования и описанием — и разложены на три вида:

…узнать, почему что-то не сработало

  1. Посмотрите на карточку службы. Каждая служба показывает Последняя проблема, если что-то пошло не так; щёлкните по ней, чтобы открыть журнал, уже отфильтрованный по этой службе.
  2. На Активность → Журнал пользуйтесь кнопками служб (приём, отправка, печать, рабочий список, Q/R, РИС…), переключателем Только ошибки и предупреждения и текстовым фильтром — AE title, IP-адрес или номер исследования быстро сужают поиск.
  3. Чтобы передать проблему кому-то ещё, скопируйте видимые строки или скачайте журнал за целый день.
  4. Нажмите Проверить на карточке. Если проверка с этой машины проходит, а аппарат всё равно не может, смотрите на сеть между ними.
  5. Если изменилась настройка и никто не знает, кто её изменил, Активность → Аудит называет человека и разделы, которые он менял.

4. Когда что-то идёт не так

Сначала признак, затем где смотреть, затем как исправить. Почти каждая строка начинается в одних и тех же двух местах: карточка службы на панели Службы (её Последняя проблема) и Активность → Журнал.

Отправка, приём и рабочий список

ПризнакГде смотретьКак исправить
Аппарат вообще не может отправить намКарточка «Приём»; строки журнала, начинающиеся с Receiver: refusedits AE title is not in the allowed list: добавьте AE аппарата в Принимать только эти AE или очистите список. В журнале ничего: соединение так и не дошло — проверьте IP и порт на аппарате, брандмауэр и то, что служба на карточке запущена. Проверить на карточке отделяет «эту машину» от «сети».
Аппарат отправил, но исследования нет в ИсторииЖурнал, кнопка приём; Свободно на ОбзореRefused C-STORE — low disk space: освободите место на диске или уменьшите scp.min_free_gb. Когда место появится, аппарат может отправить заново.
Карточка янтарная: «Включена, но не работает»Журнал в момент запускаЕё порт занят другой программой или другой службой здесь. Смените порт или остановите другую программу, затем Пуск.
Исследования не доходят до основного PACSИсследования → ЗастрялиОшибка в строке говорит, что сделал PACS: rejected permanently: calling AE title not recognised → добавьте наш отправляющий AE (по умолчанию CARINOSCU) на PACS; connection refused → неверный порт или PACS остановлен; no reply → хост выключен или мешает брандмауэр; aborted → часто несовпадение TLS.
Одному назначению ничего не отправляется, и повторов нетЗастряли, раздел «Удержано»Удержание из-за обезличивания — см. Обезличивание при пересылке.
Аппарат не видит свой рабочий списокКарточка «MWL»; журнал, кнопка рабочий списокMWL: refused: добавьте его AE в список разрешённых для рабочего списка. Запроса в журнале нет: неверные IP/порт/AE на аппарате. Запрос приходит, а список пуст: AE адресата заявки не совпадает со станцией в точности — исправьте его в Модальностях.
Собственная РИС больницы не отдаёт аппарату его списокКонфигурация → Модальности → Проверить рабочий списокСм. Проверка рабочего списка; ответы попадают в Активность → Проверки рабочего списка.
Заявки HL7 от РИС не появляютсяКарточка «Аварийная РИС»; журнал, кнопка РИСПроверьте, что РИС отправляет на этот IP и порт 2575 и что её адрес есть в ris.allowed_hosts, если этот список задан.
Всех выкинуло из системы—Нормально после перезапуска: сессии намеренно хранятся только в памяти. Войдите снова.
Панель перестала отвечать или её числа не меняютсяПерезагрузите страницуЕсли она не возвращается, движок не отвечает: проверьте, что служба или контейнер работает (systemctl status carino-pacs, docker compose ps), и прочитайте его журнал.
Контейнер падает с EACCESdocker compose logs pacsВ ./data не может писать пользователь контейнера: задайте PACS_UID/PACS_GID в .env и пересоберите (см. Docker выше).
Почта при переключении включена, но никто её не получилКонфигурация → ЛюдиПисьма уходят профилям с адресом почты, а не по списку. См. Оповещения.

Печать

Каждый отказ и сбой приёма печати — это строка в журнале (кнопка печать), она учитывается в Ошибках карточки и показывается как её Последняя проблема. Удачное задание показывается как Последняя печать.

Строка журнала или признакЧто это значитКак исправить
Print: refused X @ ip — its AE title is not in the allowed list (…)AE title аппарата нет в списке приёма печати.Добавьте его в Печать → Принимать только эти AE или очистите список.
Print: refused X @ ip — too many connections at onceАппарат — или несколько — открыли больше соединений, чем приём принимает одновременно.Дайте заданиям завершиться и напечатайте снова; если повторяется, печатайте по одному заданию.
Print: X @ ip asked for Basic Color Print Management…, which this printer does not offerАппарат хочет цвет, а цвет выключен.Отметьте Принимать также цветные задания печати. Ошибкой не считается: аппарат, предлагающий оба варианта, печатает в оттенках серого.
Print from X: nothing to print — no film was created first, … N film(s) were created but no image was sent into them, … the film it named (…) was never created hereАппарат пропустил шаг последовательности печати. Ему ответили «пустая страница» (предупреждение, 0xB603).Настройка или неисправность на стороне аппарата: проверьте конфигурацию его принтера (формат плёнки, число изображений) и напечатайте снова.
Print: X sent an image for a box this printer never handed out (…); refusedАппарат сослался на ячейку изображения из другого сеанса.Начните печать на аппарате заново; если повторяется, сообщите об этом вместе со строкой журнала.
Print: could not read image N from X (…); it is left blank on the filmОдно изображение пришло в формате пикселей, который приём не читает; остальной лист сохранён.Измените настройки печати на аппарате (разрядность, цвет), если это возможно; иначе сообщите эту строку.
Failed to render or queue the print job from X: …Плёнка пришла, но её не удалось записать (статус 0x0110).Проверьте свободное место на диске и то, что в папку ожидающих (scu.pending_dir) можно писать.
Другие строки, начинающиеся с Print: …Переданные как есть сообщения самой библиотеки DICOM по этому соединению печати.Читайте вместе с предыдущей строкой; если непонятно — сообщите.
В журнале вообще ничегоСоединение так и не дошло.Служба на карточке запущена (не янтарная)? Аппарат использует IP этой машины и порт 11113? Не мешает ли брандмауэр? Отправить тестовую печать на карточке доказывает, что эта сторона в порядке.

Две вещи, которые причиной не являются: AE title принтера, набранный на аппарате (он не проверяется), и то, что аппарат оставляет номера сеанса плёнки и плёночного листа принтеру (если их нет, они создаются здесь). Presentation LUT принимается и игнорируется, а аппарат, который сначала запрашивает конфигурацию принтера, получает ответ.

Вкладка Журнал в разделе Активность: над потоком — кнопка на каждую службу, флажок «только ошибки и предупреждения» и текстовый фильтр; ниже — строки за день с отметками времени, самые новые внизу, у каждой помечена служба, которая её записала; под потоком — число строк, «Копировать видимое», выбор дня, «Скачать журнал за день» и «Очистить вид».
Отсюда начинается каждая строка таблиц выше. Журнал открывается на самых новых строках, внизу; ошибки выделены красным, предупреждения — янтарным, и каждая значимая строка говорит, что с ней делать. Когда поток шумный, сужайте его кнопками служб (Приём, Автоотправка, Печать…), флажком Только ошибки и предупреждения и текстовым фильтром. Копировать видимое копирует то, что оставили фильтры, Скачать журнал за день сохраняет весь файл за день, выбранный рядом, а Очистить вид очищает экран, но не файл.

5. Модель безопасности и правило токена

Экран входа: карточка с заголовком «Этому PACS нужен токен доступа», одно поле для ввода, кнопка «Войти» и примечание о том, что токен — это web.auth_token в файле конфигурации и что панель никогда не показывает сохранённый токен.
Это выдаётся вместо панели, а затем снова при каждом ответе 401 — перезапуск намеренно обрывает все сессии, поэтому повторный вход нормален, а не является неисправностью.

Начнём с главного: панель — это ключ от архива. Любой, кто её откроет, может прочитать каждое сохранённое исследование, скачать файлы DICOM, изменить любую настройку, запустить и остановить службы, удалить исследования и выключить сервер. Из коробки существует один общий секрет, и тот, у кого он есть, может всё — профили необязательны и выключены, пока вы их не включите. Как только вы это сделаете, каждый входит под собой — со своими разрешениями, со своим доступом к идентификаторам пациента и со своим именем в журнале аудита; общий токен продолжает работать как администратор, поэтому ничего из того, что уже им пользуется, не ломается.

Вкладка Люди раздела Конфигурация, под строкой «Профилей: 4.». Карточка Администратора короткая — у неё отмечен пункт «Администратор (всё, включая будущие разрешения)», поэтому таблицы разрешений на ней нет вовсе, только поле пароля и кнопки «Сохранить» и «Удалить». Карточка IT под ней показывает сетку отметок «Может делать», а снимок обрывается посреди этой сетки, ещё до строки «Может видеть». Внизу справа закреплена кнопка «Добавить».
Профили, когда они включены. Разрешения задаются на человека и проверяются на каждой конечной точке, а не прячутся в браузере — и Может видеть отделено от Может делать: профилю можно разрешить перенаправить исследование по номеру исследования, ни разу не прочитав имени пациента. Всё неотмеченное показывается как *** везде, где оно появилось бы.

Разрешения определяют и то, что панель рисует. Профиль видит только те разделы боковой панели и только те вкладки внутри них, на которые у него есть разрешения: Рентгенолог, у которого есть routing.read, но нет config.read, получает Конфигурацию с Назначениями и Маршрутами и без вкладки Настройки, а Регистратура, не имеющая ни одного из трёх, вообще не видит раздела Конфигурация. Индикаторы служб в верхней шапке — намеренное исключение, см. Веб-панель ниже.

Тот же экран входа на устройстве с профилями: четыре кнопки — Администратор, ИТ, Рентгенолог, Регистратура — каждая с замком, и ссылка «Войти по токену доступа».
Экран входа устройства с профилями. Список рисуется, только пока включено users.list_profiles: публиковать состав сотрудников для всех, кто дотянется до порта, — это реальное раскрытие, и решать вам. Токен по-прежнему работает, спрятанный ниже, потому что это путь обратно, когда кто-то закрыл себе вход.

Правило

Пустой web.auth_token допустим, только пока web.host — это петлевой адрес. Если web.host — что-либо другое: 0.0.0.0, адрес в локальной сети, имя хоста — а токен пуст, сервер отказывается запускаться. Этот отказ — свойство программы, а не ошибка, которую надо обойти.

Причина вполне конкретна. Без аутентификации на 127.0.0.1 контролем доступа служит операционная система: до API дотянется только процесс на этой машине. Это оправданно. Но web.host задаёт оператор, и в тот момент, когда кто-нибудь меняет его на 0.0.0.0, чтобы «зайти с другого ПК» — второпях, во вторник, не думая о безопасности, — тот же API выдаёт любому соседу по локальной сети список исследований, пути хранения, байты DICOM и /api/shutdown. Правило существует потому, что такое изменение занимает десять секунд, а последствия остаются на годы.

Любой контейнер всегда находится именно в этом положении: контейнер по своей природе публикует службу на 0.0.0.0. Поэтому образ при первом запуске создаёт 256-битный токен, если вы не задали свой, — и печатает его. А вот молча он токен не создаёт никогда: секрет, которого никто не видел, — это секрет, который никто не меняет.

Создание и предъявление токена

./run.sh init --token                                  # записывает его в config.json
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
openssl rand -base64 32

Сервер принимает учётные данные тремя способами:

Файл cookie существует для того, чтобы панель спрашивала токен один раз вместо того, чтобы держать его в JavaScript, где его прочитает любой XSS и любое расширение браузера. Он никогда не несёт сам токен: он несёт HMAC по секрету, созданному при старте и хранимому только в памяти. Поэтому перезапуск завершает все сессии — верный компромисс для устройства с одним оператором: никакого хранилища сессий на диске, нечему утечь, а в худшем случае токен придётся ввести заново раз в смену.

Панель говорит по обычному HTTP. Встроенного TLS для веб-уровня нет. Если вы открываете её за пределы петлевого адреса, поставьте перед ней обратный прокси, который завершает HTTPS. Токен, переданный по открытому HTTP в общей сети, — это токен, который вы отдали.

Сторона DICOM

Что эта модель не защищает

Резервные копии. Индекс sqlite — это кеш, и он восстанавливается сам; изображения — нет. Делайте резервные копии каталогов хранения — и хотя бы раз проверьте восстановление.

Никакой телеметрии — с одним исключением, которое включаете вы сами. Движок никуда ничего не отправляет: ни аналитики, ни отчётов о сбоях, ни счётчиков использования, ни сторонних скриптов, подгружаемых во время работы. Его единственные исходящие соединения — это ассоциации DICOM и подтверждения HL7, которые вы настроили, к названным вами узлам, и развёртывание в Docker, Podman или systemd — это ровно оно и ничего сверх того, пока администратор не включит описанную ниже проверку версии. Именно эта фраза здесь и важна: такая машина и стоит в отделении лучевой диагностики с данными пациентов на ней. Настольное приложение вдобавок умеет проверять, вышла ли более новая версия, — но только если вы ответили «да» на вопрос, который оно задаёт один раз, при первом запуске; по умолчанию проверка выключена, и в меню трея её можно выключить снова. Эта проверка — один HTTPS-запрос в сутки к списку релизов на GitHub, с заголовком User-Agent и ничем больше: без идентификаторов, без счётчиков, без конфигурации, без данных пациентов. Поэтому GitHub узнаёт IP-адрес этой машины и примерное время запуска приложения — и это всё. Ничего не скачивается и ничего не устанавливается само. Панель предлагает ту же проверку с самого сервера — для развёртываний без настольного приложения: она выключена, пока администратор не включит её кнопкой рядом с версией в Обзоре или в Настройки → Интеграции; отправляется ровно тот же запрос, с сервера, а не из вашего браузера; после этого Обзор показывает, актуальна ли эта версия, с кнопкой «Обновить», открывающей этот сайт. Изменение, которое отправляло бы больше этого или отправляло бы то, на что никто не соглашался, считалось бы уязвимостью.

Вкладка Аудит раздела Активность: строка с числом файлов, размером на диске и хешем вершины цепочки; ряд фильтров — «Все», «Все действия» и флажок «Только неудачи и отказы» — с кнопками «Проверить целостность» и «Экспорт» справа; и строки с отметками времени, новые сверху, все «Готово» и со свёрнутыми подробностями: вход движка по токену доступа, по строке на каждый вход, а под ними — «Проверил рабочий список» для US_ROOM_2 от токена доступа.
Журнал аудита, а над ним напечатана вершина цепочки — именно этот хеш стоит скопировать туда, куда эта машина не дотянется. У вкладки свой ряд фильтров — чьи действия (все или один человек), какое действие и Только неудачи и отказы, — а справа от него Проверить целостность и Экспорт; Очистить вид относится к вкладке Журнал, здесь его нет. Отказы записываются так же добросовестно, как и успехи: отклонённый запрос — такая же строка здесь, как и любая другая.

6. Службы, одна за другой

Всё, что открывает порт, поставляется выключенным. Правильный вопрос не «что оно умеет?», а «что должна делать эта машина?». Каждая включённая служба — это ещё один открытый порт. Индекс ниже — единственное исключение: он запускается сам, потому что это локальный кеш sqlite, который ничего не слушает.

У каждой службы один выключатель. Пуск на её карточке запускает её и оставляет включённой после перезапуска; Стоп сначала спрашивает, затем останавливает её и оставляет выключенной. В Конфигурация → Настройки тот же выключатель — это флажок Запускать эту службу (и при старте), поэтому карточка и настройка никогда не расходятся. Сохранение настроек перезапускает только те службы, чьи собственные настройки изменились, и панель говорит, какие именно.

Панель Службы: по карточке на службу — Приём, Автоотправка, Приём печати, Аварийная РИС, MWL, Запрос/выдача — у каждой точка состояния, кнопка «Стоп», строка «Направьте модальность на» с AE title, IP-адресом и портом и кнопкой копирования, кнопка «Проверить» и её счётчики; на карточке приёма печати также строка «Последняя печать» и кнопка «Отправить тестовую печать».
Каждая служба запускается и останавливается сама по себе, и на каждой карточке есть то, что нужно лаборанту: адрес, который надо ввести в аппарат, Проверить, которая проверяет службу с этой машины, и два числа, за которыми стоит следить, — сколько обработано и сколько не удалось. Когда что-то ломается, Последняя проблема говорит, что именно, и ведёт в журнал. Панель — не движок: остановка здесь останавливает эту службу, а не устройство.
Панель Обзор: плитки со счётчиками служб запущено, принято, отправлено, застряло, ожидает, открытых заявок и свободного места, а под ними карточки с адресом этой машины, её приёмником, путями конфигурации и хранения, доступностью каждого назначения и последним принятым исследованием.
Обзор отвечает на вопрос «работает ли эта машина» одним экраном. Каждая плитка, считающая то, у чего есть своё место, — это кнопка в ту панель, которой это принадлежит — Застряли и Ожидают открывают свои вкладки в разделе Исследований, — поэтому неожиданное число находится в одном клике от подробностей. Свободно — исключение: оно никуда не ведёт, поэтому и остаётся простым показателем, а не кнопкой, которая ничего не делает.

Приём — Storage SCP

Порт 11112 · C-STORE, C-ECHO

Что делает
Принимает исследования, которые присылают аппараты, и раскладывает их на диск — при желании по схеме пациент / исследование / серия. Он принимает все синтаксисы передачи и сохраняет сжатые объекты как есть — без перекодирования, поэтому по пути внутрь ничего не меняется.
Включайте, когда
Сюда что-либо должно отправлять изображения: аппараты, другой PACS, рабочая станция. Это основная служба.
Оставьте выключенным, когда
Эта машина только пересылает то, что кто-то другой кладёт в папку.
Вкладка История раздела Исследований, полоса Принятые: поле фильтра и кнопки модальностей над строками исследований — у каждой пациент, ID, дата, номер исследования, описание, модальность и число изображений, под ней перечислены серии, а справа кнопка «Отправить» и кнопка «⋯», открывающая остальные действия строки.
Что пришло, новое сверху, и из каких серий состоит каждое исследование; поле фильтра и кнопки модальностей сужают список. На строке видно одно действие — Отправить, остальные в меню ⋯: Добавить PDF/изобр., Теги (когда задан адрес редактора), Копировать путь, Открыть папку (только если панель открыта на самой этой машине) и Удалить. Путь настоящий: раскладка — это дерево папок, а не база данных, из которой кому-то надо выгружать. Переключатель над списком имеет три положения — в том порядке, в каком живёт исследование: Принятые; Исходящие — папка исходящих, с состоянием отправки на каждый узел для каждого исследования; и Архив, который заполняется, только если автоотправка настроена перемещать исследование после отправки. При значении по умолчанию — «оставлять» — отправленное исследование остаётся в Исходящих.

Автоотправка — Storage SCU и правила маршрутизации

Исходящий клиент · C-STORE

Что делает
Наблюдает за папкой и пересылает каждый новый файл на те назначения, которые подходят. Файл уходит, только когда он стабилен (размер не изменился между двумя сканированиями), поэтому недописанный файл никогда не пересылается. Прогресс отслеживается по каждому назначению: файл считается обработанным, только когда его приняло каждое включённое назначение, а отправка на недоступные узлы повторяется на следующем сканировании. При успехе оригинал можно оставить, переместить или удалить.
Правила
При включённой маршрутизации правило выбирает назначения по модальности, вызывающему AE title, станции, ID пациента или описанию исследования (шаблоны * и ?, без учёта регистра). Например: CT из ER_* — в учебный архив, обезличенно.
Гарантия
Исследование никогда не может остаться неотправленным. Маршрутизация выключена, ни одно правило не совпало, заголовок не читается, правило называет назначение, которого больше нет, — в каждом из этих случаев файл уходит на все включённые назначения. Лишняя отправка раздражает оператора; недостаточная теряет изображение.
Единственное исключение
Правило, которое требует обезличивания для назначения, когда очистку выполнить нельзя, удерживает это назначение: ему не отправляется ничего, вместо того чтобы отправить с данными пациента. Очистке мешают две вещи — профиль в состоянии off или профиль включён, но из настроек не удаётся построить деидентификатор, — и исправляются они по-разному. Ничего не теряется ни в том, ни в другом случае; см. Обезличивание при пересылке ниже.
Включайте, когда
Эта машина должна доставлять изображения дальше: в центральный PACS, на рабочую станцию врача или копиями в учебный архив.
Вкладка Назначения раздела Конфигурация: три строки — Main archive, Reading room и Teaching archive — у каждой хост, порт, AE title, отметки TLS, «Без РИС» и «Резервирование», а также кнопка Echo.
Назначения получают имя здесь, а везде далее упоминаются по имени, поэтому переименование узла не оставляет сиротами правила, которые на него ссылаются. Echo проверяет ассоциацию до того, как от неё будет зависеть хоть одно изображение.
Вкладка Маршруты раздела Конфигурация: отметка «Маршрутизировать по правилам», затем пронумерованные правила с полями сопоставления по модальности, вызывающему AE, станции, ID пациента и описанию исследования, и строка «Отправлять на» с отметками назначений плюс «Обезличить» и «Остановиться здесь».
Правила читаются сверху вниз, а пустые поля означают любое: правило — это фильтр, а не бланк, который надо заполнить. Порядок важен, поэтому у каждого правила есть стрелки для перемещения.
Вкладка Застряли раздела Исследований под баннером «8 файлов требуют внимания», разделённая надвое: «Повтор идёт автоматически» — с учебным архивом, двумя ожидающими объектами исследования CR, его последней ошибкой красным, кнопкой «Повторить сейчас» и обратным отсчётом до следующей попытки; и «Удержано — ничего не отправляется» — с учебным архивом, помеченным «профиль выключен», шестью ожидающими объектами исследования CT под его пациентом и номером исследования, правкой, которая их освободит, кнопками «Настройки обезличивания» и «Правило, которое этого требует», а также «Отправить на…» и «Убрать из очереди».
Две половины здесь — это разные проблемы, и вкладка их разделяет. Сверху — узел, который лежит и починится сам; снизу — удержание из-за обезличивания, которое не снимает никакой таймер, поэтому в каждой строке прямо названа правка, снимающая его, и стоят две кнопки, которые ведут туда, где её делают. Строки сгруппированы по исследованию, так что видно, чьи снимки ждут, а не только имена файлов. Третий раздел, Не осталось назначения для повтора, рисуется, только когда ему есть что показать: это файлы, направленные на имя, которое больше не является включённым назначением. Их никто не повторяет, потому что обращаться уже не к кому — верните назначение, отправьте их на другое кнопкой Отправить на… или смиритесь с потерей кнопкой Убрать из очереди; до тех пор они лежат в папке исходящих, а не архивируются и не удаляются.

Обезличивание при пересылке

Профиль PS3.15 Annex E · применяется только к уходящей копии

Что делает
Применяет Basic Application Level Confidentiality Profile к отправляемому объекту и записывает в (0012,0064), какие именно опции сохранения были использованы, чтобы получатель видел, что осталось. Сохранённый оригинал никогда не переписывается — в этой асимметрии вся суть.
Профили
basic сохраняет даты (полностью или со сдвигом), характеристики пациента, сведения об устройстве и об учреждении. strict убирает сведения об устройстве и учреждении и удаляет частные атрибуты, даже если вы просили их сохранить.
Включайте, когда
Изображения покидают клиническую среду: обучение, научная работа, поставщик, внешнее второе мнение.
Выключение
Профиль решает, происходит ли очистка вообще; отметка Обезличить у правила решает, какие назначения получат очищенную копию. Поставьте профиль в off, пока правило всё ещё этого требует, — и Carino удержит это назначение, а не отправит копию с данными пациента на узел, владельцу которого сказали, что таких данных он не получает.

Удержано, а не отправлено, — и это ответ, когда исследования перестают двигаться. Назначение, для которого правило требует обезличивания, не получает ничего всякий раз, когда очистку выполнить нельзя. Доставка откладывается, личность не раскрывается: исследование, ждущее на диске, освобождается одной правкой, а имя, дошедшее до внешнего узла, никакой правкой не вернуть.

Очистка не может произойти по двум причинам, и исправляются они по-разному. Каждое удержание записывает, какая именно это причина, и вкладка Застряли раздела Исследований показывает способ исправления именно для записанной причины, а не гадает:

  • Профиль в состоянии off, а правило всё ещё требует очистки. Поставьте профиль в basic или strict — и следующий проход автоотправки их освободит.
  • Профиль включён, но из текущих настроек не удалось построить деидентификатор, поэтому очищать по-прежнему нечем. Ошибка, которая этому помешала, есть на вкладке Журнал раздела Активность, на канале отправки, — исправьте её. Выключение профиля это не освободит: оно не освобождает ничего и лишь меняет то, какая из двух половин мешает очистке.

Снятая у правила отметка Обезличить освобождает любое из этих удержаний — и освобождает исследования с данными пациента, то есть даёт ровно тот исход, ради предотвращения которого удержание и существует. Делайте эту правку, только если это назначение действительно больше не должно получать очищенные данные.

Какая бы из этих правок ни была верной, до неё один клик из строки, которая сообщает об удержании. В каждой удержанной строке есть кнопка Настройки обезличивания, открывающая вкладку Настройки раздела Конфигурация, и кнопка Правило, которое этого требует, открывающая вкладку Маршруты. Обе только открывают вкладку и не более: ни одна не прокручивает до правила, требующего очистки, и ни одна его не подсвечивает. И всё же строка, называющая проблему, ведёт в оба места, где она чинится, поэтому починка короче, чем была, когда каждое из них надо было искать в боковой панели.

Ничего не теряется, и молчания об этом нет. Исследования остаются в папке исходящих — не архивируются и не удаляются, — каждое другое назначение того же исследования всё равно их получает, вкладка Журнал раздела Активность поднимает ошибку с именем исследования, удержанного назначения и причины, набор полей обезличивания на вкладке Настройки тоже её называет, а значок ⚠ рядом с разделом Исследований считает эти файлы — и сам этот значок является кнопкой, открывающей вкладку Застряли. Само по себе удержание не снимается ничем: таймер его не отсчитывает, и никто его не повторяет.

Пиксели не очищаются. Данные пациента, впечатанные в изображение, — надпись, которую УЗИ или вторичный захват печатает внутри кадра, — переживают любой профиль, и именно так «анонимизированные» данные чаще всего уходят из больницы с именем. Опция Clean Pixel Data (113101) намеренно не заявлена, потому что она не выполняется. Внутри программы ничто не может обнаружить эту утечку за вас: изображения должен посмотреть человек. Текст внутри структурированных отчётов также не читается.

Индекс

sqlite · index.db

Что делает
Держит по одной записи на сохранённый файл и выводит из них ответы о пациенте, исследовании и серии, поэтому сводка исследования никогда не разойдётся с объектами, которые она сводит. Это слой запросов, стоящий за Запросом/выдачей и DICOMweb.
Включайте, когда
Вы пользуетесь Запросом/выдачей или DICOMweb. Это их единственная зависимость.
Что гарантирует
Индекс — это кеш, а не источник истины. Его потеря стоит пересканирования, но никогда — изображения.

Запрос/выдача — C-FIND, C-MOVE, C-GET

Порт 11115 · Patient Root и Study Root

Что делает
Позволяет другой системе спросить «какие исследования у вас есть по этому пациенту?», а затем «пришлите их мне / вон на ту рабочую станцию». Это та половина PACS, с которой старое оборудование действительно умеет разговаривать: УЗИ 2009 года или считыватель CR никогда не заговорит на DICOMweb, но на DIMSE — говорит.
Включайте, когда
Рабочие станции или аппараты должны забирать исследования отсюда, или вы выступаете временным архивом на время аварии основного PACS.
Что гарантирует
C-MOVE никогда не выдумывает список объектов — он разрешает его через индекс. Всё, о чём индекс знает, но что не читается с диска, засчитывается как неудавшаяся подоперация и называется в Failed SOP Instance UID List. Это никогда не замалчивается: C-MOVE, который отчитывается об успехе, отправив меньше изображений, чем нашёл, — худший отказ, какой эта программа может допустить.

DICOMweb — QIDO-RS, WADO-RS, STOW-RS

HTTP, по пути /dicom-web на порту панели

Что делает
Позволяет современным просмотрщикам (OHIF, Weasis и подобным) запрашивать, получать и сохранять по HTTP, не договариваясь об ассоциации DICOM. Загрузка по STOW-RS проходит через ту же раскладку, что и C-STORE, поэтому исследование, отправленное просмотрщиком, неотличимо от присланного аппаратом.
Включайте, когда
Вам нужен веб-просмотрщик поверх архива. Помните, что токен защищает и эти маршруты.
CORS
cors_origins сверяется точно (схема + хост + порт) и по умолчанию пуст, поэтому источнику не из списка ничего не отражается. Синтаксиса шаблонов нет — но буквальная * принимается, и только потому, что вы её набрали: с этого момента отражается любой источник, и любая страница, которую откроет оператор, сможет прочитать архив с этой машины. Лучше назовите просмотрщик.
Чего не делает
/rendered, /thumbnail, bulkdata URI и перекодирование между синтаксисами передачи не реализованы и отвечают 406. Наполовину работающий просмотрщик хуже отсутствующей возможности: то, что не может быть получено, никогда не подделывается.

Modality Worklist (MWL)

Порт 11114 · C-FIND рабочего списка

Что делает
Выдаёт заявки аппаратам, чтобы лаборант не набирал данные пациента вручную. В каждой заявке есть заранее созданный Study Instance UID, который записывается в исследование, поэтому вернувшееся с аппарата исследование сопоставляется со своей заявкой точно. Поле AE целевого аппарата направляет заявку на одну станцию; пустое означает, что её видят все станции.
Включайте, когда
РИС не доводит заявки до аппаратов: потому что она недоступна, потому что на площадке РИС просто нет, или чтобы проверить поток РИС→PACS без живой РИС.

Зарегистрированные модальности

Без порта · Конфигурация → Модальности

Что делает
Список кабинетов, которые обслуживает этот узел: имя, понятное человеку, AE title станции, который рабочий список сверяет посимвольно, код модальности и, если нужно, имя станции. Это не список разрешений: аппарату не обязательно быть здесь, чтобы отправлять снимки, и его присутствие здесь ничего ему не разрешает. Выигрыш в том, что адресат заявки выбирается из списка, а не набирается руками, и что именно отсюда проверка рабочего списка одалживает AE title.
Заполняйте, когда
Вы раздаёте рабочий список или вводите заявки вручную. Пока не зарегистрирована ни одна станция, адресат заявки набирается руками, и опечатку нечем поймать.
Вкладка Модальности раздела Конфигурация: три зарегистрированных кабинета — ER CT, Ultrasound 2 и Portable CR — у каждого имя, AE title станции, код модальности и имя станции, отметка «включено» и кнопка «Проверить рабочий список», а под ними кнопка сохранения модальностей.
Кабинеты, которые обслуживает этот узел. Адресат заявки выбирается из этого списка, а Проверить рабочий список в строке спрашивает у чужой РИС больницы, что она отдала бы этому кабинету.

Проверка рабочего списка — спросить чужую РИС

Исходящий клиент · C-FIND рабочего списка · Конфигурация → Модальности

Что делает
Отвечает на вопрос «почему этот аппарат не видит свой список», не трогая сам аппарат. Задайте адрес настоящей больничной РИС — или её брокера — в Настройках и нажмите Проверить рабочий список в строке модальности: Carino задаёт этой РИС тот вопрос, который задала бы сама модальность, её же AE title, и складывает каждый ответ в Активность → Проверки рабочего списка. Один прогон задаёт один и тот же вопрос пять раз, снимая по одному ключу — станция, дата и модальность; затем без модальности; без даты; без станции, — потому что место неисправности показывает именно разница между ответами. Не вернулось ничего ни на один — у чужой РИС попросту нет записей; пустым вернулся только самый узкий — неверен AE title станции.
Прежде чем нажать
Сначала снимите модальность с сети. Проверка обращается как эта модальность, а две вещи, отвечающие на один AE title в одной РИС, — это путаница, которую потом придётся идти и разбирать.
Чего не делает
Это не служба, не расписание и не очередь. Во вкладке Проверки рабочего списка лежит запись о том, что ответил кто-то другой: эти заявки никогда не раздаются в собственном рабочем списке этого узла, и он ни на одну из них не претендует. Очистить опустошает эту запись и только её.
Вкладка Проверки рабочего списка раздела Активность: один прогон проверки от имени US_ROOM_2 к чужой РИС на pacs-peer с пятью вопросами — станция, дата и модальность, затем поочерёдно без каждого ключа — и ответом на каждый; здесь все ответы пусты, потому что в чужой РИС демо нет заявок. Справа кнопки «Обновить» и «Очистить».
Один прогон Проверить рабочий список: в заголовке — кого спрашивали и от имени какого AE title, а каждая строка — один вопрос с оставленными в нём ключами и ответ на него. Читайте от самого узкого вопроса к самому широкому: если более широкий вопрос вернул заявки там, где более узкий не вернул ничего, неверен тот ключ, который он убрал. Здесь на каждый вопрос ответ ничего — это значит, что у чужой РИС для этой комнаты ничего не запланировано, а не что неверен ключ: в РИС демо заявок нет.

Аварийная РИС — заявки HL7

Порт 2575 · HL7 ORM^O01 поверх MLLP

Что делает
Принимает заявки HL7 по MLLP, а также позволяет вводить их вручную в панели, когда выше по цепочке ничего живого не осталось. Когда исследование возвращается по C-STORE, оно сопоставляется со своей заявкой по номеру исследования (запасной вариант — ID пациента), и заявка закрывается и уходит в архив для аудита — но не стирается.
Включайте, когда
РИС недоступна или вы проверяете интеграцию.
Что гарантирует
Доставка изображений никогда не зависит от совпадения с заявкой. Исследование без подходящей заявки всё равно сохраняется и пересылается; заявка просто остаётся открытой для ручного сопоставления.
Панель заявок РИС: раскрывающийся блок «Новая заявка (ручной ввод)», вкладки «Открытые» и «Закрытые», поле поиска, переключатель звукового сигнала о новых заявках и три открытые заявки из РИС, в каждой номер исследования, пациент, модальность, запрошенная процедура, отправитель HL7 и время постановки в очередь, с кнопкой «Добавить PDF/изобр.».
Заявки, пришедшие по HL7, и раскрывающийся блок сверху для ввода заявки вручную, когда выше по цепочке ничего живого не осталось. Каждая закрывается сама, когда придёт соответствующее исследование, — и уходит в архив, а не стирается. Заявки из РИС может отменить только сама РИС, поэтому кнопки «Отменить» у них нет. Новая заявка заставляет мигать раздел Заявки, а это рабочее место может ещё и подать звуковой сигнал: переключатель действует на эту машину и запоминается. Заявка на фото глаза (модальность OP) предлагает ещё Фото глаза: редактор открывается с этой заявкой, а сделанные в нём фото возвращаются как исследование заявки, и она закрывается.

Ни TLS, ни учётных данных. См. раздел о безопасности: любой, кто может открыть сокет на этот порт, может внедрить заявки. Доверенная клиническая сеть и брандмауэр — или совсем не включать.

Аварийное переключение

Наблюдение по C-ECHO за назначением, помеченным основным

Что делает
Пометьте назначение основным и включите наблюдение: Carino периодически шлёт ему C-ECHO и следит за неудачами пересылки. Если он остаётся недоступным дольше порога, вы получаете предложение включить аварийную РИС. Включение запускает локальный рабочий список, чтобы лаборанты продолжали снимать, удерживает каждое исследование, принятое во время аварии, и пересылает его, как только основной узел вернётся. Когда выходить из режима — решаете вы.
Включайте, когда
Эта машина — шлюз к PACS, от которого вы зависите. Именно поэтому слово «непрерывность» стоит в первой строке этого руководства.

Оповещения — webhook и почта

Только исходящие · HTTP webhook, SMTP

Что делает
Достаёт до тех, у кого панель не открыта. Плашки на экране хватает тому, кто и так смотрит на передачу, и она бесполезна ровно в том случае, ради которого всё это есть: основной узел падает в 03:00, а дежурный рентгенолог не сидит ни перед каким экраном. Два канала, независимые друг от друга, и оба мертвы, пока не включён общий рубильник.
Что отправляет
Только события переключения: сработало, включено, восстановлено. Никакая другая часть узла оповещений не порождает. Это не система оповещений общего назначения и не становится ею оттого, что её включили.
Кому приходит почта
Профилям, а не адресам. Письмо уходит каждому включённому профилю, который указан в оповещениях переключения и у которого заполнен адрес почты. Настроенный SMTP без единого адреса ни в одном профиле не отправляет ничего и молча — это первое, что стоит проверить, когда «почта включена, а никого не предупредили». Формулировки зависят от роли, а адрес отказавшего назначения попадает в письмо только тому, кому и так позволено видеть таблицу назначений: письмо уходит за пределы здания, и отозвать его нельзя.
Чего не сделает никогда
Не задержит и не сломает то, о чём сообщает. Каждая отправка передаётся рабочему потоку, очередь ограничена — самое старое отбрасывается, и отброшенное считается, потому что «основной узел лежит», доставленное после восстановления, хуже молчания, — а сбой становится счётчиком и строкой журнала, а не исключением. Отправлено, не удалось, отброшено, в очереди и последняя ошибка — всё это можно прочитать, поэтому «включено и ни разу ничего не доставило» видно до той аварии, которая на это рассчитывает. Проверяйте спокойным днём, а не во время аварии.
Где настраивается
Пока не в панели: в разделе notify файла config.json (URL webhook, сервер SMTP и общий рубильник notify.enabled). Секрет webhook и пароль SMTP задаются через POST /api/notify/secret, а не руками в файле. Каждый ключ описан в CONFIGURATION.md.

Виртуальная печать

Порт 11113 · DICOM Print, вывод в PDF или Secondary Capture

Что делает
Представляется принтером DICOM (по умолчанию AE title CARINOPRINT) и захватывает каждый присланный лист плёнки. Для оборудования, единственный вывод которого — плёнка, это способ сохранить хоть что-то, что можно подшить. Надёжных данных пациента плёнка не несёт, поэтому каждая ждёт в Ожидают, пока человек её не идентифицирует; ничего напечатанное само дальше не уходит.
Включайте, когда
У вас есть аппарат, умеющий только печатать, чей вывод вы хотите спасти. Шаги — в Как сделать… печать.
Настройки
В Конфигурация → Настройки → Печать: AE title и порт, Принимать только эти AE (аппараты, которым разрешено печатать; пусто — любые), Принимать также цветные задания печати и Сохранять каждую снятую плёнку как PDF или изображение. Выбирайте изображение, когда принимающий PACS или просмотрщик не показывает PDF, — а многие не показывают.
Как понять, что работает
Отправить тестовую печать на карточке прогоняет через приём целое задание печати и кладёт в Ожидают лист с пометкой «TEST PRINT — safe to discard». После настоящего задания карточка показывает Последняя печать (сколько плёнок, с какого AE и адреса) и счётчик Захвачено. Отказ или сбой показывается как Последняя проблема; см. таблицу неполадок печати.
Что допускает
Аппараты, оставляющие номера сеанса плёнки и плёночного листа принтеру, Presentation LUT (принимается и игнорируется) и AE title принтера, набранный на аппарате иначе: вызываемый AE не проверяется.
Вкладка Ожидают раздела Исследований, на вкладке число 3: JPEG и под ним PDF, пришедшие рядом с МР-исследованием; у обоих уже заполнены ФИО пациента, ID пациента, номер исследования, дата и описание исследования, взятые из него, есть ссылка «Просмотр», кнопка «Сопоставить с заявкой…» и кнопки «Утвердить» и «Отбросить». Третий элемент — плёнка, снятая приёмом печати, — ниже по списку, за краем снимка.
JPEG и PDF, найденные рядом с МР-исследованием, — у обоих уже заполнены данные этого исследования. Всё, что не DICOM, ждёт здесь, а не превращается в DICOM наугад; плёнка, снятая приёмом печати, тоже попадает на эту вкладку — в этом списке она ниже этих двух. Выберите открытую заявку, к которой элемент относится, кнопкой Сопоставить с заявкой… или введите данные (ID и ФИО пациента обязательны), затем утвердите. Смените пациента у элемента, пришедшего рядом с исследованием, — и вас спросят, относится ли он всё ещё к этому исследованию или начинает новое.

Веб-панель

Порт 8042 · HTTP, по умолчанию 127.0.0.1

Что делает
Состояние каждой службы, назначения, заявки, живой журнал активности, настройки и встроенный редактор DICOM. Всё, что делает панель, есть и в CLI.
Как ориентироваться
В боковой панели шесть разделов: Обзор, Службы, Исследований, Заявки, Конфигурация и Активность — и седьмой, Тестовый узел, только в процессе, запущенном с --dev-peer. Три вопроса, задаваемые одной и той же куче файлов, — это вкладки раздела Исследований: История, Ожидают и Застряли; то, что задаётся при вводе машины в работу, — это вкладки Конфигурации: Назначения, Маршруты, Настройки, Модальности и Люди, и открывается она на Назначениях, а не на Настройках; а записи — это вкладки Активности: Журнал, Подключения, Аудит и Проверки рабочего списка. Подключения перечисляют недавние DICOM-подключения в обе стороны и говорят, почему одно из них не удалось и что изменить, на какой машине; кнопка Что принимает в строке назначения спрашивает этот узел о каждом типе изображений, хранящемся здесь. Проверки рабочего списка — то, что ответила чужая РИС, когда этот узел спросил её как одну из ваших модальностей. У Заявок своя полоса открытых и закрытых. Раздел Исследований несёт два счётчика, которые сами по себе являются кнопками, 📎 ожидающих и ⚠ застрявших, и каждый открывает свою вкладку: поэтому попасть в Застряли — один клик, пока что-то застряло, и два (раздел, затем вкладка), когда не застряло ничего, потому что нулевой счётчик прячется. Счётчик, постоянно показывающий 0, — это вечно включённая сигнализация, а на такие никто не смотрит. У Заявок свой счётчик на тех же условиях. У каждой панели и вкладки есть свой адрес — #studies/stuck, #configuration/routing, #activity/logs, — поэтому место в панели можно занести в закладки, продиктовать по телефону во время аварии и покинуть кнопкой «Назад» в браузере; старые ссылки вида #dlgStuck по-прежнему работают. Обзор — единственное исключение: он откликается на #overview, если его набрать, но никогда не пишется в адресную строку и не оставляет записи в истории, потому что он печатает имя пациента, а оставленный без присмотра экран не должен возвращаться к нему после перезагрузки.
Индикаторы служб в шапке
Индикаторы служб вдоль верхнего края не меняются для тех, кому можно запускать и останавливать службы. Профиль без services.control по-прежнему их видит — как индикаторы, — но они неактивны: состояние службы не является привилегированным (регистратура должна видеть, что приём не работает), а переключатель, который сервер всё равно отклонит, предлагать незачем.
Уводите с петлевого адреса, когда
Только при конкретной причине — и тогда с токеном (обязательно) и с HTTPS впереди. См. правило токена.
Вкладка Настройки раздела Конфигурация, раскрытая на первой группе «Этот PACS» с пометкой, что приём работает: флажок «Запускать эту службу (и при старте)», AE title приёмника, адрес прослушивания, порт, папка хранения, флажок упорядочивания по пациенту, исследованию и серии, порог свободного места, список разрешённых AE и флажок DICOM поверх TLS. Остальные группы ниже, а справа внизу — кнопки «Заново выбрать службы» и «Сохранить конфигурацию».
Всё, что есть в config.json, в виде формы, сгруппированной по задачам: раскройте нужную группу, остальные пусть остаются свёрнутыми. Файл остаётся источником истины: правка здесь записывается в него, а конфигурация, которую панель отказалась бы сохранить, отклоняется с указанием причины и поля, к которому она относится, а не переписывается молча. Группы — это сворачиваемые разделы: здесь раскрыт Этот PACS, а Отправка, Рабочий список и заказы, Печать и остальные свёрнуты ниже; Остановить службу стоит отдельно в последней группе, Опасная зона. Заново выбрать службы и Сохранить конфигурацию — кнопки в нижней строке, всегда на виду.
Встроенный редактор DICOM с открытым примером исследования: слева изображение с ползунками центра и ширины окна, справа панель «Основное» (клиническое) с пациентом, модальностью и описанием исследования, где имя пациента скрыто за кнопкой «Показать», а под ней панель «Техническое» с SOP Instance UID, синтаксисом передачи, фотометрической интерпретацией и SHA-256 файла.
Встроенный редактор, открываемый из исследования на вкладке История или сам по себе. Он работает целиком в браузере — никуда ничего не загружается, — и имя пациента изначально скрыто, потому что чаще всего заголовок открывают, чтобы прочитать UID.
Вкладка правки в редакторе: панель инструментов с кнопками «Анонимизировать (PS3.15)», «Параметры», «Вымышленный пациент (тестовые данные)», отмена и повтор, «+ Тег», JSON, CSV, «Печать», «Этот файл» и «Скачать всё»; полоса из пяти загруженных серий; фильтры по пациенту, исследованию, серии, изображению, оборудованию, UID и приватным тегам; сама таблица тегов, по строке на атрибут с номерами группы и элемента, описанием, VR и редактируемым значением; и в правой трети панель правки изображения — поворот, отражение, инверсия и закрашивание впечатанного текста, записываемые в сохранённые пиксели — над предпросмотром, свёрнутым значением окна/уровня и областью для перетаскивания файлов.
Таблица тегов, где заголовок исправляют перед повторной отправкой исследования: неверный номер исследования или опечатка в ID пациента, поправленные на шлюзе, а не на аппарате, который уже занят другим.

Тестовый узел — одноразовый второй архив

Только петлевой адрес · pacs serve --dev-peer · никогда в поставляемой сборке

Что делает
Создаёт внутри этого же процесса целый второй архив — только на 127.0.0.1, со своим AE title, своими портами приёма и Query/Retrieve и своим хранилищем во временной папке, — а на этом узле сразу появляется назначение, нацеленное на него. Это настоящее место, куда исследованию есть куда уйти, пока вы проверяете, что пересылка, правило маршрутизации, обезличивание при пересылке или C-MOVE делают то, что вы думаете. Рядом создаётся выключенное назначение-«чёрная дыра»: включите его — и отправки начнут падать нарочно, а это и есть способ заполнить вкладку «Застряли» по требованию и увидеть, как выглядит затор, до того как он случится по-настоящему.
Как открыть
Запустите движок с pacs serve --dev-peer — он пишет об этом строкой ниже адреса панели, — затем выберите в боковой панели 🧪 Тестовый узел и нажмите Создать узел. Дальше панель показывает его AE title, оба порта, папку и сколько он принял. Без этого ключа строки просто нет, API отвечает 404, и никакая настройка в config.json её не вернёт. Так и задумано: настройку может изменить любой, у кого есть токен панели, а до аргумента запуска по HTTP не дотянуться вовсе.
Кому можно
Профилю, которому позволено создавать и удалять одноразовый тестовый архив. Из встроенных профилей это ИТ и администратор: доказать, где умирает пересылка, — работа стенда. Регистратуре и рентгенологу это не даётся: второй архив — инструмент стенда, а не клиники. И создание, и удаление записываются в журнал аудита.
Что станет с тем, что он сохранил
Уходит всё — и папка, и обе строки назначений на этом узле — когда вы нажимаете Удалить, когда процесс останавливается и при следующем запуске после аварийного завершения или отключения питания. Не остаётся ничего, и ровно ради этого им пользуются вместо того, чтобы написать вторую конфигурацию руками и забыть про папку. Одно стоит иметь в виду: удалите узел, пока против него ещё стоят отправки, — и этим отправкам будет некуда повторять; разберите их в разделе Исследований → Застряли.

Это инструмент стенда, и ключ запуска — то, что удерживает его в этой роли. Узел слушает только петлевой адрес, без исключений: тестовый архив, который могла бы найти модальность из сети, был бы вторым, никем не проверяемым хранилищем чьих-то снимков, и никто бы не заметил, что он там есть. Не передавайте этот ключ на узле с настоящими исследованиями. Настольная и контейнерная сборки никогда не передают его за вас.

7. Это не медицинское изделие

Carino DICOM не является медицинским изделием. Он не сертифицирован, не допущен и не зарегистрирован ни одним регулятором ни в одной стране. У него нет ни маркировки CE как медицинского изделия, ни допуска FDA, ни регистрации в ANVISA, COFEPRIS или любом равнозначном органе. Никто не проводил в отношении него валидацию клинического применения.

Он не предназначен для первичной диагностики. Диагностического просмотрщика здесь нет: ни оконного преобразования, ни измерений, ни калиброванной цепочки отображения, ни контроля над монитором, на котором он показан. Читайте исследования на валидированной рабочей станции, которая у вас уже есть.

Сказано прямо, потому что это полезнее сноски:

Всё это не пессимизм. Проект всерьёз исходит из того, что изображение, которое молча так и не пришло, хуже, чем сбой, — поэтому он скорее откажется запускаться, будет явно считать неудавшиеся подоперации и отправит лишнее, чем недоотправит. Он придерживает доставку ровно в одном случае, громко и обратимо: назначение, для которого правило требует обезличивания, когда обещанную очистку выполнить нельзя, — потому что профиль выключен или потому что профиль включён, но деидентификатор построить не удалось. И он говорит, что именно из двух, потому что чинятся они по-разному. Но программа может отвечать только за то, что делает сама; остальное — на вас.

8. Лицензия и где получить помощь

Carino DICOM выпускается под AGPL-3.0-or-later. Поскольку это сетевой сервер, применяется §13: если вы запускаете изменённую версию как сервис, вы обязаны предложить её исходный код тем, кто им пользуется. Сохраняйте файл LICENSE и ссылку на исходный код при любом изменённом развёртывании.

Нашли ошибку в этом руководстве — или перевод, который врач-рентгенолог никогда не произнёс бы? Заведите issue. Руководства на русском, испанском, португальском и японском — часть проекта, а не дополнение.

Carino DICOM · главная страница · часть мастерской carino.systems · AGPL-3.0-or-later.