1
1

More than 1 year has passed since last update.

HL7とopenEHRのObservation

Last updated at Posted at 2022-06-25

HL7とopenEHRのObservation

はじめに

HL7openEHRはよく比較される1。相互に影響を受けているところもあり,共通点も多いのであるが,最も違いが大きいのは Observation だろう。openEHR CKM(Clinical Knowledge Manager)にはObservationから派生した470ものarchetypeが登録されているが,HL7 FHIRでは公式にはObservationリソースの下に派生したリソースはない。これはFHIRだけの特徴ではなく,HL7 V2から全ての観察項目がOBXレコードとして扱われていた伝統的なものである。バイタルサインや検査、問診内容や病歴など全ての「観察」をObservationで扱うことにはメリットデメリットがある。

  • メリット
    • スキーマの変更なしでほぼすべての観察データを扱うことができる。
    • データの多くは項目名と値の組み合わせであるため、柔軟に扱う項目を増やすことができる。
    • スキーマをコンパクトにまとめることができる。
  • デメリット
    • 観察項目の特性を反映させることが難しい。例えば,血圧のように拡張期と収縮期の2つの値をセットで扱うためには別途制約を設ける必要がある。
    • 観察項目によって値が数値であったり文字列であったりとデータ型が一定ではない。
    • データを統合して扱うためには用語集の整備が不可欠であり、場合によっては複雑なマッピング操作が必要となる。

このため,用語集を指定したり,値のデータ型を指定するなど適切な制約を設けるように実装ガイドラインが整備されていることが一般的となっている。HL7 V3以降ではObservation以下に観察項目の特性に合わせたデータモデルを組み込むことができるようになり,HL7 FHIRではComponentとして複数のデータをまとめることができるようになった。日本で利用されている
アメリカで提示されているガイドラインUS Coreの例を下記に示す。

いずれも既存のObsertionに新しい制約を設けている。血圧や脈拍、心拍数などのバイタルサインは基本的データであるため、計測方法や計測時の状態についての関連情報も多く、それらをまとめて扱うことができるようにProfileが設計されている。

HL7とopenEHRのマッピング

上記のようにHL7のObservationは扱われるデータ項目が多岐にわたるため,openEHRにマッピングする場合にはどのようなデータなのかを内容を見て判定し,場合分けをする必要がある。openEHR -> HL7方向には比較的楽にデータを送信することができるため,openEHRで規定されているObservationクラスについての制約をHL7 FHIRのProfileを導出するツールが開発されている。

 残念ながら万能とまではいかないが、このツールでProfileの設計をかなり省力化することができる。LOINCなどの用語集を使ってFHIRの項目とArhcetypeのエレメントの両方にコードを割り振っておくとマッピングが楽になる。主要なバイタルサインに相当するLOINCのコードを以下に供覧する。

表1 バイタルサインテーブル

項目名 LOINC code LOINC name 単位
バイタルサイン計測 29274-8 Vital signs measurements
心拍数 8867-4 Heart rate /min
脈拍 8893-0 Heart rate Peripheral artery by palpation /min
呼吸数 9279-1 Respiratory rate /min
収縮期動脈血圧(侵襲的測定) 76215-3 Invasive Systolic blood pressure mm[Hg]
収縮期動脈血圧(侵襲的測定) 76215-3 Invasive Systolic blood pressure mm[Hg]
平均動脈血圧(侵襲的測定) 76214-6 Invasive Mean blood pressure mm[Hg]
拡張期動脈血圧(侵襲的測定) 76213-8 Invasive Diastolic blood pressure mm[Hg]
収縮期動脈血圧(非侵襲的測定) 76534-7 Systolic blood pressure by Noninvasive mm[Hg]
平均動脈血圧(非侵襲的測定) 76536-2 Mean blood pressure by Noninvasive mm[Hg]
拡張期動脈血圧(非侵襲的測定) 76535-4 Diastolic blood pressure by Noninvasive mm[Hg]
収縮期中心静脈圧 60987-5 Central venous pressure (CVP) Systolic mm[Hg]
拡張期中心静脈圧 60986-7 Central venous pressure (CVP) Diastolic mm[Hg]
平均中心静脈圧 8591-0 Central venous pressure (CVP) Mean mm[Hg]
収縮期肺動脈圧 8440-0 Pulmonary artery Systolic blood pressure mm[Hg]
平均肺動脈圧 8414-5 Pulmonary artery Mean blood pressure mm[Hg]
拡張期肺動脈圧 8385-7 Pulmonary artery Diastolic blood pressure mm[Hg]
体温 8310-5 Body temperature Cel
深部体温 8329-5 Body temperature - Core Cel
直腸温 8332-9 Rectal temperature Cel
血液温 60834-9 Blood temperature Cel
腋窩温 8328-7 axillary temperature Cel
膀胱温 8334-5 Body temperature - Urinary bladder Cel
酸素飽和度(経皮的測定) 59408-5 Oxygen saturation in Arterial blood by Pulse oximetry %

