Pesquisar

文章
· 九月 16 阅读大约需 4 分钟

依托FHIR标准,北京安贞医院推动进口医疗设备集成

2024年10月19日,首都医科大学附属北京安贞医院(简称“北京安贞医院”)通州院区开诊,医院迈入两院区一体化发展新阶段。“随着手术量的增加,术中监测设备上的指标自动采集,对于术中工作效率的提高就更加重要了,但有些设备尚未做到国产化,例如心脏搭桥手术血流监测仪是挪威进口设备,只支持通过FHIR标准与HIS对接。于是,我们在院区开业一个月后启动了基于FHIR(Fast Healthcare Interoperability Resources,快速医疗保健互操作性资源)的医疗信息系统集成技术路线与开发项目,以心脏搭桥手术血流监测仪为突破口,实现此类设备的结构化数据采集、存储及应用。”北京安贞医院信息中心技术总监周奕介绍。

对接医、教、研需求

作为一家以心血管疾病诊治为特长的大型三甲医院,北京安贞医院的心脏内外科数量占了医院科室的一半,医院心脏外科手术量每年超过15000例,其中心脏搭桥手术占一半以上。

在开展搭桥手术过程中,血管流量监测设备中的指标数据,是术中及术后判断桥血管通畅性和血流动力学的重要指标。北京安贞医院的信息化建设基本上涵盖了医院所有业务,但此类进口医疗设备数据始终游离于信息系统之外,“因为它平常是采用DICOM格式传输报告,输出形式主要是影像图片,但PACS系统解析这些图片后,却是一堆乱码,无法进一步应用。”周奕介绍道。

于是,医生在进行搭桥手术时,需要由巡回护士或麻醉师手工抄写患者手术过程中的相关数据,录入Excel表格中,整个过程效率低下,也不利于科研和教学等数据利用需求。

从手工到电子化,可实时提取数据

“接口系统开发的第一步是需求调研。”周奕谈到,项目组分别同心外科医生、麻醉医生、体外循环医生和护士等相关人员进行了充分讨论,考虑了临床的各个场景,在此基础上设计了系统架构,然后是详细设计。由于FHIR具有便捷开发的特点,因此整个系统只用了半个月时间就开发完毕,实现了HIS系统与血管流量监测设备的对接。

“系统对接后的一大转变,是彻底改变了以前传统的手工流程,实现了全自动提取。”周奕强调,巡回护士在血管流量监测设备系统屏幕上点击一下,就会调出患者的基本信息及手术排班信息。当患者的心脏搭桥手术开始后,监测数据便开始自动采集,并传回HIS系统。HIS系统可以按照手术时间顺序、血流量和压力变化等,即刻出具图文并茂的报告,呈现在电子病历的手术记录中,具有很好的临床辅助决策支持意义。根据科研需求,周奕带领团队对报告的内容进行了全结构化处理,并将其保存在科研数据中,方便统一利用。

 

巡回护士在血管流量监测设备系统屏幕上点击一下,就会调出患者的基本信息及手术排班信息,手术开始后,监测数据便开始自动采集,并传回HIS系统。
 

 

手术开始后,数据即时回传至HIS系统,并生成图文并茂的报告,医生可以随时在电子病历中查看相关数据和报告。

 

FHIR四大优势推动集成落地

周奕谈到,基于WEB技术构建的FHIR标准具备四大优势,有效推动了集成的落地。

第一,开发非常便捷。FHIR能够按照标准的RESTful的常用请求方法调用接口,对开发人员而言,不用再摸索那些复杂的数据传输协议,使开发过程简单便捷。

第二,数据互操作性强。FHIR具备跨系统、跨平台的兼容性,只要遵循FHIR的资源结构和接口规范,即可实现标准化的数据交换,极大降低了系统集成复杂度。 

第三,行业认可度高。FHIR在全球范围获得认可。

第四,符合法规和安全要求。医疗数据一般涉及患者的隐私,受到严格的法律法规监管。FHIR充分考虑到这一点,内置了安全机制,包括身份认证和数据加密等,支持多模式的验证,确保数据传输、存储的合规。

作为此次项目运行的基石平台,InterSystems IRIS医疗版数据平台提供了标准化FHIR API、事务处理能力与高性能存储能力,为整个系统的可靠运行提供了坚实支撑。“由于FHIR标准所具备的优势,我们的开发仅用了半个月就完成了。”周奕说,“在这个项目中,InterSystems提供的不仅仅是一个平台,还提供了方法和服务,帮助医院快速构建起稳定、高效、具备扩展性的FHIR数据服务能力。”

三步走整合数据中心

医院积累了大量数据后,如何实现数据的有效应用是一大挑战。

