HL7 v2.x で複数装置と接続する方法 (設計と実装の考察)

今回は少し趣向を変えて、「テキストベースのレガシーな通信を、複数クライアントでどう安全に捌くか」という話をしたいと思います。扱う題材は「HL7 (Health Level 7) v2.x」という医療分野の通信プロトコルですが、本質的な悩みは Web や API サーバーとあまり変わりません。
この記事では
- 同時接続をどう管理するか
- 1 クライアントの遅延が全体に波及しないか
- 再送や重複をどう扱うか
などの問題について、設計と実装の観点から考察します。TCP/IP 接続でサーバーとして動作させる仕組みについて知りたい方にも参考になるかもしれません。
※ 本記事の前半は HL7 の簡単な技術解説、後半は接続方式・通信設計の考察という構成になっています。
目次
HL7 とは
まず初めに HL7 について簡単にご説明します。
HL7 (Health Level 7) とは、医療システム同士を接続するためのプロトコルで、「電子カルテ」、「検査装置」、「会計システム」など、IT 化されている多くの医療施設では、何らかの形で HL7 が使用されていると思われます。
HL7 は「HL7 International」という非営利団体によって策定されています。世界中の医療 IT ベンダー・病院・検査会社が関わっています。日本では「HL7.jp (HL7 International の日本支部)」、「JAHIS (日本の医療 IT ベンダーの業界団体)」などによってガイドラインが策定されています。
HL7 には様々なバージョンがありますが、代表的なものは以下のようになっています。
- HL7 v2.x ・・・レガシーなテキストベースのプロトコルです。現在最も使用されています。
- HL7 v3 ・・・ XML 形式のプロトコルです。実装・運用コストが高く、結果として普及は限定的だったようです。
- HL7 FHIR (ファイア) ・・・ REST API タイプのプロトコルです。今後普及していくと思われます。新規の開発で使用されます。
今回の記事では、レガシーな技術ですが今後もしばらく (最低でも 10 年以上は) 使用されると思われる、HL7 v2.x を使用して複数の装置を接続する方法についてご説明します。(以降は HL7 v2.x プロトコルを単に HL7 と記述しています。)
HL7 プロトコルについて
このセクションでは HL7 の構造や通信手順について、簡単にご説明します。
一般的な HL7 プロトコルは単なる TCP/IP を使用したソケット通信なので、特に使用するポート番号などは決まっていません。任意のポート番号を使用できます。RS232C を使用したシリアル通信などでも実装可能です。
HL7 の各レコードは以下のような構造になっています
MSH|^~\&||GA0000||MA0000|199705221605||QBP^Q11^QBP_Q11|19970522GA40|T|2.5.1|||NE|AL|||||Z34^CDCPHINVS|<CR>
QPD|Z34^Request Immunization History^CDCPHINVS|19970522GA05|25^^^STATE_IIS^MR|FLOYD^FRANK^R^^^^L|MALLARD^F|20030123|M|8444 N. 90th Street^Suite 100^Scottsdale^AZ^85258^USA^L|^PRN^PH^^^480^7458554<CR>
RCP|I|20^RD|R<CR>レコードの特徴として
- 各レコードは先頭に 3 文字のレコードタイプ文字列を持つ
- レコード内の各項目は縦棒「|」によって区切られている
- レコードの終端は制御文字「CR」で終了する
などの特徴があります。
さらにこれらのレコードは、MLLP (Minimum Lower Layer Protocol) と呼ばれるプロトコルでカプセル化して送受信されます。具体的には先頭が VT (0x0B)、終端が FS (0x1C) + CR (0x0D) という制御文字で囲まれた内容が 1 つの HL7 メッセージ単位になっています。
<VT>
MSH|^~\&|...<CR>
PID|...<CR>
OBR|...<CR>
OBX|...<CR>
<FS><CR>各 HL7 メッセージは MSH (メッセージヘッダー) レコードで始まっていて、MSH レコードは以下のような情報を持っています。
- フィールド区切り文字 (MSH-1)
- エンコード文字 (MSH-2)
- メッセージ種別 (MSH-9)
- メッセージ ID (MSH-10)
- バージョン (MSH-12)
この中で 9 番目の項目 (メッセージ種別) に 「QBP^Q11」などと書き込むことで HL7 のメッセージタイプを指定します。
MSH|^~\&||GA0000||MA0000|199705221605||QBP^Q11^QBP_Q11|19970522GA40|T|2.5.1|||NE|AL|||||Z34^CDCPHINVS|余談ですが、MSH レコードだけは各項目のインデックス番号が 1 つずれています。細かい話になりますが、フィールド区切り文字 (MSH-1) は上の例では「^~\&」ではなくて「|」になります。これは MSH の「次の文字」がフィールド区切り文字の定義になっているためです。本当は「MSH|'|'|^~\&|...」とでも書きたいところですが文字の定義なので「MSH|^~\&|...」となっています。(「MSH:」などと書いても仕様上は誤りではありませんが、ほぼ外部と通信できないと思われます。事実上区切り文字は「|」固定になっています。)
さて少し横道にそれましたが、シチュエーション (トランザクション) 毎にどのメッセージを使用して、どのメッセージで応答するかなど、ある程度ガイドラインで規定されているようです。医療情報通信のデファクトスタンダードと思われる、「IHE (Integrating the Healthcare Enterprise)」と呼ばれる国際プロジェクトによると検体検査 (IHE LAB) 分野では以下の通信方法を実装することが推奨されているようです。
| トランザクション | 区分 |
|---|---|
| オーダー受信 (OML / ORL) | 必須 |
| 結果送信 (OUL / ACK) | 必須 |
| オーダー問い合わせ (QBP / RSP など) | オプション |
| 再送・訂正シナリオ | 条件付き必須 |
上記のトランザクションの内、「オーダー受信」と「結果送信」は一般的な REST API 通信と同じように同期的に通信できます。海外の装置に多いですが「オーダー問い合わせ」機能が実装されていると非同期通信になります。複数台の装置を接続するだけなら非同期通信でも問題は起きませんが、複数台の装置の接続を「単一の接続にまとめてホスト (LIS) に中継する」などの機能を実装すると問題が起きることがあります。本記事ではこのような問題に焦点を当てて解説していきます。
複数台の装置と接続するには
複数の装置と接続するには、以下のようにアプリケーション側をサーバーとして動作させるのが一般的です。(ポート番号は一例です。特に使用するポートは決まっていません。)
アプリケーション側をクライアントとして接続する方法もありますが、その場合は
- それぞれの装置の IP アドレス情報を管理する必要がある
- ファイアウォールを使用した場合に接続できなくなる
などの問題を解決する必要があります。

