/* * Copyright (c) 1994, 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. */ /* NOTES about Oracle's Pro*C and C++: * * The Pro*C precompiler declares functions with `extern'. In order to avoid * C++ name mangling on the function names, `extern' must be redefined to * `extern "C"'. Since Pro*C has no means to redefine keywords and the * declarations are made at the very beginning of our own program we have to * redefine `extern' on the gcc command line (like -Dextern='extern "C"'). * However, for the inclusion of C++ declaration we must temporarily turn * off the redefinition. */ /* * begin C++ include section */ #undef extern #define extern extern #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern "C" int mod(const char *, int); /* * end C++ include section */ #undef extern #define extern extern "C" EXEC SQL INCLUDE sqlca; /* * Macros that give meaning to the `indicator variables'. */ #define IS_NULL(x) (x == -1) #define IS_VALID(x) (x == 0) #define IS_TRUNC(x) (x > 0) /* * We reference the cummulative row counter as `ROW_COUNT' */ #define ROW_COUNT sqlca.sqlerrd[2] /* * The string type to be used in the SQL DECLARE SECTION */ typedef char charray[20]; /* * MAX_MSG is the array size for the arrays where oracle put the results * into. NOTE: For the oracle Pro*C preprocessor, the cpp definitions are * not yet expanded thus we have to repeat the value for MAX_MSG literally! * In order to allow easy modification of that value, we always prepend */ /*MAX_MSG*/ /* directly to the literal value. */ #define MAX_MSG 10 EXEC SQL BEGIN DECLARE SECTION; EXEC SQL TYPE charray IS STRING(20) REFERENCE; charray username; charray password; charray database; /* * Define a *very* ugly spaghetti of arrays (Oracle's C interface is * rather brain dead since it doesn't support structures!) */ /* * EVN */ charray EventTypeCode [/*MAX_MSG*/10]; short iEventTypeCode [/*MAX_MSG*/10]; int AdmYear [/*MAX_MSG*/10]; short iAdmYear [/*MAX_MSG*/10]; int AdmMonth [/*MAX_MSG*/10]; short iAdmMonth [/*MAX_MSG*/10]; int AdmDay [/*MAX_MSG*/10]; short iAdmDay [/*MAX_MSG*/10]; int AdmHour [/*MAX_MSG*/10]; short iAdmHour [/*MAX_MSG*/10]; int AdmMin [/*MAX_MSG*/10]; short iAdmMin [/*MAX_MSG*/10]; int DisYear [/*MAX_MSG*/10]; short iDisYear [/*MAX_MSG*/10]; int DisMonth [/*MAX_MSG*/10]; short iDisMonth [/*MAX_MSG*/10]; int DisDay [/*MAX_MSG*/10]; short iDisDay [/*MAX_MSG*/10]; int DisHour [/*MAX_MSG*/10]; short iDisHour [/*MAX_MSG*/10]; int DisMin [/*MAX_MSG*/10]; short iDisMin [/*MAX_MSG*/10]; /* * PID */ charray PatIdExtId [/*MAX_MSG*/10]; short iPatIdExtId [/*MAX_MSG*/10]; charray PatName_FamName [/*MAX_MSG*/10]; short iPatName_FamName [/*MAX_MSG*/10]; charray PatName_GivenName [/*MAX_MSG*/10]; short iPatName_GivenName [/*MAX_MSG*/10]; charray PatName_Deg [/*MAX_MSG*/10]; short iPatName_Deg [/*MAX_MSG*/10]; charray PatName_Pre [/*MAX_MSG*/10]; short iPatName_Pre [/*MAX_MSG*/10]; charray MotSMaiName [/*MAX_MSG*/10]; short iMotSMaiName [/*MAX_MSG*/10]; /*int*/charray DateOfBirth_Year [/*MAX_MSG*/10]; short iDateOfBirth_Year [/*MAX_MSG*/10]; /*int*/charray DateOfBirth_Month [/*MAX_MSG*/10]; short iDateOfBirth_Month [/*MAX_MSG*/10]; /*int*/charray DateOfBirth_Day [/*MAX_MSG*/10]; short iDateOfBirth_Day [/*MAX_MSG*/10]; charray SexCode [/*MAX_MSG*/10]; short iSexCode [/*MAX_MSG*/10]; charray PatAdd_StrAdd [/*MAX_MSG*/10]; short iPatAdd_StrAdd [/*MAX_MSG*/10]; charray PatAdd_Zip [/*MAX_MSG*/10]; short iPatAdd_Zip [/*MAX_MSG*/10]; charray PatAdd_City [/*MAX_MSG*/10]; short iPatAdd_City [/*MAX_MSG*/10]; charray PhoneNumHome [/*MAX_MSG*/10]; short iPhoneNumHome [/*MAX_MSG*/10]; /* * PV1 */ charray PatClassCode [/*MAX_MSG*/10]; short iPatClassCode [/*MAX_MSG*/10]; charray SerFac [/*MAX_MSG*/10]; short iSerFac [/*MAX_MSG*/10]; charray PriVisitNum [/*MAX_MSG*/10]; short iPriVisitNum [/*MAX_MSG*/10]; charray RefDoc_Name [/*MAX_MSG*/10]; short iRefDoc_Name [/*MAX_MSG*/10]; charray VisitNum [/*MAX_MSG*/10]; short iVisitNum [/*MAX_MSG*/10]; charray DisDis [/*MAX_MSG*/10]; short iDisDis [/*MAX_MSG*/10]; EXEC SQL END DECLARE SECTION; void sqlerror(); /* handles unrecoverable errors */ void process(int max = MAX_MSG); /* process the results of the retreival */ void admit (MSHseg &msh, EVNseg &evn, PIDseg &pid, PV1seg &pv1); void discharge(MSHseg &msh, EVNseg &evn, PIDseg &pid, PV1seg &pv1); void transfer (MSHseg &msh, EVNseg &evn, PIDseg &pid, PV1seg &pv1); /* * Event counters */ static int admit_cnt = 0; static int discharge_cnt = 0; static int transact_cnt = 0; #define SEN_APP "uks3p" #define SEN_FAC "adt-update" #define REC_APP "uks3p" #define REC_FAC "hl7-comsrv" int main(int argc, char *argv[]) { { /* parse options and setup logger */ int loglevel = 0; char c; while( (c = getopt(argc, argv, "l")) != -1 ) { switch(c) { case 'l': loglevel = atoi(optarg); break; default: cerr << "usage: " << argv[0] << "[-l loglevel]" << endl; exit(0); } } argc -= optind; argv += optind; log_init(argv[0], "./hl7update.log", "test"); log_level(loglevel); lprintf(L_MESG, "hl7 updater startup"); } /* prepare output stream for hl7 */ xios cout_x(cout); /* Log onto ORACLE */ strcpy(username, "schadow"); strcpy(password, "hl7ora"); strcpy(database, "T:anae:anae"); EXEC SQL WHENEVER SQLERROR DO sqlerror(); EXEC SQL CONNECT :username IDENTIFIED BY :password USING :database; EXEC SQL DECLARE cURSo CURSOR FOR SELECT EVN.EventTypeCode, EVN.AdmYear, EVN.AdmMonth, EVN.AdmDay, EVN.AdmHour, EVN.AdmMin, EVN.DisYear, EVN.DisMonth, EVN.DisDay, EVN.DisHour, EVN.DisMin, PID.PatIdExtId, PID.PatName_FamName, PID.PatName_GivenName, PID.PatName_Deg, PID.PatName_Pre, PID.MotSMaiName, PID.DateOfBirth_Year, PID.DateOfBirth_Month, PID.DateOfBirth_Day, PID.SexCode, PID.PatAdd_StrAdd, PID.PatAdd_Zip, PID.PatAdd_City, PID.PhoneNumHome, PV1.PatClassCode, PV1.SerFac, PV1.PriVisitNum, PV1.RefDoc_Name, PV1.VisitNum, PV1.DisDis FROM EVN, PID, PV1 WHERE EVN.LINK = PID.LINK AND EVN.LINK = PV1.LINK; EXEC SQL OPEN cURSo; { /* * Emmit batch header */ BHSseg bhs; bhs.setBatchSenApp(SEN_APP); bhs.setBatchSenFac(SEN_FAC); bhs.setBatchRecApp(REC_APP); bhs.setBatchRecFac(REC_FAC); bhs.setBatchNameIdType("ADT-UPDATE"); bhs.setBatchCom("ADT update from PASTA " "(Spaghetti, Canneloni, Lasagne, Macaroni et.al.)"); char xanbuf[20]; sprintf(xanbuf, "B%05d", (int)getpid()); bhs.setBatchConId(xanbuf); cout << bhs; } /* * Begin Retrieval */ int blocks; for(blocks = 0; TRUE; blocks++) { EXEC SQL WHENEVER NOT FOUND DO break; EXEC SQL FETCH cURSo INTO EventTypeCode:iEventTypeCode, AdmYear:iAdmYear, AdmMonth:iAdmMonth, AdmDay:iAdmDay, AdmHour:iAdmHour, AdmMin:iAdmMin, DisYear:iDisYear, DisMonth:iDisMonth, DisDay:iDisDay, DisHour:iDisHour, DisMin:iDisMin, PatIdExtId:iPatIdExtId, PatName_FamName:iPatName_FamName, PatName_GivenName:iPatName_GivenName, PatName_Deg:iPatName_Deg, PatName_Pre:iPatName_Pre, MotSMaiName:iMotSMaiName, DateOfBirth_Year:iDateOfBirth_Year, DateOfBirth_Month:iDateOfBirth_Month, DateOfBirth_Day:iDateOfBirth_Day, SexCode:iSexCode, PatAdd_StrAdd:iPatAdd_StrAdd, PatAdd_Zip:iPatAdd_Zip, PatAdd_City:iPatAdd_City, PhoneNumHome:iPhoneNumHome, PatClassCode:iPatClassCode, SerFac:iSerFac, PriVisitNum:iPriVisitNum, RefDoc_Name:iRefDoc_Name, VisitNum:iVisitNum, DisDis:iDisDis; process(); } process(ROW_COUNT - blocks * MAX_MSG); /* * Close cursor and logoff database */ EXEC SQL CLOSE cURSo; EXEC SQL COMMIT WORK RELEASE; { /* * Emmit Batch Trailer */ BTSseg bts; char xanbuf[20]; sprintf(xanbuf, "%05d", transact_cnt); bts.setBatchMesCount(xanbuf); bts.setBatchCom("End of Spaghetti batch"); cout << bts; } return 0; } extern sqlglm(char *msgbuf, int *buflen, int *msglen); void sqlerror() { int msgbuf_size = 512; int msglen; char msgbuf[msgbuf_size]; EXEC SQL WHENEVER SQLERROR CONTINUE; sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = 0; cerr << "database access error:" << sqlca.sqlerrm.sqlerrmc << endl; sqlglm(msgbuf, &msgbuf_size, &msglen); msgbuf[msglen] = 0; cerr << msgbuf << endl; EXEC SQL ROLLBACK RELEASE; exit(1); } /* * Code converter functions */ PatClassCode::Value grundlst2pclass(const char *grundlst, short igrundlst); EventTypeCode::Value fuc2evc(const char *fucode, short ifucode); EventTypeCode::Value fuc2evc(const char *fucode) { if(strcmp(fucode, "AUF") == 0) return EventTypeCode::AdmitAPat; else if(strcmp(fucode, "ENT") == 0) return EventTypeCode::DisAPat; else if(strcmp(fucode, "INT") == 0) return EventTypeCode::TraAPat; else { LOGWARNING("illegal function code `%s'", fucode); return EventTypeCode::invalid; } } /* * CAVE: This is not much more than a dummy. */ PatClassCode::Value grundlst2pclass(const char *grundlst) { if(strcmp(grundlst, "AKUT") == 0) return PatClassCode::Inp; else if(strcmp(grundlst, "POLI") == 0) return PatClassCode::Out; else if(strcmp(grundlst, "PAKUT") == 0) // PAKUT == ??? return PatClassCode::Pre; else if(strcmp(grundlst, "IOP") == 0) return PatClassCode::Eme; else return PatClassCode::Inp; // there are at least 15 more! } /* * Macros that deal with value-indicator pairs. * Naming convention: Where value is `Foo' the indicator is `iFoo'. * A value-indicator pair in a struc would be a much cleaner solution. */ #define SETALL(obj, prop, orac) \ { if(IS_NULL(i##orac[i])) obj.unset(); else obj.set##prop(orac[i]); } #define SETANY(obj, prop, orac) \ { if(!IS_NULL(i##orac[i])) obj.set##prop(orac[i]); } #define SETCNV(obj, prop, f, orac) \ { if(!IS_NULL(i##orac[i])) obj.set##prop(f(orac[i])); } /* * Process one group of arrays. The arrays are static!!! */ void process(int max) { int i; for(i=0; i rPhoneNumHome; rPhoneNumHome[0]=PhoneNumHome[i]; pid.setPhoneNumHome(rPhoneNumHome); } PV1seg pv1; SETCNV(pv1, PatClassCode, grundlst2pclass, PatClassCode); SETANY(pv1, SerFac, SerFac); SETCNV(pv1, VisitNum, atol, VisitNum); SETANY(pv1, DisDis, DisDis); CNtyp RefDoc; PNtyp RefDoc_PName; SETANY(RefDoc_PName, FamName, RefDoc_Name); RefDoc.setName(RefDoc_PName); pv1.setRefDoc(RefDoc); MSHseg msh; msh.setSenApp(SEN_APP); msh.setSenFac(SEN_FAC); msh.setRecApp(REC_APP); msh.setRecFac(REC_FAC); msh.setProIdCode(ProIdCode::Pro); msh.setVerId(2.1); switch((int)evn.getEventTypeCode()) { case EventTypeCode::AdmitAPat: evn.setDateTimeOfEvent(AdmTs); admit(msh, evn, pid, pv1); break; case EventTypeCode::DisAPat: evn.setDateTimeOfEvent(DisTs); pv1.setAdmitDateTime(AdmTs); discharge(msh, evn, pid, pv1); break; case EventTypeCode::TraAPat: transfer(msh, evn, pid, pv1); break; } } } /* * Assemble and Emmit one message. Separate function for each event * admit, discharge and transfer. */ void admit(MSHseg &msh, EVNseg &evn, PIDseg &pid, PV1seg &pv1) { ADT_A01msg a01; char xanbuf[21]; sprintf(xanbuf, "A%05d-%05d-%05d", (int)getpid(), ++transact_cnt, ++admit_cnt); msh.setMesConId(xanbuf); a01.setMesHea(msh); a01.setEventType(evn); a01.setPatIde(pid); a01.setPatVisit(pv1); cout << a01; } void discharge(MSHseg &msh, EVNseg &evn, PIDseg &pid, PV1seg &pv1) { ADT_A03msg a03; char xanbuf[21]; sprintf(xanbuf,"D%05d-%05d-%05d", (int)getpid(), ++transact_cnt, ++discharge_cnt); msh.setMesConId(xanbuf); a03.setMesHea(msh); a03.setEventType(evn); a03.setPatIde(pid); a03.setPatVisit(pv1); cout << a03; } void transfer(MSHseg &msh, EVNseg &evn, PIDseg &pid, PV1seg &pv1) { ADT_A02msg a02; char xanbuf[21]; sprintf(xanbuf,"T%05d-%05d-%05d", (int)getpid(), ++transact_cnt, ++discharge_cnt); msh.setMesConId(xanbuf); a02.setMesHea(msh); a02.setEventType(evn); a02.setPatIde(pid); a02.setPatVisit(pv1); cout << a02; }