以下のHL7 V2系バイタルサインメッセージをopenEHR形式に変換するスクリプト(一部)を示す。

MSH|^~\\&|JPN Kaisha|JPN Kaisha HL7|Kaisha ID|Kaisha NAME|||ORU^R01^ORU_R01|20211130000187|P|2.5||||||UNICODE UTF-8|||
PID|||2021081001||Kaisha^テスト^^^^^L^I~^^^^^^L^A||19700419000000|M|
PV1||I|^^71IC01|||||||||||||||||||||||||||||||||||||||||||
ORC|RE|
OBR|1|||29274-8^Vital Sign Measurement|||20211130184100||||||||||||||||||A|
OBX|1|NM|8867-4^Heart rate||134|/min|||||F|||20211130184100||||
OBX|2|NM||9279-1^Respiratory rate||17|/min|||||F|||20211130184100||||
OBX|3|NM|8310-5^Body temperature||39.2|Cel|||||F|||20211130184100||||
OBX|4|NM|76534-7^Systolic blood pressure by Noninvasive||138|mm[Hg]|||||F|||20211130184100||||
OBX|5|NM|76535-4^Diastolic blood pressure by Noninvasive||83|mm[Hg]|||||F|||20211130184100||||
OBX|6|NM|59408-5^Oxygen saturation in Arterial blood by Pulse oximetry||95|%|||||F|||20211130184100||||

openEHRでのArchetype, templateは以下で公開している。

使用しているものは以下のarchetype, templateである。Archetype Designerにimportすれば編集可能である。

Template: https://github.com/skoba/openehr-lesson-archetypes-jp/blob/main/local/vital_signs.t.json
心拍数: https://github.com/skoba/openehr-lesson-archetypes-jp/blob/main/local/openEHR-EHR-OBSERVATION.pulse.v2.adl
呼吸数: https://github.com/skoba/openehr-lesson-archetypes-jp/blob/main/local/openEHR-EHR-OBSERVATION.respiration.v2.adl
体温: https://github.com/skoba/openehr-lesson-archetypes-jp/blob/main/local/openEHR-EHR-OBSERVATION.body_temperature.v2.adl
血圧: https://github.com/skoba/openehr-lesson-archetypes-jp/blob/main/local/openEHR-EHR-OBSERVATION.blood_pressure.v2.adl
SPO2: https://github.com/skoba/openehr-lesson-archetypes-jp/blob/main/local/openEHR-EHR-OBSERVATION.pulse_oximetry.v1.adl

#! /bin/env ruby
# coding: utf-8
LIB_DIR = File.join(File.dirname(__FILE__) , 'lib')
$LOAD_PATH.unshift(LIB_DIR)
require 'logger'
require 'socket'

require_relative 'lib/ehrbase_client'
require_relative 'lib/vitalbox_message'
require_relative 'lib/vital_sign_message_factory'