複数台の装置と接続する場合は、それぞれのソケットなどの接続情報を、IP アドレスをキーとしたテーブルなどに保持しておく必要があります。装置ごとに専用のソケットを持っているので、メッセージが非同期でも通信方式に由来する問題は起きません。
| キー | 値 |
|---|---|
| 172.16.0.2 | 装置 1 用ソケット |
| 172.16.0.3 | 装置 2 用ソケット |
| 172.16.0.4 | 装置 3 用ソケット |
・ファイアウォールがある場合
ファイアウォールがある場合も基本的な考え方は同じですが、接続元の IP アドレスが同じになってしまいます。(ポート番号は一例です。特に使用するポートは決まっていません。)

このため接続元の IP アドレスだけでは区別できなくなるので、キーにポート番号も含める必要があります。
| キー | 値 |
|---|---|
| 10.0.0.2:9001 | 装置 1 用ソケット |
| 10.0.0.2:9002 | 装置 2 用ソケット |
| 10.0.0.2:9003 | 装置 3 用ソケット |
・どのように処理を行うか
実際の処理方法ですが、それぞれの通信を直列的に処理するといずれかの通信に時間がかかった場合に、全体の処理に影響が出てしまいます。そこで以下のように各ソケットをラップするクラス (Client) を作成してマップなどで保持します。(以下の図は「UML」という設計言語の「クラス図」と呼ばれる図で表現しています。この図は「Client」は 1 つの「Socket」を持ち、「Server」は 0 個以上の「Client」を持つという意味になっています。)

装置とのやり取りはラップクラス (Client) で行い、サーバーへは内部形式に変換されたデータを非同期で送信します。(以下の図は UML の「シーケンス図」と呼ばれる図で表現しています。)

このような構成にしておくと、装置が何台増えても見かけ上各ラップクラス (Client) が装置と並列的に対話するため、装置を待たせずに処理を実行できます。
・実運用での問題
一般的な運用では再接続・再送などの処理についても考慮する必要があります。特にファイアウォールを使用している場合などは、接続タイムアウトへの対応が必須となります。
実運用時に実装や検討が必要と思われる機能は、以下のようなものがあります。
- 切断時に接続情報をテーブルから削除
- 再接続時に古い接続情報を削除
再接続処理と受信処理が同時に走る場合、古い接続を参照したまま処理が進んでしまうケースには注意が必要です。 - 接続状態の維持機能 (キープアライブ)
実際にダミーのオーダーなどを送信できればベストですが、不可の場合は OS で実装されているキープアライブ機能を使用します。 (一般的に OS レベルのキープアライブは検知までに時間がかかるので、可能であれば検知時間を短く設定しておきます。) - 応答がない場合のリトライ機能
ただし送信時にソケットエラーが返る場合は、すでに切断されているもしくは通信不能状態になっているので、テーブルから接続情報を削除します。
などの機能が必要となります。
次のセクションからは、複数の装置との接続をホスト (LIS) に中継する方法と問題点について検討します。
複数台の装置との接続をホスト (LIS) に中継するには
・ホストにそのまま中継する場合
ホスト (LIS) 側が複数の接続を管理できる場合は、この方法が最も安定します。各装置とホスト (LIS) の間はそれぞれ独立したソケットで接続されているので、通信方法 (同期・非同期) に限らず他の装置との接続の影響を受けません。