北京安贞医院在2018年就建立了数据中心,包括临床数据中心、科研数据中心、运营数据中心。周奕介绍,在医院制定的《北京安贞医院2025-2027信息化建设三年规划》中,一项非常重要的工作就是整合数据中心,主要分为三步:第一步,尽可能全地收集原始数据,形成数据湖;第二步,进行数据质控及初步清洗,形成纯净湖;第三步,在纯净湖基础上,根据各方应用,形成统一数据中心。

谈及医疗设备与HIS系统集成后的下一步规划时,周奕强调,将根据临床提出的新需求,探索通过FHIR标准将脱敏后的手术数据接入国际心脏内、外科排名系统,客观了解国内心脏病诊疗水平在国际上的位置,提升国际影响力。在此次项目中积累的FHIR能力,也有助于这项工作的完成。

 

本文首发于CHIMA微信公众号,点击此处查看原文。

讨论 (0)1
登录或注册以继续
文章
· 九月 16 阅读大约需 2 分钟

Custom File Adapter - Lookup Table / Dynamic Files

My problem was separating HL7 messages by message type.  I had to create multiple File Operations.  So I with custom code I am able to use 1 File Adapter for 1 interface and multiple message types.  I did experiment pulling the MSH 4 out of the raw content to further access dynamic information, but that may open a need for more robust error checking / lookup default actions.

Using the recommended naming convention of "To_FILE_<IntegrationName>"

I decided to use a generic file name and path in the default settings.

I created a custom class that extended the EnsLib.File.OutboundAdapter  With some custom code that allows me to dynamically control file adapter path specific to each message type via a lookup table.  If I don't have a value then the default generic path will be used.  Otherwise my code will override the File Path and File Name.  Lookup table name can be anything, just needs to match in your code.


 

Custom Code

//SRC1 Pull  3rd piece name of the outbound operation name "<IntegrationName>"

//SRC2 Pull the 1st piece of the DOCTYPE NAME  "ORM" / "ADT" / "ORU" / etc..

// Set a new variable SRC to concatenate the SRC1_SRC2 together

//New Lookup table that will control path names all in one place.

 

