Carino DICOM マニュアル

Carino DICOMはDICOMゲートウェイであり、継続運用のための アプライアンスです。検査を受信し、振り分けて転送し、保持しているものをクエリ/取得と DICOMwebで公開し、PACSやRISが応答しなくなっても画像診断部門の仕事を止めません。本マニュアルは、 導入、日々の作業とそれがうまくいかないときの対処、セキュリティモデル、すべてのサービス、そして このソフトウェアの実際の限界を扱います。

ここに載せているスクリーンショットはすべて、このソフトウェアが実際に動いているところで、掲載のために作った架空の検査を保持しています。本マニュアルに患者は一人も登場しません。

目次

  1. これは何で、どんな役割を担うのか
  2. はじめかた
  3. こんなときは…
  4. うまくいかないとき
  5. セキュリティモデルとトークンのルール
  6. サービス、ひとつずつ(リファレンス)
  7. これは医療機器ではありません
  8. ライセンスと相談先

1. これは何で、どんな役割を担うのか

Carino DICOMは、画像を生み出す機器と、それを保存したり読影したりするシステムとのあいだに 立ちます。あなたのアーカイブを置き換えようとしているのではありません。それらをつなぐ部品であり、 そのどれかが倒れたときにも立っている部品であろうとしています。

役割は二つあり、この区別はどのサービスを有効にするかを決めるので重要です。

すべては一つのconfig.jsonで動き、すべてヘッドレスで動きます。Webダッシュボードは 便利ですが、必須ではありません。

既定のポート

サービスポートプロトコル
受信(Storage SCP)11112DICOM / DIMSE
仮想プリント11113DICOM Print
Modality Worklist11114DICOM C-FIND
クエリ/取得11115DICOM C-FIND/C-MOVE/C-GET
緊急RIS2575MLLPで運ぶHL7
ダッシュボード + DICOMweb8042HTTP

なぜ104ではなく11112なのか。DICOMの登録済みポートは104ですが、これはLinuxと macOSでは特権ポートでrootを必要とします。既定値はそれを避けています。機器の設定をこれに合わせるか、 コンテナかファイアウォールで104を11112へ公開してください。

2. はじめかた

動かす形を選ぶ

形向いている場面停電のあとに自分で戻るか
デスクトップアプリ(トレイ)人が前に座っているPC。検査室ひとつ、診療所ひとつ、 お試しの評価。「ログイン時に起動」を設定した場合のみ
Docker / Podmanサーバー、あるいは隔離してまるごと移せる状態にしておきたい PC。戻る(restart: unless-stopped)
systemdサービス画像診断部門の隅に据え置く常設のPCで、誰もログインして いないもの。戻る

デスクトップアプリ

お使いのOS向けのパッケージを リリースの ページからダウンロードして開いてください。システムトレイに収まり、クリックするとダッシュボードが 開きます。まだコード署名をしていないため、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. サービスを選びます。すべてオフの状態で出荷されます。ダッシュボードの初期設定の 画面が、このPCは何を動かすべきかを尋ねるので、それだけを有効にしてください。
  2. 送信先を追加します(名前、ホスト、ポート、AE title)。場所は設定一式の 送信先タブで、その行を開くと最初に出るタブです。
  3. 画像を1枚も動かす前に疎通を確かめます。送信先の行のEchoを押して ください。ダッシュボードのないPCでは、./run.sh echo --name "Hospital PACS"で同じことが できます。Echoが通らないなら、問題はCarinoではありません。ネットワークか、ファイアウォールか、 AE titleです。
  4. 機器を1台こちらへ向けます。受信のカードにあるモダリティに設定する宛先の 行を使って設定し、テストの検査を送ってください(こんなときは…を参照)。
  5. アクティビティのログを見ます。アクティビティのログタブです。 何かが失敗すればそこに出て、そのサービスのカードにも直近の問題として出ます。このソフトウェアは、 黙って失敗するより声を上げるほうを選びます。
ダッシュボードの初期設定の画面。「このPCで動かすサービスを選んでください」という見出しの下に、サービスごとのカード — 受信、自動送信、プリント受信、緊急RIS、ワークリスト、クエリ/取得 — が並び、それぞれに「使う」のチェックボックスとポートが空いていることを示す行があります。チェックが入っているのは受信だけで、下部には「1件を選択中」という表示と、「今はしない」「オンにする」のボタンがあります。
手順1。最初からチェックが入っているのは受信だけです。このPCを送り先にするのはこのサービスだからで、下部には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 <path>を受け付けます。Windowsでは run.ps1です。

3. こんなときは…

現場でよく出てくる作業の短い手順です。どの手順も、うまくいったことをどう確かめるかで終わります。 うまくいかなければ、次の章が確認すべきことの一覧です。

…モダリティから画像を保存させたい

  1. サービスを開きます。受信のカードが停止中なら開始を押します。
  2. カードのモダリティに設定する宛先の行を読みます。AE title、IPアドレス、ポートの三つです (既定ではCARINODICOMと11112)。横のコピーボタンで三つまとめてコピー できます。このPCに複数のアドレスがある場合は、モダリティと同じネットワークのものを使ってください。
  3. モダリティ側で保存先を追加し — メーカーによってDICOMノード、アーカイブ、「送信先」「Store」など 呼び名はさまざまです — その三つの値を入れます。モダリティ自身のAE titleは発信側のAEで、 ここに入れる必要はありません。
  4. 設定一式 → 設定 → このPACSのこれらのAEのみ許可にAE titleが 並んでいる場合は、そこにモダリティのAE titleを加えてください。加えないと受信が拒否します。
  5. モダリティ自身の接続テスト(多くはVerifyやEcho)を実行し、検査を一つ送ります。 検査 → 履歴に現れます。

カードのテストボタンは、受信をこのPC自身から確かめます。テストは通るのに モダリティからは失敗する場合、問題は両者のあいだにあります。アドレス、ポート、ファイアウォール、 あるいはAEの許可リストです。

…モダリティから印刷させたい