アプリケーションが冗長な気もしますが、このような構成にすると
- ホスト (LIS) と装置の文字コードの変換
ホスト側が半角カタカナなどを使用している場合に、文字コードを変換して装置に送信 - 異なる種類の装置を接続した場合に、HL7 実装の差異を吸収して統一的なフォーマットでホストと通信
- 将来的に HL7 FHIR 方式に変更された場合に、ゲートウェイとして使用
などの用途として使用できます。
・装置の接続を単一の接続にまとめてホスト (LIS) に中継する場合
いよいよ本題です。最も需要がありますが、実装が複雑になる接続方法です。

まずは同期的な通信の場合は以下のようなシーケンスになります。
同期的に処理すると、それぞれの送信データの応答を受信するまで他の処理がブロックされるため、装置の台数が増えるほど、結果を送信してから承認メッセージを受信するまでの時間が伸びていきます。またどこかの通信で再送や受信遅れなどが発生すると、他の通信にも影響が伝搬します。

そこで同期処理を非同期処理に分解します。同期処理と異なり他の通信の終了を待つ必要が無くなるので、装置の台数が増えても応答を受け取るまでの時間が伸びません。

ただし、このように非同期処理するには条件があります。それは各 HL7 フレーム間で「紐づけできる相関を持っている」必要があります。具体的には測定結果 (ORU) メッセージのメッセージ ID (MSH-10) の値が承認メッセージ (ACK) にある MSA レコードの 2 番目の項目 (MSA-2) に代入されて返ってくるので、この値を比較して対応するメッセージを調べることができます。
MSH|^~\&||GA0000||MA0000|199705221605||ORU^R01|a2e985ed-766a-4b85-8992-e83465c7cf8a|T|2.5.1|||NE|AL|||||Z34^CDCPHINVS|MSH|^~\&||MA0000||GA0000|199705221605||ACK^R22|59921e03-a0d0-46cf-9662-b3ff8d7a8aca|P|2.5.1||||||ASCII<CR>
MSA|AA|a2e985ed-766a-4b85-8992-e83465c7cf8a<CR>・装置が「オーダー問い合わせ」オプションを実装している場合
さて前半の HL7 のご説明でも触れましたが、「オーダー問い合わせ」オプションが実装されている装置を中継する場合はさらに複雑な制御になります。
このオプションのシーケンスは以下のようになっています。まず装置側がオーダーの問い合わせ (QBP) を行います。ホスト (LIS) は受け付けたことを示すメッセージ (RSP) を返します。この段階ではまだオーダー内容は分かりません。
次にホスト (LIS) 側から具体的なオーダー内容 (OML) が送信されます。装置側はオーダー受領 (ORL) メッセージをホストに送信してやり取りが終了します。

このように最初の問い合わせメッセージ (QBP) とオーダーダウンロードメッセージ (OML) は非同期通信になります。ホストに複数接続で中継する場合は何も考慮する必要はありませんが、単一接続の場合は「問い合わせ」部分と「オーダーダウンロード」部分の相関を見つける必要があります。一般的にオーダー問い合わせメッセージには「検体 ID」などが含まれていると思われますので、この ID をキーにしてオーダーメッセージと紐づけることになります。

上記の例ではまだそれぞれのトランザクション部分が同期的になっているので、接続台数が増えると装置が待たされてしまう可能性があります。すべて非同期にすると以下のようになります。どのメッセージがどのような順番で送信されてきても、各通信の前後関係さえあっていれば問題なく通信可能です。

ただし、このように完全に非同期で中継できるのは、それぞれの相関で唯一の HL7 メッセージが特定できる場合に限られます。例えば同一の検体 ID が複数存在する場合や、オーダーする順番に意味があるような運用の場合は非同期的に処理できません。(どのような順番で送信されてくるか保証できないため)
そのような運用の場合は、「オーダー問い合わせ」のような非同期のやり取りの部分も含めて、すべて同期的に中継する必要があります。(同期処理で運用を行う場合は、接続台数が増えると待ち時間も増えるため、装置の受信タイムアウトなども考慮する必要があります。)
まとめ
本記事では、HL7 v2.x を用いて複数の装置とホストを接続する際に、接続方式や通信設計の観点からどのような問題が起きやすいか、またそれをどのように考えるべきかについて整理してきました。
なお、これらの処理は「インターフェイスエンジン」と呼ばれる専用のアプリケーションを使用することで実現できる場合もあります。インターフェイスエンジンには OSS のものから商用の有償製品まであり、商用製品では年間数十万円から数百万円程度のコストがかかることが一般的なようです。
プロジェクトによっては、OSS の使用が禁止されていたり、そもそもサードパーティ製ソフトウェアのインストール自体が許可されていない場合もあります。そのような場合には、接続処理を自前で実装する必要があります。また、インターフェイスエンジンを使用する場合であっても、複雑な中継や非同期処理を行うためには、結局は個別のルールやロジックを自分で記述する必要があります。インターフェイスエンジンの内部でも、本記事で整理したような接続管理や同期・非同期の制御が行われていると考えられます。本記事が、HL7 を用いた接続方式や通信設計を検討する際の一つの参考になれば幸いです。
ご質問やご相談などがございましたら、お問い合わせフォームよりお気軽にご連絡ください。
※ 本記事は一般的な設計上の考察をまとめたものであり、特定のシステムや導入事例を示すものではありません。








