/* * Copyright (c) 1995, 1996 Gunther Schadow. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #pragma implementation "results.h" #include "results.h" #include "abl500.h" #include "ORUmsg.h" #include "Unit.h" #include "logfile.h" #include "LOINC.h" #include "patinfo.h" #include "operinfo.h" #include "DataTypeCode.h" #include #include #include #include "xios.h" /* ASTM --> HL7 mapping. ASTM RESULT MESSAGE | HL7 OBSERV RESULT/UNSOLICITED ================================|================================== | H.............................+.......MSH {.............................+.......{ P...........................+.........[ . | PID . | [ { NTE } ] . | [ PV1 ] .......................+.........] {...........................+.........{ . | [ ORC ] . O.........................+...........OBR . | [ { NTE } ] . {.........................+...........[ . . | { . .R........................+...............OBX . . | [ { NTE } ] . . | } . }.........................+...........] }...........................+.........} }.............................+.......} L | [ DSC ] H.SENDER(5) ZA 0--1 > MSH.SENDING APPLICATION ST 1 > MSH.SENDING FACILITY ST 1 ZA.BRAND ZA.ID H.DATE/TIME OF MESSAGE(14) TS 0--1 ? MSH.DATE/TIME OF MESSAGE TS 1 P.SEQUENCE NUMBER(1) SI 0--1 = MSH.SET ID - PATIENT ID SI 1 P.LAB ASSIGNED PATIENT ID(3) ID 0--1 = PID.ALTERNATE PATIENT ID P.PATIENT NAME(5) PN 0--1 | PID.PATIENT NAME patinfo()... P.SEX(8) 0001 ID 0--1 | PID.SEX P.PATIENT AGE(14) CQ 0--1 | -- P.PATIENT HEIGHT(16) CQ 0--1 | -- P.PATIENT WEIGHT(17) CQ 0--1 | -- P.LOCATION(26) ID 0--1 | PV1.ASSIGNED PATIENT LOCATION O.SEQUENCE NUMBER(1) SI 0--1 = OBR.SET ID OBSERVATION REQUEST O.INSTRUMENT SPECIMEN ID(3) ZI 0--1 = OBR.FILLER ORDER NUMBER CM ZI.TYPE 1005! ID CM.COM1 ZI.NUMBER SI CM.COM2 O.SPECIMEN COLLECTION DATE/TIME TS 0--1 = OBR.OBSERVATION DATE/TIME O.SPECIMEN DESCRIPTOR(15) ZS 0--1 > OBR.SPECIMEN SOURCE ZS.TYPE 1001! ID ZS.SOURCE 1002! ID R.SEQUENCE NUMBER(1) SI 0--1 = OBX.SET ID - OBSERVATIONAL SIMPLE R.UNIVERSAL TEST ID(2) ZT 0--1 | OBX.OBSERVATION IDENTIFIER toloinc() ZT.PARAMETER NAME 1003! ID ZT.PARAMETER TYPE 1004! ID R.MEASUREMENT VALUE(3) ST 0--1 > OBX.OBSERVATION VALUE ...(tounit()) R.UNITS(4) 0003! ID 0--1 > OBX.UNITS tounit() R.ABNORMAL FLAG(6) 0078 ID 0--1 = OBX.ABNORMAL FLAGS R.RESULT STATUS(8) 0085 ID 0--1 = OBX.OBSERV RESULT STATUS R.OPERATOR ID(10) ID 0--1 > OBX.RESPONSIBLE OBSERVER operinfo() R.DATE/TIME TEST STARTED(11) TS 0--1 = OBX.DATE/TIME OF THE OBSERVATION R.INSTRUMENT ID(13) 1006! ID 0--1 = OBX.PRODUCER'S ID L.SEQUENCE NUMBER(1) SI 0--1 | -- L.TERMINATION CODE(2) 1007! ID 0--1 | -- (check) */ void astm2hl7(const ASTMmsg &astm, ORUmsg &oru) { // H --> MSH oru.MesHea.SenApp = astm.Hea.Sen.Brand; oru.MesHea.SenFac = astm.Hea.Sen.Id; oru.MesHea.DateTimeOfMes = astm.Hea.DateTimeOfMes; // cycle through result per-patient reports (usually only one!) for(const repstruc *rpg = &astm.Pgroup; !rpg->end(); rpg = rpg->next()) { const ASTMmsg::Pgrp &pg = rpg->first(); ORUmsg::GRPgrp &grpg = oru.GRPgroup.append(); // P --> PID + PV1 const Pseg &p = pg.Pat; ORUmsg::GRPgrp::PIDgrp &pidg = grpg.PIDgroup; pidg.PatIde.SetIdPatId = p.SeqNum; patinfo pti(p.LabAssPatId); if(pti.visit_nr != NULL) { pidg.PatIde.PatIdIntId[0].IdNum = pti.patient_id; pidg.PatIde.PatName.FamName = pti.family_name; pidg.PatIde.PatName.GivenName = pti.given_name; pidg.PatIde.Sex = pti.sex; if(pti.dob_day != 0 && pti.dob_month != 0) pidg.PatIde.DateOfBirth = DTtyp(pti.dob_year, pti.dob_month, pti.dob_day); pidg.PatVisit.AssPatLoc.NurseUnit = "060_IOP"; // FIXME! a constant! pidg.PatVisit.AssPatLoc.Room = pti.room; pidg.PatVisit.AssPatLoc.Bed = pti.bed; pidg.PatVisit.VisitNum.IdNum = pti.visit_nr; } pidg.PatIde.AltPatId = p.LabAssPatId; // cycle through batteries for(const repstruc *rog = &pg.Ogroup; !rog->end(); rog = rog->next()) { const ASTMmsg::Pgrp::Ogrp &og = rog->first(); ORUmsg::GRPgrp::OBRgrp &obrg = grpg.OBRgroup.append(); // resolve operator id, once for the whole battery operinfo opi((const char *)og.Res.first().OpeId); // O --> OBR const Oseg &o = og.Order; OBRseg &obr = obrg.ObsReq; obr.SetIdObsReq = o.SeqNum; obr.FilOrderNum.Com1 = ((SItyp *)&o.InsSpeId.Num)->o2str(); obr.FilOrderNum.Com2 = o.InsSpeId.Type; obr.SpeSou.Com1 = specimen_for_the_world(o.SpeDes); STtyp SpecimenLOINC = specimen_for_LOINC(o.SpeDes); // DIAG. SERVICE SECTION ID is ABL even if we measure glucose. obr.DiaGnoServSectId = DiaGnoSerSecIdCode::BloodGases; obr.ResStatus = ResStatusCode::ResStoNotYetVer; // always TStyp SpeColDateTime = o.SpeColDateTime; if(opi.operator_id != -1) { // FIXME (is a CN in the CM!) (FIX HL7!) obr.Tec[0].Com1 = opi.operator_id; obr.Tec[0].Com2 = opi.family_name; obr.Tec[0].Com3 = opi.given_name; } // cycle though results for(const repstruc *rrs = &og.Res; !rrs->end(); rrs = rrs->next()) { // R --> OBX const Rseg &r = rrs->first(); const char *vals = (const char *)r.MeaValue; if(vals == NULL || *vals == '\0' || strcmp(vals,".....") == 0) { LOGINFO("discarding result %s: %s %s", (const char *)r.UniTestId.ParaMeterName, vals, (const char *)r.Units); continue; } ORUmsg::GRPgrp::OBRgrp::OBXgrp &obxg = obrg.OBXgroup.append(); OBXseg &obx = obxg.Obs; if(*vals == '?') { obx.ObsResStatus = ObsResStatusCodesIntCode::Xval; obx.AbnFlags[0] = AbnFlagsCode::Aval; // build an NTE segment strstream notes; xios _xnotes(notes); notes << hl7er << "Fehlerhafte Messung: `" << vals << " " << r.Units << "'" << ends; obxg.NotesAndCom[0].SetIdNotesAndCom = 1; obxg.NotesAndCom[0].SouOfCom = SouOfComCode::Lval; obxg.NotesAndCom[0].Com[0] = notes.str(); notes.freeze(0); vals++; } else if(*vals == '<') { obx.ObsResStatus = r.ResStatus; obx.AbnFlags[0] = AbnFlagsCode::_LTval; vals++; } else if(*vals == '>') { obx.ObsResStatus = r.ResStatus; obx.AbnFlags[0] = AbnFlagsCode::_GTval; vals++; } else { obx.ObsResStatus = r.ResStatus; obx.AbnFlags[0] = r.AbnFlag; } if(*vals == '\0' || strcmp(vals,".....") == 0) { LOGINFO("discarding result %s: %s %s", (const char *)r.UniTestId.ParaMeterName, vals, (const char *)r.Units); obxg.unset(); continue; } obx.SetIdObsSim = r.SeqNum; if(r.DateTimeTestSta.ispresent()) { obx.DateTimeOfTheObs = r.DateTimeTestSta; // if spec. coll. date time was not *entered manually* // we try to find some value from the results if(! SpeColDateTime.ispresent()) SpeColDateTime = r.DateTimeTestSta; } obx.ProSId.Ide = r.InsId; if(opi.operator_id != -1) { obx.ResObs.IdNum = opi.operator_id; obx.ResObs.FamName = opi.family_name; obx.ResObs.GivenName = opi.given_name; } // translate observation to LOINC standard Loinc loinc; Unit unit; NMtyp number(vals); toLOINC(r.UniTestId, r.Units, SpecimenLOINC, number, loinc, unit); obx.ObsIde.Ide = (const char *)loinc; obx.ObsIde.Text = loinc.getMeasure(); // FIXME! Code IDs are codes as well! obx.ObsIde.CodSys = "LN"; obx.ObsIde.AltIde = r.UniTestId.ParaMeterName; obx.ObsValue = STtyp(number); obx.Units.Ide = unit; obx.ValueType = DataTypeCode::NMval; } // if spec. coll. date/time was not *entered manually* // and we couldn't find some value from the results // we must resort to the time of the ASTM message if(! SpeColDateTime.ispresent()) SpeColDateTime = astm.Hea.DateTimeOfMes; obr.ObsDateTime = SpeColDateTime; obr.SpeRecDateTime = SpeColDateTime; obr.ResRptStaChngDateTime.set(); } } }