出力がフィルムしかない機器のためのものです。Carinoはフィルムプリンタのふりをして、各シートを取り込み、 あなたが患者を特定できるようにします。紙には何も印刷されません。

  1. サービスを開き、プリント受信のカードで開始を押します。
  2. カードのモダリティに設定する宛先の行を読みます(既定ではCARINOPRINT、このPCの IP、ポート11113)。
  3. モダリティ側でDICOMプリンタ — レーザーイメージャやフィルムプリンタと呼ばれることもあります — を 追加し、その三つの値を入れます。グレースケールは常に受け付けます。モダリティがカラーでしか印刷しない 場合は、設定一式 → 設定 → 印刷でカラーの印刷ジョブも受け付けるに チェックを入れてください。
  4. カードのテスト印刷を送るを押します。数秒でTEST PRINT — safe to discard と書かれたシートが検査 → 保留に現れます。これで受信側が動いていることが分かります。 そのシートは破棄してください。
  5. モダリティからフィルムを1枚印刷します。カードの最後の印刷にフィルムの枚数、モダリティの AE title、アドレスが出て、取り込み数が増え、フィルムは保留で承認を待ちます (下を参照)。

印刷の取り込んだフィルムの保存形式は、承認したときにフィルムを何にするかを決めます。 多くのアーカイブやビューアはPDFを保存はしても表示はしません。相手側でフィルムを読む必要があるなら 画像(Secondary Capture)を選んでください。

モダリティに入力したプリンタのAE titleは確認されません。打ち間違えていても印刷はできます。絞り込まれる のはモダリティ自身のAE titleだけで、それもこれらのAEのみ許可を埋めた場合に限ります。印刷に 失敗するとカードに直近の問題が出ます。すべてのメッセージとその直し方は 印刷のトラブル対処表にあります。

…モダリティにワークリストを取らせたい

  1. オーダがあることを確かめます。RISが緊急RISのサービス(ポート2575)へHL7 オーダを送るか、誰かがオーダ → 新規オーダで入力します。
  2. 設定一式 → モダリティで、その検査室をステーションのAE titleを正確に入れて登録 します。以後、オーダの宛先は手入力ではなくその一覧から選べます。
  3. サービスを開き、ワークリストのカードで開始を押し、 モダリティに設定する宛先の行を読みます(既定ではCARINOMWLとポート 11114)。
  4. モダリティ側で、ワークリストの提供元(MWL、「HIS/RIS」「ワークリストサーバー」など)にその値を 設定し、問い合わせます。

宛先のないオーダはすべてのステーションに表示されます。宛先のあるオーダは、AE titleが完全に一致する ステーションにだけ表示されます。検査が戻ってくるとアクセッション番号でオーダと照合され、オーダは 自動で完了します。

…保留中のフィルムを承認したい

取り込んだフィルムや、検査の隣で見つかったPDF・画像は検査 → 保留で待っています。 検査の横の📎の数が件数です。

  1. プレビューを開いてシートを見ます。
  2. その患者に未処理のオーダがあれば、オーダに照合…を押して選びます。氏名、患者ID、 アクセッション番号、検査はオーダから入り、オーダは取り込み済みとして完了します。
  3. そうでなければ患者情報を自分で入力します。患者IDと患者名は必須で、 アクセッション番号、日付、説明もできるだけ入れてください。
  4. 検査の隣に届いた項目で患者を変えた場合は、どこに属するかを選びます。元の検査に付けるは、 その検査が本当にこの患者のものである場合だけにし、そうでなければ新しい検査にしてください。 選び間違えると、ある患者のシートが別の患者の検査の中に収まってしまいます。
  5. 承認して送信を押します。DICOMに変換され、ほかの検査と同じように転送されます。

取り消しはできません。承認すると元のファイルは保留から消え、DICOMのコピーが送られていきます。間違いは アーカイブ側で直すことになります。破棄は、送らずに項目を取り除きます。

…滞留した転送を片付けたい

検査 → 滞留を開きます(検査の横の⚠の数)。行は検査ごとにまとめられ、 患者、アクセッション番号、説明が付き、三種類に分かれます。

…何が失敗したのか知りたい

  1. そのサービスのカードを見ます。どの待ち受けも、何かがうまくいかなかったときは直近の問題を 表示します。クリックすると、そのサービスに絞り込んだ状態でログが開きます。
  2. アクティビティ → ログで、サービスのチップ(保存、送信、印刷、ワークリスト、 Q/R、RIS…)、エラーと警告のみ、文字列の絞り込みを使います。AE title、IPアドレス、 アクセッション番号を入れるとすぐに絞れます。
  3. ほかの人に問題を渡すときは、表示中の行をコピーするか、その日のログをまるごとダウンロードします。
  4. カードのテストを押します。このPCからのテストは通るのにモダリティからは失敗するなら、両者の あいだのネットワークを調べてください。
  5. 設定が変わったのに誰が変えたか分からないときは、アクティビティ → 監査が、変えた人と 変えたセクションを示します。

4. うまくいかないとき

まず症状、次に見る場所、そして直し方です。ほとんどの行は同じ二か所から始まります。サービスに あるそのサービスのカード(直近の問題)と、アクティビティ → ログです。 ログの行はエンジンの出力そのままなので英語です。

送信・受信・ワークリスト

