Минимум необходимой программисту информации о HL7

Дата публикации: 2015-08-13

Введение

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

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

Одним из сервисов DICOM является сервис Modality Worklist, назначение которого — предоставление информации о пациентах медицинскому оборудованию (чтобы медицинский работник мог, например, выбрать пациента из заранее подготовленного списка вместо того, чтобы вводить его данные вручную).

Одним из популярных открытых средств для предоставления оборудованию этих списков является сервер dcm4che, в дальнейшем я буду рассматривать его.

Так вот, оказывается, что протокол DICOM (во всяком случае, на том уровне, на котором я успел его изучить) не предоставляет средств для редактирования modality worklist. Предполагается, что PACS-сервер (то есть упомянутый dcm4che) будет получать информацию о пациентах по каким-то другим каналам.

Оставим в стороне рассуждения о том, какие это каналы должны быть в идеальном случае и как же оно там на самом деле спроектировано, и перейдём сразу к практической части. Чтобы отправлять списки пациентов на PACS-сервер, нужно использовать формат HL7, поддержка которого уже встроена в сервер.

Формат HL7

Итак, HL7 версии 2 — это стандарт кодирования медицинской информации. Сразу перейдём к примеру HL7-документа:

MSH|^~\&|MPA|SYSTEMA|IMPAX|MDRADAMB|200802210826||ORM^O01|MSG242081|P|2.3|
PID|||0195313690^^^mpa||TEST^PATIENT|TEST|19500131|M|||Johann Reschstr.24^^Mannswoerth^^2320^AT||||||||2601||||||||Arb.|
PV1||O|||||||||||||||||0855025211^^^^0855025211|000003||||||||||||||||||||||||20080220|
ORC|NW|1552647.1|||||^^^20080221082647.1400^^3||20080220233830|MDIM-4A||A225021^Dietl^Christoph^^^OA Dr.|MDIM-4A_MDIM|
OBR||1552647.1||ROE_CP^Cor pulmo^mpa^ROE_CP^CP^mpa||||||||||||A225021^Dietl^Christoph^^^OA Dr.|||1552647.1|1552647.1||||CR|||^^^^20080221^3|
ZDS|1.2.4.0.13.1.432252867.1552647.1^100^Application^DICOM|ScheduledAET|Scheduled Station name

Да, это не шутка, именно так и выглядят пакеты в HL7 (обратите внимание на переносы строк — они важны для протокола).

Существует также XML-образный стандарт HL7 версии 3, но в данном посте речь пойдёт не о нём.

Весь HL7-документ состоит из сегментов, которые разделяются символом возврата каретки \r (который в некоторых нормативных документах обозначают также <cr>). В начале каждого сегмента приводится его наименование (обычно состоящее из трёх букв). Описание каждого сегмента в отдельности можно найти в документации.

Сегмент состоит из полей, которые разделяются, как правило, символом | (но это может быть любой другой символ, определённый заголовком пакета). Поле может содержать один или несколько компонентов, разделённых символом ^.

Первый сегмент MSH является обязательным — это заголовок пакета. Поскольку он задаёт структуру пакета, то он является несколько особенным. В частности, четвёртый символ сегмента — это разделитель полей, который будет использован во всём теле документа. А его второе поле (первым полем обычно считается поле с именем сегмента) задаёт несколько других важных разделителей, которые могут быть использованы в документе; в частности — разделитель компонентов ^. Стандартное значение разделителя полей — это |; стандартное значение второго поля сегмента MSH — это ^~\&.

Содержимое пустых полей опускается (просто ставится пара разделителей); если группа пустых полей располагается в конце сегмента — то можно даже разделители не ставить.

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

Поля, которые начинаются с буквы "Z", являются зависимыми от реализации. В частности, dcm4che предлагает кодировать в поле ZDS имена устройств, которым предназначается HL7-сообщение, и некоторую служебную информацию.

Протокол MLP

Замечательно, теперь мы умеем кодировать нужную нам информацию в формате HL7v2 (или, во всяком случае, знаем, где искать дальнейшую информацию). Но как нам передать эту информацию на сервер?

Для обработки информации в формате HL7 предлагается простейший протокол MLP. Каждое сообщение протокола представляет собой пакет в формате HL7, перед которым поставлен байт \x0b, а после которого — два байта \x1c\x0d. Каждую строку в HL7-документе (в том числе последнюю) следует заканчивать символом \r, причём только одним.

Подключение к MLP-серверу осуществляется через обычный сокет (хотя встречаются реализации на SSL-сокетах; возможны другие каналы передачи информации, но мне такие пока не встречались).

На каждое сообщение клиента сервер отвечает своим сообщением в таком же формате с теми же разделителями. Тело сообщения также представлено в формате HL7; интересующая клиента информация хранится в сегменте MSA (документация). Статус обработки сообщения, как правило, хранится в поле Acknowledgment Code; какое-то текстовое пояснение (которое бывает полезно в случае ошибок) — в поле Text Message.

В дистрибутиве dcm4che сервер MLP активен по умолчанию и разворачивается на порту 2575. Возможно настроить сервер на работу с юникодом; для этого следует поменять кодировку на "UTF-8" в JMX-консоли, а также, возможно, немного подтюнить XSL-таблицы, в соответствии с которыми сервер преобразует информацию из HL7 в DICOM (т.к. в поставке по умолчанию они настроены на однобайтовую кодировку). Если интересны подробности — прошу, спрашивайте в комментариях.

А, ну да. Казалось бы, причём тут XSLT, если HL7 — это текстовый формат? Поверьте, вы не хотите этого знать.