ROOT_DIR = File.join(File.dirname(__FILE__), '../')
LOG_DIR = File.join(ROOT_DIR, '/log')
logger = Logger.new(File.join(LOG_DIR, 'rclient.log'))
CR = "\r".freeze
FS = "\x1c".freeze
socket = TCPSocket.open('192.168.183.100', 8102)
loop do
  message = ''
  logger.info 'receiving a message'
  loop do
    line = socket.gets(sep = CR) # socket.readline do |line|
    logger.info line
    message += line
    break if line.nil? || line.include?("#{FS}#{CR}")
  end
  hl7message = VitalBoxMessage.new(message)
  # msh_10 = hl7message.msh.message_control_id
  ack_message = hl7message.ack # "MSH|^~\\&|||||||ACK^R01|#{msh_10}|P|||||||UNICODE UTF-8|||#{CR}MSA|AA|#{msh_10}|#{CR}#{FS}#{CR}"
  logger.info 'send ACK'
  logger.info(ack_message.tr("\r", "\n"))

  # socket.close
  # socket = TCPSocket.open("192.168.183.100", 8102)
  socket.write(ack_message)
  socket.flush

  # confirm EHR id
  patient_id = hl7message.pid.patient_id
  ehr_client = EHRbaseClient.new('http://localhost', 8080)
  response = ehr_client.get_ehr_with(subject_id: patient_id, issuer: 'ICU_AI_Project')  
  logger.info "Status: #{response[:status]}, EHR ID: #{response[:ehr_id]}"
  if response[:status] == 404
    logger.info "create a new EHR"
    response = ehr_client.create_ehr_with(subject_id: patient_id, issuer: 'ICU_AI_Project')
    logger.info 'created EHR', response[:ehr_id]
    logger.info response[:status]
  end
  logger.info 'post EHR to EHRbase'
  # create VitalSign composition with EHR ID
  vitalsign_message = VitalSignMessageFactory.create(hl7message)
  logger.info vitalsign_message
  response = ehr_client.post_composition(ehr_id: response[:ehr_id], format: 'FLAT', template_id: 'vital_signs', body: vitalsign_message)
  logger.info "EHRbase post status: #{response[:status]}"
  #patient_id_issuer = "JPN Kaisha"

end

socket.close
class VitalSignMessageFactory
  def self.create(hl7message)
    <<"END"
{
    "vital_signs/language|code": "en",
    "vital_signs/language|terminology": "ISO_639-1",
    "vital_signs/category|code": "433",
    "vital_signs/category|value": "event",
    "vital_signs/category|terminology": "openehr",
    "vital_signs/territory|code": "US",
    "vital_signs/territory|terminology": "ISO_3166-1",
    "vital_signs/composer|name": "Shinji Kobayashi",
    "vital_signs/body_temperature/language|code": "en",
    "vital_signs/body_temperature/language|terminology": "ISO_639-1",
    "vital_signs/body_temperature/any_event:0/temperature|magnitude": #{hl7message.obx.records['8310-5'].value},
    "vital_signs/body_temperature/any_event:0/temperature|unit": "Cel",
    "vital_signs/body_temperature/location_of_measurement|code": "at0060",
    "vital_signs/pulse_heart_beat/language|code": "en",
    "vital_signs/pulse_heart_beat/language|terminology": "ISO_639-1",
    "vital_signs/pulse_heart_beat/any_event:0/rate|magnitude": #{hl7message.obx.records['8867-4'].value},
    "vital_signs/pulse_heart_beat/any_event:0/rate|unit": "/min",
    "vital_signs/respiration/language|code": "en",
    "vital_signs/respiration/language|terminology": "ISO_639-1",
    "vital_signs/respiration/any_event:0/rate|magnitude": #{hl7message.obx.records['9279-1'].value},
    "vital_signs/respiration/any_event:0/rate|unit": "/min",
    "vital_signs/blood_pressure/language|code": "en",
    "vital_signs/blood_pressure/language|terminology": "ISO_639-1",
    "vital_signs/blood_pressure/any_event:0/systolic|magnitude": #{hl7message.obx.records['76534-7'].value},
    "vital_signs/blood_pressure/any_event:0/systolic|unit": "mm[Hg]",
    "vital_signs/blood_pressure/any_event:0/diastolic|magnitude": #{hl7message.obx.records['76535-4'].value},
    "vital_signs/blood_pressure/any_event:0/diastolic|unit": "mm[Hg]",
    "vital_signs/blood_pressure/location_of_measurement|code": "at1020",
    "vital_signs/pulse_oximetry/language|code": "en",
    "vital_signs/pulse_oximetry/language|terminology": "ISO_639-1",
    "vital_signs/pulse_oximetry/any_event:0/spo|numerator": #{hl7message.obx.records['59408-5'].value},
    "vital_signs/pulse_oximetry/any_event:0/spo|denominator": 100.0,
    "vital_signs/pulse_oximetry/any_event:0/spo|type": 2,
}
END
  end
end
  1. Thomas Beale, FHIR compared to openEHR, https://wolandscat.net/2017/01/29/fhir-compared-to-openehr/ , accessed June 23, 2022, 日本語訳:FHIRとopenEHRの違いについて,https://openehr.jp/projects/translation-jp/wiki/FHIR%E3%81%A8openEHR%E3%81%AE%E9%81%95%E3%81%84%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1