症状見る場所直し方
モダリティからまったく送れない受信のカード。Receiver: refusedで始まる ログの行its AE title is not in the allowed list: モダリティのAEを これらのAEのみ許可に加えるか、一覧を空にします。ログに何もない: 接続が届いていません。 モダリティのIPとポート、ファイアウォール、カードが動いているかを確認します。カードのテストで 「このPC」と「ネットワーク」を切り分けられます。
モダリティは送ったのに、履歴に検査がないログの保存チップ。概要の 空き容量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: refused: そのAEをワークリストの許可リストに加えます。ログに問い合わせが ない: モダリティ側のIP/ポート/AEが違います。問い合わせは来ているのに一覧が空: オーダの宛先AEが ステーションと完全には一致していません。モダリティで直してください。
病院自身のRISがモダリティにリストを渡さない設定一式 → モダリティ → ワークリスト確認ワークリスト確認を参照してください。答えは アクティビティ → ワークリスト確認の結果に入ります。
RISからのHL7オーダが現れない緊急RISのカード。ログのRISチップRISが このIPのポート2575へ送っているか、またris.allowed_hostsが設定されている ならそのアドレスが入っているかを確認します。
全員がサインアウトされた—再起動のあとは正常です。セッションは設計上メモリにしか ありません。もう一度サインインしてください。
ダッシュボードが反応しない、数字が動かないページを再読み込みする戻らない なら、エンジンが応答していません。サービスかコンテナが動いているかを確認し(systemctl status carino-pacs、docker compose ps)、そのログを読みます。
コンテナがEACCESで終了するdocker compose logs pacs./dataに コンテナのユーザーが書き込めません。.envにPACS_UID/PACS_GIDを 書いて再ビルドします(上の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ライブラリ自身が 出したメッセージをそのまま流したものです。直前の行と合わせて読みます。分からなければ報告 してください。
ログに何も出ない接続が届いていません。カードは動いていますか(橙色では ありませんか)。モダリティはこのPCのIPとポート11113を使っていますか。ファイアウォールが 邪魔をしていませんか。カードのテスト印刷を送るで、こちら側は確かめられます。

原因ではないものが二つあります。モダリティに入力したプリンタのAE title(確認されません)と、 モダリティがフィルムセッションやフィルムボックスの番号をプリンタ任せにすること(無ければこちらで 作ります)です。Presentation LUTは受け付けたうえで無視します。先にプリンタの構成を問い合わせるモダリティにも応答します。

アクティビティのログタブ。流れの上にサービスごとのチップ、エラーと警告のみのチェックボックス、文字列の絞り込みがあります。その下にその日の時刻付きの行が並び、新しい行ほど下にあり、どの行にも書き込んだサービスの印が付いています。流れの下には行数、「表示中をコピー」、日付の選択、「その日のログをダウンロード」「表示をクリア」があります。
上の表のどの行も、ここから始まります。ログは最新の行、つまりいちばん下を表示した状態で開きます。エラーは赤、警告は橙色で示され、意味のある行はすべて、それについて何をしてほしいのかを述べます。流れが速いときは、サービスのチップ(受信、自動送信、印刷など)、エラーと警告のみ、文字列の絞り込みで行を絞ってください。表示中をコピーは絞り込んだ後に残っている行をコピーし、その日のログをダウンロードは隣で選んだ日のファイルを丸ごと保存し、表示をクリアは画面を空にするだけでファイルは消しません。

5. セキュリティモデルとトークンのルール

サインインの入口。「このPACSにはアクセストークンが必要です」と題したカードに、パスワードの入力欄がひとつ、サインインのボタン、そしてトークンは設定ファイルの web.auth_token であること、保存済みのトークンをダッシュボードが表示することはない旨の注記があります。
ダッシュボードの代わりにこれが出ます。あとから401になったときにも、また出ます。再起動すると設計上すべてのセッションが切れるので、もう一度サインインするのは不具合ではなく通常のことです。

まず要となる点から。ダッシュボードはアーカイブの鍵です。これを開ける人は、保存されて いるすべての検査を読み、DICOMファイルをダウンロードし、あらゆる設定を変更し、サービスを開始・停止し、 検査を削除し、サーバーを停止できます。初期状態では共有の秘密がひとつあるだけで、それを持つ人は何でも できます。プロファイルは任意で、有効にするまではオフです。有効にすれば、各自が自分と してサインインし、自分の権限、自分に見える患者の識別情報、そして監査証跡に残る自分の名前を持ちます。 共有トークンは管理者として動作し続けるので、すでにそれを使っている仕組みが壊れることはありません。

設定一式のスタッフタブ。「プロファイル 4 件。」という行の下に、管理者のカードが短く置かれています。「管理者(今後追加される権限も含めてすべて)」にチェックが入っているため、権限の表はなく、パスワード欄と「保存」「削除」だけです。その下のITのカードには「できる操作」のチェックボックスの表が並び、スクリーンショットはその表の途中、「見られる情報」の行より前で切れています。右下には「追加」ボタンが固定されています。
プロファイルを有効にしたあとの姿です。権限は人ごとで、ブラウザの中に隠すのではなく、すべてのエンドポイントで強制されます。そして見られる情報はできる操作とは別です。患者の氏名を一度も読むことなく、アクセッション番号で検査を振り直せるプロファイルを作れます。チェックの入っていない項目は、本来表示される場所すべてで***と表示されます。

権限はダッシュボードが何を描くかも決めます。プロファイルに見えるのは、その権限に見合うサイドバーの 行と、その中のタブだけです。routing.readを持ちconfig.readを持たない放射線科医 には、送信先とルーティングのある設定一式が見え、設定タブはありません。 そのどれも持たない受付には、設定一式の行そのものがありません。上部ヘッダのサービスチップは 意図的な例外です。下のWebダッシュボードを参照してください。

プロファイルを使うアプライアンスでの同じ入口。管理者、IT、放射線科医、受付のボタンがそれぞれ南京錠付きで並び、その下に「アクセストークンでサインインする」というリンクがあります。
プロファイルを使うアプライアンスが出す入口です。この選択画面が描かれるのはusers.list_profilesがオンのあいだだけです。そのポートに到達できる人へスタッフの一覧を公開するのは本物の情報開示であり、判断はあなたに委ねられています。トークンは下に畳まれて今も使えます。誰かが自分を締め出したときの、戻り道だからです。

ルール

web.auth_tokenが空であってよいのは、web.hostがループバックのあいだ だけです。web.hostがそれ以外 — 0.0.0.0、LANのアドレス、ホスト名 — で トークンが空なら、サーバーは起動を拒否します。この拒否は、迂回すべきエラーでは なく、機能です。

理由は具体的です。127.0.0.1で認証なしのとき、アクセス制御を担っているのはOSです。この PC上のプロセスだけがAPIに到達できます。それは筋が通ります。しかしweb.hostは運用者が 変更できる値で、誰かが「向こうのPCから入れるように」と0.0.0.0に書き換えた瞬間 — 急いで いて、火曜日で、セキュリティのことなど考えずに — その同じAPIは、LANの隣人の誰にでも検査の一覧、保存先の パス、DICOMのバイト列、そして/api/shutdownを差し出します。この変更が10秒で済み、その結果が 何年も続くからこそ、このルールがあります。

コンテナは常にその状況にあります。コンテナは仕組み上0.0.0.0で公開する からです。そのためイメージは、こちらがトークンを渡さなければ初回起動で256ビットのトークンを生成し、 それを出力します。決してしないのは、黙って生成することです。誰も見ていない秘密は、誰も更新 しない秘密です。

トークンの生成と渡し方

./run.sh init --token                                  # config.json に書き込む
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
openssl rand -base64 32

サーバーは次のいずれの形でも認証情報を受け付けます。

クッキーがあるのは、ダッシュボードがトークンを尋ねるのを一度きりにするためです。 JavaScriptの中に持ち続ければ、あらゆるXSSとあらゆるブラウザ拡張がそれを読めます。クッキーがトークンを 運ぶことは決してありません。運ぶのは、起動時に生成してメモリ上だけに置いた秘密によるHMACです。だから 再起動すると全員がサインアウトします。運用者が一人のアプライアンスには、これが妥当な割り切りです。ディスク上に セッションの保管場所はなく、漏れるものもなく、最悪でも1シフトに一度トークンを打ち直すだけです。

ダッシュボードは平文のHTTPで通信します。Web層には組み込みのTLSがありません。ループバックの 外へ公開するなら、HTTPSを終端するリバースプロキシの後ろに置いてください。共有ネットワークで平文の HTTPに載せて送ったトークンは、渡してしまったトークンです。

DICOM側

このモデルが守らないもの

バックアップ。sqliteの索引はキャッシュで、自分で作り直せます。画像はそうではありません。 保存先のディレクトリをバックアップし、そして少なくとも一度は復元を試してください。

テレメトリはありません。ただし、あなた自身が入れる例外がひとつだけあります。エンジンは どこへも何も送りません。解析も、クラッシュレポートも、利用回数の集計も、実行時に取ってくる第三者の スクリプトもありません。外向きの接続は、あなたが設定したDICOMのアソシエーションとHL7の応答だけで、 接続先はあなたが名指しした相手だけです。そしてDocker、Podman、systemdでの運用はそれだけであり、 管理者が下記のバージョン確認をオンにしない限り、それ以上は何もしません。ここが肝心なところで、患者データを載せたまま検査室に置かれるのはその形だから です。デスクトップアプリだけは、これに加えて新しいバージョンが出ていないかを確認できます。ただし初回 起動時に一度だけ出る質問に「はい」と答えた場合に限られ、既定では無効で、トレイのメニューからいつでも また無効にできます。この確認はGitHubのリリース一覧に対する一日一回のHTTPSリクエストひとつだけで、 User-Agentのほかには何も付きません。識別子も、カウンタも、設定も、患者データもありません。したがって GitHubに分かるのはこのPCのIPアドレスと、アプリを起動したおおよその時刻だけで、それが全部です。何も ダウンロードされませんし、勝手に入るものもありません。デスクトップアプリを使わない運用のために、ダッシュボードは同じ確認をサーバー自身から行えます。管理者が概要のバージョン横のボタン、または 設定 → 連携 からオンにするまでは無効です。送るのはまったく同じリクエストで、ブラウザーからではなくサーバーから送られます。オンにすると概要にこのバージョンが最新かどうかが表示され、更新ボタンでこのサイトが開きます。これを超えるものを送る変更、あるいは誰も同意 していないものを送る変更は、脆弱性として扱われます。

アクティビティの監査タブ。ファイル数、ディスク上のサイズ、チェーン先端のダイジェストを示す行があり、その下に「全員」「すべての操作」「失敗・拒否のみ」の絞り込みと、右端に「整合性を検証」「エクスポート」のボタンがあります。時刻付きの行は新しい順に並び、いずれも「完了」で詳細は折りたたまれています。サインインのたびに一行ずつ増える、エンジンがアクセストークンでサインインした記録と、その下にアクセストークンによるUS_ROOM_2の「ワークリストを確認」の記録があります。
監査証跡と、その上に印字されたチェーンの先端です。このダイジェストこそ、このPCが到達できない場所へ写しておく値打ちのある値です。このタブには専用の絞り込みの行があり、誰の操作か(全員または一人)、どの操作か、失敗・拒否のみを選べ、その右端に整合性を検証とエクスポートがあります。表示をクリアはログのもので、ここにはありません。拒否は成功と同じ忠実さで記録されます。断られた要求も、ここでは他と変わらない一行です。

6. サービス、ひとつずつ

ポートを開くものはすべてオフの状態で出荷されます。正しい問いは「何ができるか」ではなく「このPCは何をする必要があるか」です。オンにしたサービスが増えれば、開いたポートもその分だけ増えます。下記の索引だけが唯一の例外で、これは何も待ち受けないローカルのsqliteキャッシュなので、自動的に起動します。

各サービスのスイッチは一つだけです。カードの開始はサービスを起動し、再起動のあとも動いたままに します。停止は確認してから止め、止まったままにします。設定一式 → 設定では、 同じスイッチがこのサービスを動かす(起動時も)のチェックボックスになっているので、カードと設定が 食い違うことはありません。設定を保存して再起動されるのは、自分の設定が変わったサービスだけで、どれを 再起動したかはダッシュボードが伝えます。

サービスのパネル。サービスごとにカードが並びます — 受信、自動送信、プリント受信、緊急RIS、ワークリスト、クエリ/取得 — それぞれに状態のドット、停止ボタン、AE title・IPアドレス・ポートとコピーボタンの付いた「モダリティに設定する宛先」の行、テストボタン、カウンタが表示されています。プリント受信のカードには最後の印刷の行と「テスト印刷を送る」ボタンもあります。
各サービスはそれぞれ個別に開始・停止し、どのカードにも技師に必要なものが載っています。モダリティに入力する宛先、このPCからサービスを確かめるテスト、そして見ておくべき二つの数字 — 処理した件数と、失敗した件数です。何かが失敗すると、直近の問題が何が起きたかを述べ、ログへリンクします。ダッシュボードはエンジンそのものではありません。ここで止めれば止まるのはその待ち受けだけで、アプライアンスではありません。
概要のパネル。稼働中のサービス、受信数、送信数、滞留、保留、未処理オーダ、空き容量を数えるタイルが並び、その下にこのPCのアドレス、受信、設定ファイルと保存先のパス、各送信先への到達可否、最後に受信した検査を示すカードがあります。
概要は「このPCは動いているか」にひと画面で答えます。行き先のあるものを数えるタイルはすべて、それを担当するパネルへのボタンになっていて — 滞留と保留はそれぞれ検査の自分のタブを開きます — 思いがけない数字はその詳細までワンクリックです。空き容量だけは例外です。開く先がないので、何もしないボタンにはせず、そのままの表示にとどめています。

受信 — Storage SCP

ポート11112 · C-STORE、C-ECHO

できること
モダリティから送られてきた検査を受け取り、ディスクに保存します。患者 / 検査 / シリーズ別に整理することもできます。すべての転送構文を受け付け、圧縮されたオブジェクトはそのまま保存します。トランスコードは行わないため、入ってくる途中で何かが変えられることはありません。
オンにする場面
ここへ画像を送信するものがある場合です。モダリティ、他のPACS、ワークステーションなど。これが中心となるサービスです。
オフのままでよい場面
このPCが、他の何かがフォルダに置いたものを転送するだけの場合です。
検査の履歴タブ、📥受信の表示。絞り込み欄とモダリティのチップの下に、検査ごとに患者、ID、日付、アクセッション番号、説明、モダリティ、画像数の行が並び、その下にシリーズが一覧され、行ごとに送信ボタンと、残りの操作を開く「⋯」ボタンが付いています。
届いたものが新しい順に、各検査を構成するシリーズとともに並びます。絞り込み欄とモダリティのチップで一覧を絞れます。行に出ている操作は送信だけで、残りは⋯のメニューにあります。PDF/画像を追加、タグ編集(エディタのアドレスを設定したとき)、パスをコピー、フォルダを開く(ダッシュボードをそのPC自体で開いているときだけ)、削除です。パスは本物です。整理されているのは実際のフォルダツリーであり、誰かがエクスポートしなければならないデータベースではありません。一覧の上の切り替えは、検査がたどる順に三つあります。受信、送信フォルダー(送信フォルダーの中身と、検査ごとの各宛先への送信状況)、そして送信済み / アーカイブです。最後のものは、自動送信が送信後に検査を移動する設定のときだけ埋まります。既定の「残す」では、送信した検査は送信フォルダーに残ります。

自動送信 — Storage SCUとルーティングルール

外向きクライアント · C-STORE

できること
フォルダを監視し、新しいファイルを該当するすべての送信先へ転送します。ファイルが送られるのは安定している(2回のスキャンでサイズが変わらない)と判断されてからなので、書き込み途中のファイルが転送されることはありません。進捗は送信先ごとに管理され、有効なすべての送信先が受け取って初めて完了として数えられ、失敗したホストは次のスキャンで再試行されます。成功したら、元のファイルはそのまま残す・移動する・削除するのいずれかを選べます。
ルール
ルーティングを有効にすると、ルールがモダリティ、発信側 AE title、ステーション、患者ID、検査説明で送信先を選びます(* と ? のワイルドカードは大文字小文字を区別しません)。例: ER_* からのCTを匿名化して教育用アーカイブへ。
保証すること
検査がどこにも送られずに終わることは絶対にありません。ルーティングがオフ、一致するルールがない、ヘッダが読めない、もう存在しない送信先を名指ししているルール — そのどれもが、有効なすべての送信先へ送るという動作に戻ります。送りすぎは運用者を煩わせますが、送り足りなければ画像が失われます。
唯一の例外
ある送信先について匿名化を要求しているルールがあり、その除去を実際には実行できない場合、その送信先は保留されます。識別可能なまま送るのではなく、そこへは一切送信しません。除去を止める原因は、プロファイルがoffであること、またはプロファイルはオンでも現在の設定から匿名化処理を構築できないことで、対処はそれぞれ異なります。どちらの場合も失われるものはありません。下の転送時の匿名化を参照してください。
オンにする場面
このPCが画像を先へ届ける必要がある場合です。中央のPACSへ、読影用のワークステーションへ、あるいは教育用アーカイブへのコピーとして。
設定一式の送信先タブ。Main archive、Reading room、Teaching archiveの行が並び、それぞれにホスト、ポート、AE title、TLS・RISなし・フェイルオーバーのチェックボックス、Echoボタンがあります。
送信先はここで名前を付け、他のあらゆる場所ではその名前で参照されるので、ノードの名前を変えても、それを指しているルールが迷子になりません。Echoは、実際の画像をそこに託す前にアソシエーションが成立するかを確かめます。
設定一式のルーティングタブ。「ルールで振り分ける」のチェックボックスがあり、その下に番号の付いたルールが並びます。各ルールにはモダリティ、発信側AE、ステーション、患者ID、検査説明の照合欄と、送信先のチェックボックスが並ぶ「送信先」の行、そして「匿名化」と「ここで停止」があります。
ルールは上から順に読まれ、空欄はすべてを意味します。ルールは、すべて埋めるフォームではなくフィルタです。順序が効くので、各ルールには並べ替えるための矢印が付いています。
検査の滞留タブ。対応が必要なファイルが8件あることを知らせるバナーの下に、「自動で再試行中」と「保留中 — 何も送信していません」が並びます。前者にはTeaching archiveの行があり、CRの検査の待機中のインスタンス2件、赤字の直近のエラー、「今すぐ再試行」ボタン、次の試行までのカウントダウンが出ています。後者には「プロファイルがオフ」と付いたTeaching archiveの行があり、患者とアクセッション番号の下にまとめられたCTの検査の待機中のインスタンス6件、それらを解放する設定変更、「匿名化の設定」と「それを要求しているルール」のボタン、そして「別の送信先へ送る…」と「キューから外す」があります。
ここに並ぶ上下は別々の問題で、タブはそれらを分けたままにします。上は落ちているノードで、これは自然に解消します。下は匿名化の保留で、これをタイマーが解除することは絶対にありません。だから各行には、それを解除する設定変更が書き出され、その場所へ行って実行するためのボタンが添えられています。行は検査ごとにまとめられるので、ファイル名だけでなく、誰の画像が待っているのかが分かります。再試行できる送信先がありませんというセクションは、そこに入れるものがあるときだけ描かれます。有効な送信先ではなくなった名前に振り分けられたファイルです。接続できるノードが残っていないため、それらを再試行するものはありません。その送信先を復旧させるか、別の送信先へ送る…で別の送信先へ送るか、キューから外すで失われることを受け入れてください。それまで、それらはアーカイブも削除もされずに送信フォルダに残ります。

転送時の匿名化

PS3.15 Annex Eのプロファイル · 出ていくコピーにのみ適用

できること
送信されるオブジェクトにBasic Application Level Confidentiality Profileを適用し、どの保持オプションを使ったかを(0012,0064)に明記するので、受け取った側は何が残されたかを確認できます。アーカイブされた原本が書き換えられることは絶対にありません。この非対称こそが要点です。
プロファイル
basicは日付(そのまま、またはシフト)、患者の身体的特性、機器の識別情報、施設の識別情報を保持します。strictは機器と施設の識別情報を落とし、保持を指定していてもプライベートタグを削除します。
オンにする場面
画像が臨床環境の外へ出る場合です。教育、研究、ベンダー、外部へのセカンドオピニオンなど。
オフにするとき
そもそも除去を行うかどうかを決めるのはプロファイルで、どの送信先が除去済みのコピーを受け取るかを決めるのはルールの匿名化のチェックです。ルールがまだ匿名化を要求しているのにプロファイルをoffにすると、Carinoはその送信先を保留し、匿名化されたものしか受け取らないと伝えられているノードへ識別可能なコピーを送ることはしません。

保留されているのであって、送信されたのではありません — そして検査が動かなくなったときの答えはこれです。ルールが匿名化を指定している送信先は、その除去を実際に実行できない限り、何も受け取りません。配信は先送りされますが、識別情報は開示されません。ディスクで待っている検査は設定を直せば解放されますが、外部のノードに届いてしまった氏名は、どんな設定変更でも取り戻せません。

除去が起きない理由には種類があり、直し方は同じではありません。どの保留にもそのどちらであるかが記録され、検査の滞留タブは、推測ではなく記録された原因に対応する対処を表示します。

  • プロファイルがoffなのに、ルールがまだ除去を要求している場合です。プロファイルをbasicかstrictにすれば、次の自動送信で解放されます。
  • プロファイルはオンでも、現在の設定から匿名化処理を構築できなかった場合です。この場合も除去に使えるものがありません。それを止めた失敗はアクティビティのログタブの送信のチャンネルに出ているので、そちらを直してください。プロファイルをオフにしてもこちらは解放されません。何も解放されず、除去を止めている側が入れ替わるだけです。

ルールの匿名化のチェックを外せばどちらの保留も解放されますが、検査は識別可能なまま解放されます。それは、この保留が防ぐために存在している、まさにその結果です。その送信先が本当にもう匿名化されたデータを受け取る対象ではない場合にのみ、この変更を行ってください。

どの変更が正しいにせよ、それは保留を報告している行からワンクリックです。保留された各行には、設定一式の設定タブを開く匿名化の設定ボタンと、ルーティングタブを開くそれを要求しているルールボタンが付いています。どちらもタブを開くだけで、除去を要求しているルールまでスクロールすることも、それを強調表示することもありません。それでも、問題を示している行が、それを直す両方の場所への入口になっているので、それぞれをサイドバーの行から探していたころより、直すまでの手数は少なくなっています。

失われるものはなく、黙って起きることもありません。検査は送信フォルダに残り、アーカイブも削除もされません。同じ検査の他の送信先には引き続き届きます。アクティビティのログタブは、検査、送らなかった送信先、その原因を挙げたエラーを出します。設定タブの匿名化のフィールドセットもそれを示し、検査の横の⚠バッジがそれらのファイルを数えます。そしてそのバッジ自体がボタンで、滞留タブを開きます。保留が独りでに解除されることはありません。時間切れを進めるタイマーもなければ、再試行するものもありません。

ピクセルはきれいにしません。画像に焼き込まれた患者情報 — 超音波やSecondary Captureがフレームの中に印字するバナー — は、ここのどのプロファイルを使っても残ります。そしてそれこそが、「匿名化済み」のデータが氏名を付けたまま病院の外へ出ていく、最も多い経路です。Clean Pixel Dataのオプション(113101)は、実際に行っていないので意図的に宣言していません。この漏えいをプログラムの内部から検出することはできません。画像は人が見る必要があります。構造化レポート(Structured Report)の中の記述文も同様に読み取りません。

索引

sqlite · index.db

できること
保存したファイルごとに1行を保持し、そこから患者、検査、シリーズの答えを導き出すので、検査の要約が、それがまとめているインスタンスとずれることは決してありません。クエリ/取得(Query/Retrieve)とDICOMwebの裏側にある問い合わせ層です。
オンにする場面
クエリ/取得やDICOMwebを使う場合です。どちらもこれだけに依存しています。
保証すること
索引はキャッシュであり、正ではありません。失っても再スキャンで済み、画像が失われることはありません。

クエリ/取得 — C-FIND、C-MOVE、C-GET

ポート11115 · Patient RootとStudy Root

できること
他のシステムから「この患者の検査は何がありますか」と尋ね、続いて「こちらへ / あのワークステーションへ送ってください」と頼めるようにします。古い機器が実際に話せるのは、PACSのこちら側です。2009年の超音波装置やCRリーダーがDICOMwebを話すことは決してありませんが、DIMSEなら話せます。
オンにする場面
ワークステーションやモダリティがここから検査を取得する必要がある場合、あるいはプライマリPACSの障害の間、このPCが一時的なアーカイブとして働く場合です。
保証すること
C-MOVEがインスタンスの一覧を勝手に作ることはありません。必ず索引を通して解決します。索引は知っているのにディスクから読み出せなかったものは失敗したサブオペレーションとして数えられ、Failed SOP Instance UID Listに名前が載ります。黙って落とすことは絶対にありません。一致した数より少ない画像しか送っていないのに成功を報告するC-MOVEは、このソフトウェアが起こしうる最悪の失敗です。

DICOMweb — QIDO-RS、WADO-RS、STOW-RS

HTTP、ダッシュボードのポートの/dicom-web配下

できること
最近のビューア(OHIF、Weasisなど)が、DICOMのアソシエーションを交渉することなくHTTPで検索・取得・保存できるようにします。STOW-RSのアップロードはC-STOREと同じ保存処理を通るため、ビューアから投稿された検査は、モダリティが送ってきたものと区別がつきません。
オンにする場面
アーカイブに対してWebビューアを使いたい場合です。これらの経路もトークンが守っていることを忘れないでください。
CORS
cors_originsは完全一致で照合され(スキーム + ホスト + ポート)、既定では空なので、一覧にないオリジンへ何かが返されることはありません。パターン構文はありません。ただしリテラルの*は有効で、それは運用者が自分でそう入力したからです。それ以降はすべてのオリジンに応答が返り、運用者が開いたどのページからでも、このPCのアーカイブを読めてしまいます。ビューアを名指しで指定してください。
できないこと
/rendered、/thumbnail、bulkdataのURI、転送構文どうしのトランスコードは実装しておらず、406を返します。中途半端に動くビューアは、機能がないことより悪いです。作れないものを偽ることは絶対にありません。

Modality Worklist(MWL)

ポート11114 · ワークリストのC-FIND

できること
技師が患者情報を手入力しなくて済むよう、モダリティへオーダを提供します。各オーダには事前に生成されたStudy Instance UIDが入っていて、それが検査に焼き込まれるため、モダリティが返してくる検査はそのオーダと正確に突き合わせられます。オーダの対象装置のAEを設定すると特定のステーションだけに送られ、空欄ならすべてのステーションに表示されます。
オンにする場面
RISがモダリティに届いていない場合です。RISが止まっているから、その場所にそもそもRISがないから、あるいは実際のRISなしでRIS→PACSの流れを試すためです。

登録済みのモダリティ

ポートなし · 設定一式 → モダリティ

できること
この装置が面倒を見る検査室の一覧です。人が見て分かる名前、ワークリストが完全一致で照合するステーションのAE title、モダリティのコード、必要なら装置名。これは許可リストではありません。ここに登録がなくても画像は送れますし、登録があるから送れるようになるわけでもありません。得られるのは、オーダの宛先が手入力ではなく一覧から選ぶものになること、そしてワークリスト確認がAE titleを借りる先がこの一覧になることです。
オンにする場面
ワークリストを配信するとき、あるいはオーダを手入力するとき。ステーションが一つも登録されていない間は、オーダの宛先は手で打つしかなく、打ち間違いを捕まえる手立てがありません。
設定一式のモダリティタブ。登録済みの三つの検査室 — ER CT、Ultrasound 2、Portable CR — が並び、それぞれに名前、ステーションのAE title、モダリティのコード、装置名、有効のチェックボックス、「ワークリスト確認」ボタンがあり、その下に「モダリティを保存」ボタンがあります。
この装置が面倒を見る検査室です。オーダの宛先はこの一覧から選び、行のワークリスト確認は、その検査室に何を渡すかを病院の相手のRISに尋ねます。

ワークリスト確認 — 相手のRISに尋ねる

送信側クライアント · ワークリストのC-FIND · 設定一式 → モダリティ

できること
「なぜこの装置に自分のリストが出ないのか」を、装置に触らずに切り分けます。病院の本物のRIS(またはそのブローカ)のアドレスを設定で与え、モダリティの行のワークリスト確認を押すと、Carinoはそのモダリティ自身のAE titleを使い、そのモダリティが投げるはずの問いを相手のRISに投げ、返ってきたものをすべてアクティビティ → ワークリスト確認の結果に記録します。一度の実行で同じ問いを鍵を一つずつ外しながら五回投げます — ステーションと日付とモダリティ、次にモダリティなし、日付なし、ステーションなし。障害の位置を教えてくれるのは、答えどうしの差だからです。どれにも何も返らなければ相手のRISに予定が入っていないということですし、いちばん狭い問いだけが空で返るなら、間違っているのはステーションのAE titleです。
押す前に
先にそのモダリティをネットワークから外してください。この確認はそのモダリティとして接続します。一つのRISに対して同じAE titleで二つが応答する状態は、あとから人が行って解かなければならない混乱です。
できないこと
サービスでも、予定表でも、キューでもありません。ワークリスト確認の結果にあるのは他人が答えた内容の記録です。それらのオーダがこの装置自身のワークリストで配信されることはなく、この装置はどれも自分のものだと主張しません。クリアはその記録を空にするだけです。
アクティビティのワークリスト確認の結果タブ。US_ROOM_2としてpacs-peerの相手のRISに問い合わせた確認1回分が表示され、五つの問い — ステーション、日付、モダリティ、そして鍵を一つずつ外したもの — と、それぞれの答えが並んでいます。デモの相手のRISにはオーダがないため、答えはすべて「なし」です。右側に更新と消去のボタンがあります。
ワークリスト確認の1回分です。見出しには誰に、どのAE titleとして問い合わせたかが書かれ、各行は一つの問いと、その問いに残した鍵、そして返ってきた答えです。いちばん狭い問いから広い問いへと読んでください。狭い問いでは何も返らず、広い問いでオーダが返ってきたなら、外した鍵が間違っていた鍵です。ここではすべての問いの答えがなしです。これは鍵の誤りではなく、相手のRISにその部屋の予定が何もないという意味で、デモの相手にはオーダがありません。

緊急RIS — HL7オーダ

ポート2575 · MLLPで運ぶHL7のORM^O01

できること
MLLPでHL7オーダを受信します。上流が何も生きていないときには、ダッシュボードから手入力することもできます。検査がC-STOREで戻ってくるとアクセッション番号(なければ患者ID)でオーダと照合され、オーダは完了し、証跡のためにアーカイブされます。消されることは絶対にありません。
オンにする場面
RISが止まっているとき、または連携をテストしているときです。
保証すること
画像の配信がオーダとの照合を条件にすることは絶対にありません。一致するオーダのない検査も、保存され転送されます。オーダは手作業で突き合わせるために未処理のまま残るだけです。
RISオーダのパネル。「➕ 新規オーダ(手入力)」の開閉欄と、「🟢 未処理」「✓ 完了」のタブ、検索欄、着信音の切り替えがあり、RISから届いた三件の未処理オーダにはアクセッション番号、患者、モダリティ、依頼検査、HL7の送信元、登録した時刻が表示され、それぞれに「📎 PDF/画像を追加」ボタンが付いています。
HL7で届いたオーダと、上流が何も生きていないときに手で入力するための、上部の開閉欄です。対応する検査が届くとそれぞれ自分で完了し、消されるのではなくアーカイブされます。RISから来たオーダを取り消せるのはRISだけなので、取り消しは付いていません。新しいオーダが届くとオーダの行が点滅し、このPCでは音で知らせることもできます。この切り替えはPCごとで、記憶されます。眼の写真のオーダ(モダリティ OP)には眼の写真を追加もあります。エディタがそのオーダで開き、作成した写真はオーダの検査として戻り、オーダは完了になります。

TLSも認証情報もありません。セキュリティの節を参照してください。そのポートにソケットを開ける人は、誰でもオーダを注入できます。信頼できる臨床ネットワークとファイアウォールを用意するか、さもなければ使わないでください。

緊急フェイルオーバー

プライマリに指定した送信先のC-ECHO監視

できること
送信先をプライマリに指定して監視を有効化すると、Carinoが定期的にそこへC-ECHOを送り、転送の失敗も見ます。しきい値を超えて到達できないままなら、緊急RISを有効化するかどうかの確認が出ます。有効化すると、技師が検査を続けられるようローカルのワークリストを起動し、障害の間に受信した検査をすべて保留し、プライマリが戻ったら転送します。通常運用に戻すタイミングは運用者が決めます。
オンにする場面
このPCが、業務が依存しているPACSへのゲートウェイである場合です。本マニュアルの冒頭に「継続運用」という言葉があるのは、このためです。

通知 — WebhookとE-mail

送信のみ · HTTP Webhook、SMTP

できること
ダッシュボードを開いていない人に届かせます。画面上のバナーは転送を見ている人には十分で、これが存在する理由である場面には無力です。午前3時に主系が落ち、当直の放射線科医はどの画面の前にもいません。二つの経路は互いに独立していて、大元のスイッチが入るまでは両方とも動きません。
送るもの
フェイルオーバーの出来事だけです — 検知、切替、復旧。装置のほかのどの部分も通知を出しません。これは汎用の警報システムではありませんし、オンにしてもそうはなりません。
メールが届く相手
アドレスではなくプロファイルです。フェイルオーバーが通知先に指定していて、かつメールアドレスが入っている有効なプロファイルすべてに届きます。SMTPを設定してもどのプロファイルにもアドレスが無ければ、何も送られず、音も立てません。「メールはオンなのに誰も知らされていない」ときに最初に見るのはそこです。文面は役割によって変わり、落ちた送信先のアドレスは、送信先の表をすでに見てよい相手にしか含まれません。メールは建物の外へ出ていき、取り消せないからです。
絶対にしないこと
報告している当のものを遅らせたり壊したりすること。送信はすべてワーカースレッドに渡され、キューには上限があり — あふれれば古いものから捨て、捨てた回数を数えます。復旧後に届く「主系が落ちています」は沈黙より悪いからです — 失敗は例外ではなくカウンタとログ行になります。送信数、失敗数、破棄数、待ち数、最後のエラーはいずれも読めるので、「オンなのに一度も実際には届いていない」が、それに頼る障害の前に見えます。試すのは静かな午後であって、障害の最中ではありません。
設定する場所
ダッシュボードにはまだありません。config.jsonのnotifyセクションで設定します(WebhookのURL、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件)。MR検査の隣に届いたJPEGと、その下のPDFが見え、どちらにもその検査から患者名、患者ID、アクセッション番号、検査日、検査説明が入力済みで、「プレビュー」のリンク、「オーダに照合…」ボタン、「承認して送信」「破棄」のボタンがあります。三件目のプリント受信から取り込んだフィルムは、一覧のさらに下、画面の外にあります。
MR検査の隣で見つかったJPEGとPDFで、どちらにもその検査の情報があらかじめ入っています。DICOMでないものは、推測で変換されるのではなく、ここで待ちます。プリント受信で取り込んだフィルムもこのタブに届き、この一覧ではこの二件の下にあります。オーダに照合…で属する未処理のオーダを選ぶか、患者情報を入力して(患者IDと氏名は必須)、承認してください。検査の隣に届いた項目の患者を変えると、それがまだその検査に属するのか、新しい検査にするのかを尋ねられます。

Webダッシュボード

ポート8042 · HTTP、既定は127.0.0.1

できること
サービスごとの状態、送信先、オーダ、リアルタイムのアクティビティのログ、設定、そして同梱のDICOMエディタです。ダッシュボードでできることはすべてCLIにもあります。
画面の構成
サイドバーの行は概要、サービス、検査、オーダ、設定一式、アクティビティの六つで、--dev-peerを付けて起動したプロセスにだけ七つ目のテスト用ノードが加わります。同じファイルの山に対して投げる問いが検査のタブで、履歴、保留、滞留です。機器を立ち上げるときに決めるものが設定一式のタブで、送信先、ルーティング、設定、モダリティ、スタッフです。開くのは設定ではなく送信先です。そして記録がアクティビティのタブで、ログ、接続、監査、ワークリスト確認の結果です。接続は最近のDICOM接続を双方向に一覧し、失敗した理由と、どの機器で何を変えればよいかを示します。送信先の行の受付可能な種類ボタンは、ここに保存されている画像の種類をすべてその相手に尋ねます。ワークリスト確認の結果は、この装置があなたのモダリティの一つとして尋ねたときに相手のRISが答えた内容です。オーダは未処理と完了の独自の切り替えを持ちます。検査の行には、それ自体がボタンになっているカウンタ、📎保留と⚠滞留が付いていて、それぞれ自分のタブを開きます。つまり何かが滞留している間は滞留までワンクリック、何もないときは行、次にタブの二段階です。0という件数は自分を隠すからです。ずっと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、転送構文、Photometric Interpretation、ファイルのSHA-256が出ています。
同梱のエディタは、履歴タブの検査から、または単独で開けます。すべてブラウザの中で動作し — どこにも何もアップロードされません — 患者名は最初は隠された状態です。ヘッダを開く最も多い理由が、UIDを読むことだからです。
エディタの編集タブ。匿名化 (PS3.15)、オプション、架空の患者(テストデータ)、元に戻す・やり直し、+タグ、JSON、CSV、印刷、このファイル、すべてダウンロードのツールバー、読み込んだシリーズの帯、患者・検査・シリーズ・画像・機器・UID・プライベートタグの絞り込みチップ、属性ごとに1行、グループ番号と要素番号、説明、VR、編集できる値が並ぶタグ表、そして右三分の一には画像編集パネル(回転・反転・階調反転・焼き込まれた文字の塗りつぶし。いずれも保存画素に書き込まれます)と、その下にプレビュー、折りたたまれたウィンドウ/レベルの値、ファイルのドロップ領域があります。
タグ表は、検査を再送する前にヘッダを直す場所です。誤ったアクセッション番号や打ち間違えた患者IDを、すでに次の作業へ移ってしまったモダリティではなく、ゲートウェイで直します。

テスト用ノード — 使い捨ての第二アーカイブ

ループバックのみ · pacs serve --dev-peer · 配布ビルドには決して入りません

できること
同じプロセスの中に、まるごと第二のアーカイブを作ります。127.0.0.1のみで待ち受け、自分のAE title、自分の受信ポートとQuery/Retrieveポート、一時フォルダの中の自分の保存領域を持ち、この装置側にはそこを指す送信先が最初から用意されます。転送、ルーティングルール、転送時の匿名化、C-MOVEが思ったとおりに動くかを確かめるあいだ、検査が実際に行く先になります。併せて、無効状態の「ブラックホール」送信先も作られます。こちらを有効にすると送信はわざと失敗し、滞留タブを好きなときに埋めて、本番で起きる前に滞留とはどう見えるものかを確かめられます。
開き方
pacs serve --dev-peerでエンジンを起動し(ダッシュボードのURLの下の行にその旨が出ます)、サイドバーの🧪 テスト用ノードを選んでノードを作成を押します。あとはパネルが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.