Set src1=$PIECE(..%ConfigName,"_",3,3)
Set src2=$PIECE(pDocument.DocTypeName,"_",1,1)
If src=""
{
Set src=src1_"_"_src2
} Set pFilename = ..Adapter.CreateFilename(##class(%File).GetFilename(src), $PIECE((##class(Ens.Rule.FunctionSet).Lookup("HL7FileNamePath",src)),"^",2,2)_..Filename) $$$TRACE(pFilename)
//Reset file path to return a file path based on the Lookup and PIECE function(s)
Set ..Adapter.FilePath =$PIECE((##class(Ens.Rule.FunctionSet).Lookup("HL7FileNamePath",src)),"^",1,1)
$$$TRACE(..Adapter.FilePath)
Set tSC = ..Adapter.open(pFilename) Quit:$$$ISERR(tSC) tSC
Set $ZT="Trap"
Use ..Adapter.Device  Set tSC=..OutputFramedToDevice(pDocument,pSeparators,"",0,..IOLogEntry,.pDoFraming) Use ..Adapter.OldIO
Set $ZT=""

讨论 (0)1
登录或注册以继续
公告
· 九月 16

Kick-off Webinar for InterSystems External Languages Contest

Hey Community,

We're pleased to invite all the developers to the upcoming kick-off webinar for the InterSystems External Languages Contest!

During the webinar, you will discover the exciting challenges and opportunities that await developers in this contest. We will also discuss the topics we would like the participants to cover and show you how to develop, build, and deploy applications using the InterSystems IRIS data platform.

Date & Time: Wednesday, September 24 – 11:30 am EDT | 5:30 pm CEST  

Speakers:  
🗣 @Derek Gervais, Developer Relations Evangelist, InterSystems
​​​🗣 @Evgeny Shvarov, Senior Manager of Developer and Startup Programs, InterSystems
🗣 @Stefan Wittmann, Product Manager, InterSystems
​​​🗣 @Raj Singh, Product Manager - Developer Experience, InterSystems

✅ Register for the kick-off today!

讨论 (0)2
登录或注册以继续
问题
· 九月 16

HL7 Pass-through interface

For historic reasons we've got a mix of ADT feeds coming out of our PAS (TrakCare) to a wide range of downstream systems. In particular, there are some that are direct from TrakCare to the downstream systems, and many more that pass through Ensemble as our integration engine.

This is complicating management of the integrations, and so we'd like everything to go through the integration engine. In other words move from the flow in the top of the diagram to the flow in the bottom of the diagram:

So we want to build a couple of pass-through interfaces in Ensemble that respond identically to the current direct feeds - no transformations, and all ACK behaviour is transparent so that the only change visible to the TrakCare PAS is a change of IP/port for each of the currently direct feeds.

Should be easy, right? An HL7 TCP Service, possibly a routing process that simply passes everything on, and an HL7 TCP Operation that is connected to the downstream...

But what is happening is that the Router Process is generating an ACK and sending it back to the Service before the ACK comes back from the downstream. (I've faked a downstream, manually populated the ACK its sending back so I can definitely identify it. I can see it arriving in Ensemble - but after an ACK is sent to the upstream via the Service.) See the annotated message trace below:

Any pointers to what to be looking at?

I'm aware of Important HL7 Scenarios | Routing HL7 Version 2 Messages in Productions | InterSystems IRIS for Health 2025.1 and the Settings for HL7 Services, linked from there, but not seeing what we might have set wrong...

Thanks, Colin

2 条新评论
讨论 (2)2
登录或注册以继续
文章
· 九月 16 阅读大约需 3 分钟

Do "Ops!" ao "Aha!" - Evitando erros de principiante no ObjectScript

Começar a usar ObjectScript é realmente empolgante, mas também pode parecer um pouco estranho se você está acostumado com outras linguagens. Muitos iniciantes tropeçam nos mesmos obstáculos, então aqui estão alguns "pegadinhas" que você vai querer evitar. (Além de algumas dicas amigáveis para contorná-las.)


Nomear Coisas Aleatoriamente

Todos nós já fomos culpados de nomear algo como Test1 ou MyClass apenas para seguir em frente rapidamente. Mas quando seu projeto cresce, esses nomes se tornam um pesadelo.

➡  Escolha nomes claros e consistentes desde o início. Pense nisso como deixar um rastro para o seu "eu" do futuro e para seus colegas de equipe.


Confundir Globais e Variáveis

Globais (^GlobalName) podem ser confusas no começo. Eles não são apenas variáveis normais. Eles vivem no banco de dados e persistem mesmo depois que seu código para de rodar.

➡ Use-os apenas quando você realmente precisar de dados persistentes. Para todo o resto, use variáveis locais. (Isso também economiza armazenamento.)


Esquecer Transações

Imagine atualizar um registro de paciente e sua sessão travar na metade. Sem uma transação, você fica com dados incompletos.

➡ Envolva atualizações importantes em TSTART/TCOMMIT. É como apertar "salvar" e "desfazer" ao mesmo tempo.


Construir SQL em Strings

É tentador simplesmente colocar SQL em strings e executá-lo. Mas isso rapidamente se torna confuso e difícil de depurar.

➡ Use SQL embutido. É mais limpo, mais seguro e mais fácil de manter.

EXEMPLO:

❌ Construindo SQL em Strings

Set id=123
Set sql="SELECT Name, Age FROM Patient WHERE ID="_id
Set rs=##class(%SQL.Statement).%ExecDirect(,sql)

✅ Usando SQL Embutido

&SQL(SELECT Name, Age INTO :name, :age FROM Patient WHERE ID=:id)
Write name_" "_age,!

Ignorar o Tratamento de Erros

Ninguém gosta de ver seu aplicativo travar com uma mensagem enigmática. Isso geralmente acontece quando o tratamento de erros é ignorado.

➡Envolva operações arriscadas em TRY/CATCH e forneça a si mesmo mensagens de erro significativas.



Deixar de Usar Ferramentas Melhores

Sim, o terminal funciona. Mas se você só codifica por lá, está perdendo muito.

➡ Use o VS Code com a extensão ObjectScript. A depuração, o autocompletar e o destaque de sintaxe tornam a vida muito mais fácil.


Reinventar a Roda

Novos desenvolvedores frequentemente tentam escrever suas próprias utilidades para registro ou manipulação de JSON, sem perceber que o ObjectScript já tem soluções integradas.

➡ Explore a %Library e os objetos dinâmicos antes de criar os seus.


Escrever "Código Misterioso"

Todos nós já pensamos: "Vou me lembrar disso depois."

⚠️SPOILERVOCÊ NÃO VAI!

Adicione comentários curtos e claros. Mesmo uma única linha explicando por que você fez algo ajuda muuuito.


 

Considerações Finais : )

Aprender ObjectScript é como aprender qualquer outra língua nova. É preciso um pouco de paciência, e você cometerá erros ao longo do caminho. O segredo é reconhecer essas armadilhas comuns cedo e construir bons hábitos desde o início. Dessa forma, em vez de lutar contra a linguagem, você realmente aproveitará o que ela pode fazer. :)

讨论 (0)1
登录或注册以继续