Exemplo n.º 1
0
		private void TimerCallbackSendTCP(Object stateInfo) {
			List<HL7Msg> list=HL7Msgs.GetOnePending();
			for(int i=0;i<list.Count;i++) {//Right now, there will only be 0 or 1 item in the list.
				string sendMsgControlId=HL7Msgs.GetControlId(list[i]);//could be empty string
				Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
				string[] strIpPort=HL7DefEnabled.OutgoingIpPort.Split(':');//this was already validated in the HL7DefEdit window.
				IPAddress ipaddress=IPAddress.Parse(strIpPort[0]);//already validated
				int port=int.Parse(strIpPort[1]);//already validated
				IPEndPoint endpoint=new IPEndPoint(ipaddress,port);
				try {
					socket.Connect(endpoint);
					if(!IsSendTCPConnected) {//Previous run failed to connect. This time connected so make log entry that connection was successful
						EventLog.WriteEntry("OpenDentHL7","The HL7 send TCP/IP socket connection failed to connect previously and was successful this time.",EventLogEntryType.Information);
						IsSendTCPConnected=true;
					}
				}
				catch(SocketException ex) {
					if(IsSendTCPConnected) {//Previous run connected fine, make log entry and set bool to false
						EventLog.WriteEntry("OpenDentHL7","The HL7 send TCP/IP socket connection failed to connect.\r\nException: "+ex.Message,EventLogEntryType.Warning);
						IsSendTCPConnected=false;
					}
					return;
				}
				string data=MLLP_START_CHAR+list[i].MsgText+MLLP_END_CHAR+MLLP_ENDMSG_CHAR;
				try {
					byte[] byteData=Encoding.ASCII.GetBytes(data);
					socket.Send(byteData);//this is a blocking call
					//For MLLP V2, do a blocking Receive here, along with a timeout.
					byte[] ackBuffer=new byte[256];//plenty big enough to receive the entire ack/nack response
					socket.ReceiveTimeout=5000;//5 second timeout. Database is polled every 6 seconds for a new message to send, 5 second wait for acknowledgment
					int byteCountReceived=socket.Receive(ackBuffer);//blocking Receive
					char[] chars=new char[byteCountReceived];
					Encoding.UTF8.GetDecoder().GetChars(ackBuffer,0,byteCountReceived,chars,0);
					StringBuilder strbAckMsg=new StringBuilder();
					strbAckMsg.Append(chars);
					if(strbAckMsg.Length>0 && strbAckMsg[0]!=MLLP_START_CHAR) {
						list[i].Note=list[i].Note+"Malformed acknowledgment.\r\n";
						HL7Msgs.Update(list[i]);
						throw new Exception("Malformed acknowledgment.");
					}
					else if(strbAckMsg.Length>=3
						&& strbAckMsg[strbAckMsg.Length-1]==MLLP_ENDMSG_CHAR//last char is the endmsg char.
						&& strbAckMsg[strbAckMsg.Length-2]==MLLP_END_CHAR)//the second-to-the-last char is the end char.
					{
						//we have a complete message
						strbAckMsg.Remove(0,1);//strip off the start char
						strbAckMsg.Remove(strbAckMsg.Length-2,2);//strip off the end chars
					}
					else if(strbAckMsg.Length>=2//so that the next line won't crash
						&& strbAckMsg[strbAckMsg.Length-1]==MLLP_END_CHAR)//the last char is the end char.
					{
						//we will treat this as a complete message, because the endmsg char is optional.
						strbAckMsg.Remove(0,1);//strip off the start char
						strbAckMsg.Remove(strbFullMsg.Length-1,1);//strip off the end char
					}
					else {
						list[i].Note=list[i].Note+"Malformed acknowledgment.\r\n";
						HL7Msgs.Update(list[i]);
						throw new Exception("Malformed acknowledgment.");
					}
					MessageHL7 ackMsg=new MessageHL7(strbAckMsg.ToString());
					try {
						MessageParser.ProcessAck(ackMsg,IsVerboseLogging);
					}
					catch(Exception ex) {
						list[i].Note=list[i].Note+ackMsg.ToString()+"\r\nError processing acknowledgment.\r\n";
						HL7Msgs.Update(list[i]);
						throw new Exception("Error processing acknowledgment.\r\n"+ex.Message);
					}
					if(ackMsg.AckCode=="" || ackMsg.ControlId=="") {
						list[i].Note=list[i].Note+ackMsg.ToString()+"\r\nInvalid ACK message.  Attempt to resend.\r\n";
						HL7Msgs.Update(list[i]);
						throw new Exception("Invalid ACK message received.");
					}
					if(ackMsg.ControlId==sendMsgControlId && ackMsg.AckCode=="AA") {//acknowledged received (Application acknowledgment: Accept)
						list[i].Note=list[i].Note+ackMsg.ToString()+"\r\nMessage ACK (acknowledgment) received.\r\n";
					}
					else if(ackMsg.ControlId==sendMsgControlId && ackMsg.AckCode!="AA") {//ACK received for this message, but ack code was not acknowledgment accepted
						if(list[i].Note.Contains("NACK4")) {//this is the 5th negative acknowledgment, don't try again
							list[i].Note=list[i].Note+"Ack code: "+ackMsg.AckCode+"\r\nThis is NACK5, the message status has been changed to OutFailed. We will not attempt to send again.\r\n";
							list[i].HL7Status=HL7MessageStatus.OutFailed;
						}
						else if(list[i].Note.Contains("NACK")) {//failed sending at least once already
							list[i].Note=list[i].Note+"Ack code: "+ackMsg.AckCode+"\r\nNACK"+list[i].Note.Split(new string[] { "NACK" },StringSplitOptions.None).Length+"\r\n";
						}
						else {
							list[i].Note=list[i].Note+"Ack code: "+ackMsg.AckCode+"\r\nMessage NACK (negative acknowlegment) received. We will try to send again.\r\n";
						}
						HL7Msgs.Update(list[i]);//Add NACK note to hl7msg table entry
						return;//socket closed in finally
					}
					else {//ack received for control ID that does not match the control ID of message just sent
						list[i].Note=list[i].Note+"Sent message control ID: "+sendMsgControlId+"\r\nAck message control ID: "+ackMsg.ControlId
							+"\r\nAck received for message other than message just sent.  We will try to send again.\r\n";
						HL7Msgs.Update(list[i]);
						return;
					}
				}
				catch(SocketException ex) {
					EventLog.WriteEntry("OpenDentHL7","The HL7 send TCP/IP socket encountered an error when sending message or receiving acknowledgment.\r\nSocket Exception: "+ex.Message,EventLogEntryType.Warning);
					return;//Try again in 3 seconds. socket closed in finally
				}
				catch(Exception ex) {
					EventLog.WriteEntry("OpenDentHL7","The HL7 send TCP/IP encountered an error when sending message or receiving acknowledgment.\r\nException: "+ex.Message,EventLogEntryType.Warning);
					return;//socket closed in finally
				}
				finally {
					if(socket!=null) {
						socket.Shutdown(SocketShutdown.Both);
						socket.Close();
					}
				}
				list[i].HL7Status=HL7MessageStatus.OutSent;
				HL7Msgs.Update(list[i]);//set the status to sent and save ack message in Note field.
				HL7Msgs.DeleteOldMsgText();//This is inside the loop so that it happens less frequently.  To clean up incoming messages, we may move this someday.
			}
		}
Exemplo n.º 2
0
 public string GenerateMessage()
 {
     return(_msg.ToString());
 }
Exemplo n.º 3
0
		private static Appointment _aptProcessed;//return value for ProcessSeg if an appointment was processed
		//Open \\SERVERFILES\storage\OPEN DENTAL\Programmers Documents\Standards (X12, ADA, etc)\HL7\Version2.6\V26_CH02_Control_M4_JAN2007.doc
		//At the top of page 33, there are rules for the recipient.
		//Basically, they state that parsing should not fail just because there are extra unexpected items.
		//And parsing should also not fail if expected items are missing.

		public static void Process(MessageHL7 msg,bool isVerboseLogging) {
			_hl7MsgCur=new HL7Msg();
			_hl7MsgCur.HL7Status=HL7MessageStatus.InFailed;//it will be marked InProcessed once data is inserted.
			_hl7MsgCur.MsgText=msg.ToString();
			_hl7MsgCur.PatNum=0;
			_hl7MsgCur.AptNum=0;
			List<HL7Msg> listHL7Existing=HL7Msgs.GetOneExisting(_hl7MsgCur);
			if(listHL7Existing.Count>0) {//This message is already in the db
				_hl7MsgCur.HL7MsgNum=listHL7Existing[0].HL7MsgNum;
				HL7Msgs.UpdateDateTStamp(_hl7MsgCur);
				msg.ControlId=HL7Msgs.GetControlId(_hl7MsgCur);
				return;
			}
			else {
				//Insert as InFailed until processing is complete.  Update once complete, PatNum will have correct value, AptNum will have correct value if SIU message or 0 if ADT, and status changed to InProcessed
				HL7Msgs.Insert(_hl7MsgCur);
			}
			_isVerboseLogging=isVerboseLogging;
			_isNewPat=false;
			HL7Def def=HL7Defs.GetOneDeepEnabled();
			if(def==null) {
				_hl7MsgCur.Note="Could not process HL7 message.  No HL7 definition is enabled.";
				HL7Msgs.Update(_hl7MsgCur);
				throw new Exception("Could not process HL7 message.  No HL7 definition is enabled.");
			}
			if(def.InternalType==HL7InternalType.eCWFull
				|| def.InternalType==HL7InternalType.eCWTight
				|| def.InternalType==HL7InternalType.eCWStandalone)
			{
				_isEcwHL7Def=true;
			}
			HL7DefMessage hl7defmsg=null;
			for(int i=0;i<def.hl7DefMessages.Count;i++) {
				if(def.hl7DefMessages[i].MessageType==msg.MsgType && def.hl7DefMessages[i].InOrOut==InOutHL7.Incoming) {//Ignoring event type for now, we will treat all ADT's and SIU's the same
					hl7defmsg=def.hl7DefMessages[i];
					break;
				}
			}
			if(hl7defmsg==null) {//No message definition matches this message's MessageType and is Incoming
				_hl7MsgCur.Note="Could not process HL7 message.  No definition for this type of message in the enabled HL7Def.";
				HL7Msgs.Update(_hl7MsgCur);
				throw new Exception("Could not process HL7 message.  No definition for this type of message in the enabled HL7Def.");
			}
			string chartNum=null;
			long patNum=0;
			long patNumFromIds=0;
			//if we cannot locate the patient by the supplied patnum, either from the PatNum field or the list of IDs in PID.3, then we will use the external IDs to attempt to locate the pat
			//we will only trust these external IDs to return the correct patient if they all refer to a single patient
			//we may want to do some other checking to ensure that the patient referred to is the right patient?
			List<OIDExternal> listOids=new List<OIDExternal>();
			DateTime birthdate=DateTime.MinValue;
			string patLName=null;
			string patFName=null;
			#region GetPatientIDs
			#region PID segmentsLoop
			//Identify the location of the PID segment based on the message definition
			//Get patient in question, incoming messages must have a PID segment so use that to find the pat in question
			int pidOrder=-1;
			int pidDefOrder=-1;
			//get the def's PID segment order and the defined intItemOrder of the PID segment in the message
			for(int i=0;i<hl7defmsg.hl7DefSegments.Count;i++) {
				if(hl7defmsg.hl7DefSegments[i].SegmentName!=SegmentNameHL7.PID) {
					continue;
				}
				pidDefOrder=i;
				pidOrder=hl7defmsg.hl7DefSegments[i].ItemOrder;
				//we found the PID segment in the def, make sure it exists in the msg
				if(msg.Segments.Count<=pidOrder //If the number of segments in the message is less than the item order of the PID segment
					|| msg.Segments[pidOrder].GetField(0).ToString()!="PID") //Or if the segment we expect to be the PID segment is not the PID segment
				{
					_hl7MsgCur.Note="Could not process the HL7 message due to missing PID segment.";
					HL7Msgs.Update(_hl7MsgCur);
					throw new Exception("Could not process HL7 message.  Could not process the HL7 message due to missing PID segment.");
				}
				//if we get here, we've located the PID segment location within the message based on the def and it exists in the message
				break;
			}
			#endregion PID segmentsLoop
			#region PID fieldsLoop
			//Using the identified location of the PID segment, loop through the fields and find each identifier defined
			for(int f=0;f<hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields.Count;f++) {//Go through fields of PID segment and get patnum, chartnum, patient name, and/or birthdate to locate patient
				if(hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].FieldName=="pat.ChartNumber") {
					int chartNumOrdinal=hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].OrdinalPos;
					chartNum=msg.Segments[pidOrder].GetField(chartNumOrdinal).ToString();
				}
				else if(hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].FieldName=="pat.PatNum") {
					int patNumOrdinal=hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].OrdinalPos;
					try {
						patNum=PIn.Long(msg.Segments[pidOrder].GetField(patNumOrdinal).ToString());
					}
					catch(Exception ex) {
						//do nothing, patNum will remain 0
					}
				}
				else if(hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].FieldName=="pat.birthdateTime") {
					int patBdayOrdinal=hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].OrdinalPos;
					birthdate=FieldParser.DateTimeParse(msg.Segments[pidOrder].GetField(patBdayOrdinal).ToString());
				}
				else if(hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].FieldName=="pat.nameLFM") {
					int patNameOrdinal=hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].OrdinalPos;
					patLName=msg.Segments[pidOrder].GetFieldComponent(patNameOrdinal,0);
					patFName=msg.Segments[pidOrder].GetFieldComponent(patNameOrdinal,1);
				}
				#region patientIdList
				else if(hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].FieldName=="patientIds") {
					//get the id with the assigning authority equal to the internal OID root stored in their database for a patient object
					string patOIDRoot=OIDInternals.GetForType(IdentifierType.Patient).IDRoot;
					if(patOIDRoot=="") {
						//if they have not set their internal OID root, we cannot identify which repetition in this field contains the OD PatNum
						continue;
					}
					int patIdListOrdinal=hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].OrdinalPos;
					FieldHL7 fieldPatIds=msg.Segments[pidOrder].GetField(patIdListOrdinal);
					//Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&OIDType^MR|
					//field component values will be the first repetition, repeats will be in field.ListRepeatFields
					//field 4 is an HD data type, which is composed of 3 subcomponents separated by the "&".
					//subcomponent 1 is the universal ID for the assigning authority
					//subcomponent 2 is the universal ID type for the assigning authority
					char subcompSeparator='&';
					if(msg.Delimiters.Length>3) {
						subcompSeparator=msg.Delimiters[3];
					}
					string[] arrayPatIdSubComps=fieldPatIds.GetComponentVal(3).Split(new char[] { subcompSeparator },StringSplitOptions.None);
					if(arrayPatIdSubComps.Length>1//there must be at least 2 sub-components in the assigning authority component so we can identify whose ID this is
						&& fieldPatIds.GetComponentVal(4).ToLower()=="pi")//PI=patient internal identifier; a number that is unique to a patient within an assigning authority
					{
						int checkDigit=-1;
						try {
							checkDigit=PIn.Int(fieldPatIds.GetComponentVal(1));
						}
						catch(Exception ex) {
							//checkDigit will remain -1
						}
						//if using the M10 or M11 check digit algorithm and it passes the respective test, or not using either algorithm, then use the ID
						if((fieldPatIds.GetComponentVal(2).ToLower()=="m10"
								&& checkDigit!=-1
								&& M10CheckDigit(fieldPatIds.GetComponentVal(0))==checkDigit)//using M10 scheme and the check digit is valid and matches calc
							|| (fieldPatIds.GetComponentVal(2).ToLower()=="m11"
								&& checkDigit!=-1
								&& M11CheckDigit(fieldPatIds.GetComponentVal(0))==checkDigit)//using M11 scheme and the check digit is valid and matches calc
							|| (fieldPatIds.GetComponentVal(2).ToLower()!="m10"
								&& fieldPatIds.GetComponentVal(2).ToLower()!="m11"))//not using either check digit scheme
						{
							if(arrayPatIdSubComps[1].ToLower()==patOIDRoot.ToLower()) {
								try {
									patNumFromIds=PIn.Long(fieldPatIds.GetComponentVal(0));
								}
								catch(Exception ex) {
									//do nothing, patNumFromList will remain 0
								}
							}
							else {
								OIDExternal oidCur=new OIDExternal();
								oidCur.IDType=IdentifierType.Patient;
								oidCur.IDExternal=fieldPatIds.GetComponentVal(0);
								oidCur.rootExternal=arrayPatIdSubComps[1];
								listOids.Add(oidCur);
							}
						}
					}
					//patNumFromList will be 0 if the first repetition is not the OD patient id or if the check digit is incorrect based on the specified algorithm
					if(patNumFromIds!=0) {
						continue;
					}
					for(int r=0;r<fieldPatIds.ListRepeatFields.Count;r++) {
						arrayPatIdSubComps=fieldPatIds.ListRepeatFields[r].GetComponentVal(3).ToLower().Split(new char[] { subcompSeparator },StringSplitOptions.None);
						if(arrayPatIdSubComps.Length<2) {//there must be at least 2 sub-components in the assigning authority component so we can identify whose ID this is
							continue;
						}
						if(fieldPatIds.ListRepeatFields[r].GetComponentVal(4).ToLower()!="pi")
						{
							continue;
						}
						int checkDigit=-1;
						try {
							checkDigit=PIn.Int(fieldPatIds.ListRepeatFields[r].GetComponentVal(1));
						}
						catch(Exception ex) {
							//checkDigit will remain -1
						}
						if(fieldPatIds.ListRepeatFields[r].GetComponentVal(2).ToLower()=="m10"
						&& (checkDigit==-1 || M10CheckDigit(fieldPatIds.ListRepeatFields[r].GetComponentVal(0))!=checkDigit))//using M10 scheme and either invalid check digit or doesn't match calc
						{
							continue;
						}
						if(fieldPatIds.ListRepeatFields[r].GetComponentVal(2).ToLower()=="m11"
						&& (checkDigit==-1 || M11CheckDigit(fieldPatIds.ListRepeatFields[r].GetComponentVal(0))!=checkDigit))//using M11 scheme and either invalid check digit or doesn't match calc
						{
							continue;
						}
						//if not using the M10 or M11 check digit scheme or if the check digit is good, trust the ID in component 0 to be valid and attempt to use
						if(arrayPatIdSubComps[1]==patOIDRoot.ToLower()) {
							if(patNumFromIds==0) {
								try {
									patNumFromIds=PIn.Long(fieldPatIds.ListRepeatFields[r].GetComponentVal(0));
								}
								catch(Exception ex) {
									//do nothing, patNumFromList will remain 0
								}
							}
						}
						else {
							OIDExternal oidCur=new OIDExternal();
							oidCur.IDType=IdentifierType.Patient;
							oidCur.IDExternal=fieldPatIds.ListRepeatFields[r].GetComponentVal(0);
							oidCur.rootExternal=arrayPatIdSubComps[1];
							listOids.Add(oidCur);
						}
					}
				}
				#endregion patientIdList
			}
			if(_isEcwHL7Def &&  (patLName=="" || patFName=="")) {
				EventLog.WriteEntry("OpenDentHL7","Message not processed due to missing first or last name. PatNum:"+patNum.ToString()
					,EventLogEntryType.Information);
				_hl7MsgCur.Note="Message not processed due to missing first or last name. PatNum:"+patNum.ToString();
				HL7Msgs.Update(_hl7MsgCur);
				return;
			}
			#endregion PID fieldsLoop
			#endregion GetPatientIDs
			//We now have patnum, chartnum, patname, and/or birthdate so locate pat
			Patient pat=null;
			Patient patOld=null;
			//pat will be null if patNum==0
			pat=Patients.GetPat(patNum);
			if(pat==null && patNumFromIds>0) {
				pat=Patients.GetPat(patNumFromIds);
			}
			//If ChartNumber is a field in their defined PID segment, and they are not using eCWTight or Full internal type
			//Use the ChartNumber followed by Name and Birthdate to try to locate the patient for this message if chartNum is not null
			if(def.InternalType!=HL7InternalType.eCWFull && def.InternalType!=HL7InternalType.eCWTight && pat==null && chartNum!=null) {
				pat=Patients.GetPatByChartNumber(chartNum);
				//If not using eCWTight or Full integration, if pat is still null we need to try to locate patient by name and birthdate
				if(pat==null) {
					long patNumByName=Patients.GetPatNumByNameAndBirthday(patLName,patFName,birthdate);
					if(patNumByName>0) {
						pat=Patients.GetPat(patNumByName);
					}
				}
				if(pat!=null) {
					patOld=pat.Copy();
					pat.ChartNumber=chartNum;//from now on, we will be able to find pat by chartNumber
					Patients.Update(pat,patOld);
					if(_isVerboseLogging) {
						EventLog.WriteEntry("OpenDentHL7","Updated patient "+pat.GetNameFLnoPref()+" to include ChartNumber.",EventLogEntryType.Information);
					}
				}
			}
			//Use the external OIDs stored in the oidexternal table to find the patient
			//Only trust the external IDs if all OIDs refer to the same patient
			long patNumFromExtIds=0;
			if(pat==null) {
				for(int i=0;i<listOids.Count;i++) {
					OIDExternal oidExternalCur=OIDExternals.GetByRootAndExtension(listOids[i].rootExternal,listOids[i].IDExternal);
					if(oidExternalCur==null || oidExternalCur.IDInternal==0 || oidExternalCur.IDType!=IdentifierType.Patient) {//must have an IDType of patient
						continue;
					}
					if(patNumFromExtIds==0) {
						patNumFromExtIds=oidExternalCur.IDInternal;
					}
					else if(patNumFromExtIds!=oidExternalCur.IDInternal) {//the current ID refers to a different patient than a previously found ID, don't trust the external IDs
						patNumFromExtIds=0;
						break;
					}
				}
				if(patNumFromExtIds>0) {//will be 0 if not in the OIDExternal table or no external IDs supplied or more than one supplied and they point to different pats (ambiguous)
					pat=Patients.GetPat(patNumFromExtIds);
				}
			}
			_isNewPat=pat==null;
			if(!_isEcwHL7Def && msg.MsgType==MessageTypeHL7.SRM && _isNewPat) {//SRM messages must refer to existing appointments, so there must be an existing patient as well
				MessageHL7 hl7SRR=MessageConstructor.GenerateSRR(pat,null,msg.EventType,msg.ControlId,false,msg.AckEvent);//use false to indicate AE - Application Error in SRR.MSA segment
				HL7Msg hl7Msg=new HL7Msg();
				hl7Msg.HL7Status=HL7MessageStatus.OutPending;//it will be marked outSent by the HL7 service.
				hl7Msg.MsgText=hl7SRR.ToString();
				hl7Msg.Note="Could not process HL7 SRM message due to an invalid or missing patient ID in the PID segment.";
				HL7Msgs.Insert(hl7Msg);
				throw new Exception("Could not process HL7 SRM message due to an invalid or missing patient ID in the PID segment.");
			}
			if(_isNewPat) {
				pat=new Patient();
				if(chartNum!=null) {
					pat.ChartNumber=chartNum;
				}
				if(patNum!=0) {
					pat.PatNum=patNum;
					pat.Guarantor=patNum;
				}
				else if(patNumFromIds!=0) {
					pat.PatNum=patNumFromIds;
					pat.Guarantor=patNumFromIds;
				}
				else if(patNumFromExtIds!=0) {
					pat.PatNum=patNumFromExtIds;
					pat.Guarantor=patNumFromExtIds;
				}
				pat.PriProv=PrefC.GetLong(PrefName.PracticeDefaultProv);
				pat.BillingType=PrefC.GetLong(PrefName.PracticeDefaultBillType);
			}
			else {
				patOld=pat.Copy();
			}
			long aptNum=0;
			//If this is a message that contains an ARQ or SCH segment, loop through the fields to find the AptNum.  Pass it to other segment parsing methods that require it.
			//If this is an SRM message, and an AptNum is not included or no appointment with this AptNum is in the OD db, do not process the message.
			//We only accept SRM messages for appointments that already exist in the OD db.
			for(int i=0;i<hl7defmsg.hl7DefSegments.Count;i++) {
				if(hl7defmsg.hl7DefSegments[i].SegmentName!=SegmentNameHL7.SCH//SIU messages will have the SCH segment, used for eCW or other interfaces where OD is an auxiliary application
					&& hl7defmsg.hl7DefSegments[i].SegmentName!=SegmentNameHL7.ARQ)//SRM messages will have the ARQ segment, used for interfaces where OD is the filler application
				{
					continue;
				}
				//we found the SCH or ARQ segment
				int segOrder=hl7defmsg.hl7DefSegments[i].ItemOrder;
				for(int j=0;j<hl7defmsg.hl7DefSegments[i].hl7DefFields.Count;j++) {//Go through fields of SCH or ARQ segment and get AptNum
					if(hl7defmsg.hl7DefSegments[i].hl7DefFields[j].FieldName=="apt.AptNum") {
						int aptNumOrdinal=hl7defmsg.hl7DefSegments[i].hl7DefFields[j].OrdinalPos;
						try {
							aptNum=PIn.Long(msg.Segments[segOrder].GetFieldComponent(aptNumOrdinal,0).ToString());
						}
						catch(Exception ex) {//PIn.Long will throw an exception if a value is not able to be parsed into a long
							//do nothing, aptNum will remain 0
						}
						break;
					}
				}
				if(aptNum>0) {
					break;
				}
			}
			Appointment aptCur=Appointments.GetOneApt(aptNum);//if aptNum=0, aptCur will be null
			//SRM messages are only for interfaces where OD is considered the 'filler' application
			//Not valid for eCW where OD is considered an 'auxiliary' application and we receive SIU messages instead.
			if(!_isEcwHL7Def && msg.MsgType==MessageTypeHL7.SRM) {
				MessageHL7 hl7Srr=null;
				HL7Msg hl7Msg=null;
				if(aptCur==null) {
					hl7Srr=MessageConstructor.GenerateSRR(pat,aptCur,msg.EventType,msg.ControlId,false,msg.AckEvent);//use false to indicate AE - Application Error in SRR.MSA segment
					hl7Msg=new HL7Msg();
					hl7Msg.HL7Status=HL7MessageStatus.OutPending;//it will be marked outSent by the HL7 service.
					hl7Msg.MsgText=hl7Srr.ToString();
					hl7Msg.PatNum=pat.PatNum;
					hl7Msg.Note="Could not process HL7 SRM message due to an invalid or missing appointment ID in the ARQ segment.  Appointment ID attempted: "+aptNum.ToString();
					HL7Msgs.Insert(hl7Msg);
					throw new Exception("Could not process HL7 SRM message due to an invalid or missing appointment ID in the ARQ segment.  Appointment ID attempted: "+aptNum.ToString());
				}
				if(pat.PatNum!=aptCur.PatNum) {//an SRM must refer to a valid appt, therefore the patient cannot be new and must have a PatNum
					hl7Srr=MessageConstructor.GenerateSRR(pat,aptCur,msg.EventType,msg.ControlId,false,msg.AckEvent);//use false to indicate AE - Application Error in SRR.MSA segment
					hl7Msg=new HL7Msg();
					hl7Msg.AptNum=aptCur.AptNum;
					hl7Msg.HL7Status=HL7MessageStatus.OutPending;//it will be marked outSent by the HL7 service.
					hl7Msg.MsgText=hl7Srr.ToString();
					hl7Msg.PatNum=pat.PatNum;
					hl7Msg.Note="Could not process HL7 SRM message.\r\n"
						+"The patient identified in the PID segment is not the same as the patient on the appointment identified in the ARQ segment.\r\n"
						+"Appointment PatNum: "+aptCur.PatNum.ToString()+".  PID segment PatNum: "+pat.PatNum.ToString()+".";
					HL7Msgs.Insert(hl7Msg);
					throw new Exception("Could not process HL7 SRM message.\r\n"
						+"The patient identified in the PID segment is not the same as the patient on the appointment identified in the ARQ segment.\r\n"
						+"Appointment PatNum: "+aptCur.PatNum.ToString()+".  PID segment PatNum: "+pat.PatNum.ToString()+".");
				}
			}
			//We now have a patient object , either loaded from the db or new, and an appointment (could be null) so process this message for this patient
			//We need to insert the pat to get a patnum so we can compare to guar patnum to see if relationship to guar is self
			if(_isNewPat) {
				if(pat.PatNum==0) {//Only eCWTight or eCWFull internal types will allow the HL7 message to dictate our PatNums.
					pat.PatNum=Patients.Insert(pat,false);
				}
				else {
					pat.PatNum=Patients.Insert(pat,true);
				}
				if(_isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Inserted patient "+pat.GetNameFLnoPref(),EventLogEntryType.Information);
				}
				patOld=pat.Copy();
			}
			//Update hl7msg table with correct PatNum for this message
			_hl7MsgCur.PatNum=pat.PatNum;
			HL7Msgs.Update(_hl7MsgCur);
			if(aptCur!=null && pat.PatNum!=aptCur.PatNum) {
				throw new Exception("Appointment does not match patient "+pat.GetNameFLnoPref()+", apt.PatNum: "+aptCur.PatNum.ToString()+", pat.PatNum: "+pat.PatNum.ToString());
			}
			for(int i=0;i<hl7defmsg.hl7DefSegments.Count;i++) {
				try {
					List<SegmentHL7> listSegments=new List<SegmentHL7>();
					if(hl7defmsg.hl7DefSegments[i].CanRepeat) {
						listSegments=msg.GetSegments(hl7defmsg.hl7DefSegments[i].SegmentName,!hl7defmsg.hl7DefSegments[i].IsOptional);
					}
					else {
						SegmentHL7 seg=msg.GetSegment(hl7defmsg.hl7DefSegments[i].SegmentName,!hl7defmsg.hl7DefSegments[i].IsOptional);
						if(seg==null) {//null if segment was not found but is optional
							continue;
						}
						listSegments.Add(seg);
					}
					for(int j=0;j<listSegments.Count;j++) {//normally only 1 or 0 in the list, but if it is a repeatable segment the list may contain multiple segments to process
						if(_isVerboseLogging) {
							EventLog.WriteEntry("OpenDentHL7","Process segment "+hl7defmsg.hl7DefSegments[i].SegmentName.ToString(),EventLogEntryType.Information);
						}
						ProcessSeg(pat,aptCur,hl7defmsg.hl7DefSegments[i],listSegments[j],msg);
						if(hl7defmsg.hl7DefSegments[i].SegmentName==SegmentNameHL7.SCH) {
							aptCur=_aptProcessed;
						}
					}
				}
				catch(ApplicationException ex) {//Required segment was missing, or other error.
					_hl7MsgCur.Note="Could not process this HL7 message.  "+ex;
					HL7Msgs.Update(_hl7MsgCur);
					if(!_isEcwHL7Def && msg.MsgType==MessageTypeHL7.SRM) {//SRM messages require sending an SRR response, this will be with Ack Code AE - Application Error
						MessageHL7 hl7Srr=MessageConstructor.GenerateSRR(pat,aptCur,msg.EventType,msg.ControlId,false,msg.AckEvent);//use false to indicate AE - Application Error in SRR.MSA segment
						HL7Msg hl7Msg=new HL7Msg();
						hl7Msg.AptNum=aptCur.AptNum;
						hl7Msg.HL7Status=HL7MessageStatus.OutPending;//it will be marked outSent by the HL7 service.
						hl7Msg.MsgText=hl7Srr.ToString();
						hl7Msg.Note="Could not process an HL7 SRM message.  Send SRR for the request.  "+ex;
						hl7Msg.PatNum=pat.PatNum;
						HL7Msgs.Insert(hl7Msg);
					}
					throw new Exception("Could not process an HL7 message.  "+ex);
				}
			}
			//We have processed the message so now update the patient
			if(pat.FName=="" || pat.LName=="") {
				EventLog.WriteEntry("OpenDentHL7","Patient demographics not processed due to missing first or last name. PatNum:"+pat.PatNum.ToString()
					,EventLogEntryType.Information);
				_hl7MsgCur.Note="Patient demographics not processed due to missing first or last name. PatNum:"+pat.PatNum.ToString();
				HL7Msgs.Update(_hl7MsgCur);
			}
			else {
				if(_isNewPat && pat.Guarantor==0) {
					pat.Guarantor=pat.PatNum;
				}
				Patients.Update(pat,patOld);
				if(_isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Updated patient "+pat.GetNameFLnoPref(),EventLogEntryType.Information);
				}
				_hl7MsgCur.HL7Status=HL7MessageStatus.InProcessed;
				HL7Msgs.Update(_hl7MsgCur);
			}
			//Schedule Request Messages require a Schedule Request Response if the data requested to be changed was successful
			//We only allow changing the appt note, setting the dentist and hygienist, updating the confirmation status, and changing the ClinicNum.
			//We also allow setting the appt status to broken if the EventType is S04 - Request Appointment Cancellation.
			//We will generate the SRR if we get here, since we will have processed the message properly.
			//The SRM will be ACK'd after returning from this Process method in ServiceHL7.
			//Our SRR will also be ACK'd by the receiving software.
			if(!_isEcwHL7Def && msg.MsgType==MessageTypeHL7.SRM) {
				if(msg.AckEvent=="S04") {
					Appointment aptOld=aptCur.Clone();
					aptCur.AptStatus=ApptStatus.Broken;
					Appointments.Update(aptCur,aptOld);
					if(_isVerboseLogging) {
						EventLog.WriteEntry("OpenDentHL7","Appointment broken due to inbound SRM message with event type S04 for patient "+pat.GetNameFLnoPref(),EventLogEntryType.Information);
					}
				}
				MessageHL7 hl7Srr=MessageConstructor.GenerateSRR(pat,aptCur,msg.EventType,msg.ControlId,true,msg.AckEvent);
				HL7Msg hl7Msg=new HL7Msg();
				hl7Msg.AptNum=aptCur.AptNum;
				hl7Msg.HL7Status=HL7MessageStatus.OutPending;//it will be marked outSent by the HL7 service.
				hl7Msg.MsgText=hl7Srr.ToString();
				hl7Msg.PatNum=pat.PatNum;
				HL7Msgs.Insert(hl7Msg);
			}
		}
Exemplo n.º 4
0
        //Open \\SERVERFILES\storage\OPEN DENTAL\Programmers Documents\Standards (X12, ADA, etc)\HL7\Version2.6\V26_CH02_Control_M4_JAN2007.doc
        //At the top of page 33, there are rules for the recipient.
        //Basically, they state that parsing should not fail just because there are extra unexpected items.
        //And parsing should also not fail if expected items are missing.

        public static void Process(MessageHL7 msg, bool isVerboseLogging)
        {
            HL7MsgCur           = new HL7Msg();
            HL7MsgCur.HL7Status = HL7MessageStatus.InFailed;          //it will be marked InProcessed once data is inserted.
            HL7MsgCur.MsgText   = msg.ToString();
            HL7MsgCur.PatNum    = 0;
            HL7MsgCur.AptNum    = 0;
            List <HL7Msg> hl7Existing = HL7Msgs.GetOneExisting(HL7MsgCur);

            if (hl7Existing.Count > 0)           //This message is already in the db
            {
                HL7MsgCur.HL7MsgNum = hl7Existing[0].HL7MsgNum;
                HL7Msgs.UpdateDateTStamp(HL7MsgCur);
                msg.ControlId = HL7Msgs.GetControlId(HL7MsgCur);
                return;
            }
            else
            {
                //Insert as InFailed until processing is complete.  Update once complete, PatNum will have correct value, AptNum will have correct value if SIU message or 0 if ADT, and status changed to InProcessed
                HL7Msgs.Insert(HL7MsgCur);
            }
            IsVerboseLogging = isVerboseLogging;
            IsNewPat         = false;
            HL7Def def = HL7Defs.GetOneDeepEnabled();

            if (def == null)
            {
                HL7MsgCur.Note = "Could not process HL7 message.  No HL7 definition is enabled.";
                HL7Msgs.Update(HL7MsgCur);
                throw new Exception("Could not process HL7 message.  No HL7 definition is enabled.");
            }
            HL7DefMessage hl7defmsg = null;

            for (int i = 0; i < def.hl7DefMessages.Count; i++)
            {
                if (def.hl7DefMessages[i].MessageType == msg.MsgType)               //&& def.hl7DefMessages[i].EventType==msg.EventType) { //Ignoring event type for now, we will treat all ADT's and SIU's the same
                {
                    hl7defmsg = def.hl7DefMessages[i];
                }
            }
            if (hl7defmsg == null)           //No message definition matches this message's MessageType and EventType
            {
                HL7MsgCur.Note = "Could not process HL7 message.  No definition for this type of message in the enabled HL7Def.";
                HL7Msgs.Update(HL7MsgCur);
                throw new Exception("Could not process HL7 message.  No definition for this type of message in the enabled HL7Def.");
            }
            string   chartNum      = null;
            long     patNum        = 0;
            DateTime birthdate     = DateTime.MinValue;
            string   patLName      = null;
            string   patFName      = null;
            bool     isExistingPID = false;      //Needed to add note to hl7msg table if there isn't a PID segment in the message.

            //Get patient in question, incoming messages must have a PID segment so use that to find the pat in question
            for (int s = 0; s < hl7defmsg.hl7DefSegments.Count; s++)
            {
                if (hl7defmsg.hl7DefSegments[s].SegmentName != SegmentNameHL7.PID)
                {
                    continue;
                }
                int pidOrder = hl7defmsg.hl7DefSegments[s].ItemOrder;
                //we found the PID segment in the def, make sure it exists in the msg
                if (msg.Segments.Count <= pidOrder ||           //If the number of segments in the message is less than the item order of the PID segment
                    msg.Segments[pidOrder].GetField(0).ToString() != "PID"                      //Or if the segment we expect to be the PID segment is not the PID segment
                    )
                {
                    break;
                }
                isExistingPID = true;
                for (int f = 0; f < hl7defmsg.hl7DefSegments[s].hl7DefFields.Count; f++)           //Go through fields of PID segment and get patnum, chartnum, patient name, and/or birthdate to locate patient
                {
                    if (hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName == "pat.ChartNumber")
                    {
                        int chartNumOrdinal = hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos;
                        chartNum = msg.Segments[pidOrder].Fields[chartNumOrdinal].ToString();
                    }
                    else if (hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName == "pat.PatNum")
                    {
                        int patNumOrdinal = hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos;
                        patNum = PIn.Long(msg.Segments[pidOrder].Fields[patNumOrdinal].ToString());
                    }
                    else if (hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName == "pat.birthdateTime")
                    {
                        int patBdayOrdinal = hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos;
                        birthdate = FieldParser.DateTimeParse(msg.Segments[pidOrder].Fields[patBdayOrdinal].ToString());
                    }
                    else if (hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName == "pat.nameLFM")
                    {
                        int patNameOrdinal = hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos;
                        patLName = msg.Segments[pidOrder].GetFieldComponent(patNameOrdinal, 0);
                        patFName = msg.Segments[pidOrder].GetFieldComponent(patNameOrdinal, 1);
                    }
                }
            }
            if (!isExistingPID)
            {
                HL7MsgCur.Note = "Could not process the HL7 message due to missing PID segment.";
                HL7Msgs.Update(HL7MsgCur);
                throw new Exception("Could not process HL7 message.  Could not process the HL7 message due to missing PID segment.");
            }
            //We now have patnum, chartnum, patname, and/or birthdate so locate pat
            Patient pat    = null;
            Patient patOld = null;

            if (patNum != 0)
            {
                pat = Patients.GetPat(patNum);
            }
            if (def.InternalType != "eCWStandalone" && pat == null)
            {
                IsNewPat = true;
            }
            //In eCWstandalone integration, if we couldn't locate patient by patNum or patNum was 0 then pat will still be null so try to locate by chartNum if chartNum is not null
            if (def.InternalType == "eCWStandalone" && chartNum != null)
            {
                pat = Patients.GetPatByChartNumber(chartNum);
            }
            //In eCWstandalone integration, if pat is still null we need to try to locate patient by name and birthdate
            if (def.InternalType == "eCWStandalone" && pat == null)
            {
                long patNumByName = Patients.GetPatNumByNameAndBirthday(patLName, patFName, birthdate);
                //If patNumByName is 0 we couldn't locate by patNum, chartNum or name and birthdate so this message must be for a new patient
                if (patNumByName == 0)
                {
                    IsNewPat = true;
                }
                else
                {
                    pat             = Patients.GetPat(patNumByName);
                    patOld          = pat.Copy();
                    pat.ChartNumber = chartNum;                  //from now on, we will be able to find pat by chartNumber
                    Patients.Update(pat, patOld);
                }
            }
            if (IsNewPat)
            {
                pat = new Patient();
                if (chartNum != null)
                {
                    pat.ChartNumber = chartNum;
                }
                if (patNum != 0)
                {
                    pat.PatNum    = patNum;
                    pat.Guarantor = patNum;
                }
                pat.PriProv     = PrefC.GetLong(PrefName.PracticeDefaultProv);
                pat.BillingType = PrefC.GetLong(PrefName.PracticeDefaultBillType);
            }
            else
            {
                patOld = pat.Copy();
            }
            //Update hl7msg table with correct PatNum for this message
            HL7MsgCur.PatNum = pat.PatNum;
            HL7Msgs.Update(HL7MsgCur);
            //If this is a message that contains an SCH segment, loop through to find the AptNum.  Pass it to the other segments that will need it.
            long aptNum = 0;

            for (int s = 0; s < hl7defmsg.hl7DefSegments.Count; s++)
            {
                if (hl7defmsg.hl7DefSegments[s].SegmentName != SegmentNameHL7.SCH)
                {
                    continue;
                }
                //we found the SCH segment
                int schOrder = hl7defmsg.hl7DefSegments[s].ItemOrder;
                for (int f = 0; f < hl7defmsg.hl7DefSegments[s].hl7DefFields.Count; f++)           //Go through fields of SCH segment and get AptNum
                {
                    if (hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName == "apt.AptNum")
                    {
                        int aptNumOrdinal = hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos;
                        aptNum = PIn.Long(msg.Segments[schOrder].Fields[aptNumOrdinal].ToString());
                    }
                }
            }
            //We now have a patient object , either loaded from the db or new, and aptNum so process this message for this patient
            //We need to insert the pat to get a patnum so we can compare to guar patnum to see if relationship to guar is self
            if (IsNewPat)
            {
                if (isVerboseLogging)
                {
                    EventLog.WriteEntry("OpenDentHL7", "Inserted patient: " + pat.FName + " " + pat.LName, EventLogEntryType.Information);
                }
                if (pat.PatNum == 0)
                {
                    pat.PatNum = Patients.Insert(pat, false);
                }
                else
                {
                    pat.PatNum = Patients.Insert(pat, true);
                }
                patOld = pat.Copy();
            }
            for (int i = 0; i < hl7defmsg.hl7DefSegments.Count; i++)
            {
                try {
                    SegmentHL7 seg = msg.GetSegment(hl7defmsg.hl7DefSegments[i].SegmentName, !hl7defmsg.hl7DefSegments[i].IsOptional);
                    if (seg != null)                   //null if segment was not found but is optional
                    {
                        ProcessSeg(pat, aptNum, hl7defmsg.hl7DefSegments[i], seg, msg);
                    }
                }
                catch (ApplicationException ex) {               //Required segment was missing, or other error.
                    HL7MsgCur.Note = "Could not process this HL7 message.  " + ex;
                    HL7Msgs.Update(HL7MsgCur);
                    throw new Exception("Could not process HL7 message.  " + ex);
                }
            }
            //We have processed the message so now update or insert the patient
            if (pat.FName == "" || pat.LName == "")
            {
                EventLog.WriteEntry("OpenDentHL7", "Patient demographics not processed due to missing first or last name. PatNum:" + pat.PatNum.ToString()
                                    , EventLogEntryType.Information);
                HL7MsgCur.Note = "Patient demographics not processed due to missing first or last name. PatNum:" + pat.PatNum.ToString();
                HL7Msgs.Update(HL7MsgCur);
                return;
            }
            if (IsNewPat)
            {
                if (pat.Guarantor == 0)
                {
                    pat.Guarantor = pat.PatNum;
                    Patients.Update(pat, patOld);
                }
                else
                {
                    Patients.Update(pat, patOld);
                }
            }
            else
            {
                if (isVerboseLogging)
                {
                    EventLog.WriteEntry("OpenDentHL7", "Updated patient: " + pat.FName + " " + pat.LName, EventLogEntryType.Information);
                }
                Patients.Update(pat, patOld);
            }
            HL7MsgCur.HL7Status = HL7MessageStatus.InProcessed;
            HL7Msgs.Update(HL7MsgCur);
        }
Exemplo n.º 5
0
		//Open \\SERVERFILES\storage\OPEN DENTAL\Programmers Documents\Standards (X12, ADA, etc)\HL7\Version2.6\V26_CH02_Control_M4_JAN2007.doc
		//At the top of page 33, there are rules for the recipient.
		//Basically, they state that parsing should not fail just because there are extra unexpected items.
		//And parsing should also not fail if expected items are missing.

		public static void Process(MessageHL7 msg,bool isVerboseLogging) {
			HL7MsgCur=new HL7Msg();
			HL7MsgCur.HL7Status=HL7MessageStatus.InFailed;//it will be marked InProcessed once data is inserted.
			HL7MsgCur.MsgText=msg.ToString();
			HL7MsgCur.PatNum=0;
			HL7MsgCur.AptNum=0;
			List<HL7Msg> hl7Existing=HL7Msgs.GetOneExisting(HL7MsgCur);
			if(hl7Existing.Count>0) {//This message is already in the db
				HL7MsgCur.HL7MsgNum=hl7Existing[0].HL7MsgNum;
				HL7Msgs.UpdateDateTStamp(HL7MsgCur);
				msg.ControlId=HL7Msgs.GetControlId(HL7MsgCur);
				return;
			}
			else {
				//Insert as InFailed until processing is complete.  Update once complete, PatNum will have correct value, AptNum will have correct value if SIU message or 0 if ADT, and status changed to InProcessed
				HL7Msgs.Insert(HL7MsgCur);
			}
			IsVerboseLogging=isVerboseLogging;
			IsNewPat=false;
			HL7Def def=HL7Defs.GetOneDeepEnabled();
			if(def==null) {
				HL7MsgCur.Note="Could not process HL7 message.  No HL7 definition is enabled.";
				HL7Msgs.Update(HL7MsgCur);
				throw new Exception("Could not process HL7 message.  No HL7 definition is enabled.");
			}
			HL7DefMessage hl7defmsg=null;
			for(int i=0;i<def.hl7DefMessages.Count;i++) {
				if(def.hl7DefMessages[i].MessageType==msg.MsgType) {//&& def.hl7DefMessages[i].EventType==msg.EventType) { //Ignoring event type for now, we will treat all ADT's and SIU's the same
					hl7defmsg=def.hl7DefMessages[i];
				}
			}
			if(hl7defmsg==null) {//No message definition matches this message's MessageType and EventType
				HL7MsgCur.Note="Could not process HL7 message.  No definition for this type of message in the enabled HL7Def.";
				HL7Msgs.Update(HL7MsgCur);
				throw new Exception("Could not process HL7 message.  No definition for this type of message in the enabled HL7Def.");
			}
			string chartNum=null;
			long patNum=0;
			DateTime birthdate=DateTime.MinValue;
			string patLName=null;
			string patFName=null;
			bool isExistingPID=false;//Needed to add note to hl7msg table if there isn't a PID segment in the message.
			//Get patient in question, incoming messages must have a PID segment so use that to find the pat in question
			for(int s=0;s<hl7defmsg.hl7DefSegments.Count;s++) {
				if(hl7defmsg.hl7DefSegments[s].SegmentName!=SegmentNameHL7.PID) {
					continue;
				}
				int pidOrder=hl7defmsg.hl7DefSegments[s].ItemOrder;
				//we found the PID segment in the def, make sure it exists in the msg
				if(msg.Segments.Count<=pidOrder //If the number of segments in the message is less than the item order of the PID segment
					|| msg.Segments[pidOrder].GetField(0).ToString()!="PID" //Or if the segment we expect to be the PID segment is not the PID segment
				) {
					break;
				}
				isExistingPID=true;
				for(int f=0;f<hl7defmsg.hl7DefSegments[s].hl7DefFields.Count;f++) {//Go through fields of PID segment and get patnum, chartnum, patient name, and/or birthdate to locate patient
					if(hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName=="pat.ChartNumber") {
						int chartNumOrdinal=hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos;
						chartNum=msg.Segments[pidOrder].Fields[chartNumOrdinal].ToString();
					}
					else if(hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName=="pat.PatNum") {
						int patNumOrdinal=hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos;
						patNum=PIn.Long(msg.Segments[pidOrder].Fields[patNumOrdinal].ToString());
					}
					else if(hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName=="pat.birthdateTime") {
						int patBdayOrdinal=hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos;
						birthdate=FieldParser.DateTimeParse(msg.Segments[pidOrder].Fields[patBdayOrdinal].ToString());
					}
					else if(hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName=="pat.nameLFM") {
						int patNameOrdinal=hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos;
						patLName=msg.Segments[pidOrder].GetFieldComponent(patNameOrdinal,0);
						patFName=msg.Segments[pidOrder].GetFieldComponent(patNameOrdinal,1);
					}
				}
			}
			if(!isExistingPID) {
				HL7MsgCur.Note="Could not process the HL7 message due to missing PID segment.";
				HL7Msgs.Update(HL7MsgCur);
				throw new Exception("Could not process HL7 message.  Could not process the HL7 message due to missing PID segment.");
			}
			//We now have patnum, chartnum, patname, and/or birthdate so locate pat
			Patient pat=null;
			Patient patOld=null;
			if(patNum!=0) {
				pat=Patients.GetPat(patNum);
			}
			if(def.InternalType!="eCWStandalone" && pat==null) {
				IsNewPat=true;
			}
			//In eCWstandalone integration, if we couldn't locate patient by patNum or patNum was 0 then pat will still be null so try to locate by chartNum if chartNum is not null
			if(def.InternalType=="eCWStandalone" && chartNum!=null) {
				pat=Patients.GetPatByChartNumber(chartNum);
			}
			//In eCWstandalone integration, if pat is still null we need to try to locate patient by name and birthdate
			if(def.InternalType=="eCWStandalone" && pat==null) {
				long patNumByName=Patients.GetPatNumByNameAndBirthday(patLName,patFName,birthdate);
				//If patNumByName is 0 we couldn't locate by patNum, chartNum or name and birthdate so this message must be for a new patient
				if(patNumByName==0) {
					IsNewPat=true;
				}
				else {
					pat=Patients.GetPat(patNumByName);
					patOld=pat.Copy();
					pat.ChartNumber=chartNum;//from now on, we will be able to find pat by chartNumber
					Patients.Update(pat,patOld);
				}
			}
			if(IsNewPat) {
				pat=new Patient();
				if(chartNum!=null) {
					pat.ChartNumber=chartNum;
				}
				if(patNum!=0) {
					pat.PatNum=patNum;
					pat.Guarantor=patNum;
				}
				pat.PriProv=PrefC.GetLong(PrefName.PracticeDefaultProv);
				pat.BillingType=PrefC.GetLong(PrefName.PracticeDefaultBillType);
			}
			else {
				patOld=pat.Copy();
			}
			//Update hl7msg table with correct PatNum for this message
			HL7MsgCur.PatNum=pat.PatNum;
			HL7Msgs.Update(HL7MsgCur);
			//If this is a message that contains an SCH segment, loop through to find the AptNum.  Pass it to the other segments that will need it.
			long aptNum=0;
			for(int s=0;s<hl7defmsg.hl7DefSegments.Count;s++) {
				if(hl7defmsg.hl7DefSegments[s].SegmentName!=SegmentNameHL7.SCH) {
					continue;
				}
				//we found the SCH segment
				int schOrder=hl7defmsg.hl7DefSegments[s].ItemOrder;
				for(int f=0;f<hl7defmsg.hl7DefSegments[s].hl7DefFields.Count;f++) {//Go through fields of SCH segment and get AptNum
					if(hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName=="apt.AptNum") {
						int aptNumOrdinal=hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos;
						aptNum=PIn.Long(msg.Segments[schOrder].Fields[aptNumOrdinal].ToString());
					}
				}
			}
			//We now have a patient object , either loaded from the db or new, and aptNum so process this message for this patient
			//We need to insert the pat to get a patnum so we can compare to guar patnum to see if relationship to guar is self
			if(IsNewPat) {
				if(isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Inserted patient: "+pat.FName+" "+pat.LName,EventLogEntryType.Information);
				}
				if(pat.PatNum==0) {
					pat.PatNum=Patients.Insert(pat,false);
				}
				else {
					pat.PatNum=Patients.Insert(pat,true);
				}
				patOld=pat.Copy();
			}
			for(int i=0;i<hl7defmsg.hl7DefSegments.Count;i++) {
				try {
					SegmentHL7 seg=msg.GetSegment(hl7defmsg.hl7DefSegments[i].SegmentName,!hl7defmsg.hl7DefSegments[i].IsOptional);
					if(seg!=null) {//null if segment was not found but is optional
						ProcessSeg(pat,aptNum,hl7defmsg.hl7DefSegments[i],seg,msg);
					}
				}
				catch(ApplicationException ex) {//Required segment was missing, or other error.
					HL7MsgCur.Note="Could not process this HL7 message.  "+ex;
					HL7Msgs.Update(HL7MsgCur);
					throw new Exception("Could not process HL7 message.  "+ex);
				}
			}
			//We have processed the message so now update or insert the patient
			if(pat.FName=="" || pat.LName=="") {
				EventLog.WriteEntry("OpenDentHL7","Patient demographics not processed due to missing first or last name. PatNum:"+pat.PatNum.ToString()
					,EventLogEntryType.Information);
				HL7MsgCur.Note="Patient demographics not processed due to missing first or last name. PatNum:"+pat.PatNum.ToString();
				HL7Msgs.Update(HL7MsgCur);
				return;
			}
			if(IsNewPat) {
				if(pat.Guarantor==0) {
					pat.Guarantor=pat.PatNum;
					Patients.Update(pat,patOld);
				}
				else {
					Patients.Update(pat,patOld);
				}
			}
			else {
				if(isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Updated patient: "+pat.FName+" "+pat.LName,EventLogEntryType.Information);
				}
				Patients.Update(pat,patOld);
			}
			HL7MsgCur.HL7Status=HL7MessageStatus.InProcessed;
			HL7Msgs.Update(HL7MsgCur);
		}
Exemplo n.º 6
0
		private void Send(Socket socketWorker) {
			List<HL7Msg> list=HL7Msgs.GetOnePending();
			while(list.Count>0) {
				string sendMsgControlId=HL7Msgs.GetControlId(list[0]);//could be empty string
				string data=MLLP_START_CHAR+list[0].MsgText+MLLP_END_CHAR+MLLP_ENDMSG_CHAR;
				byte[] byteData=Encoding.ASCII.GetBytes(data);
				socketWorker.SendTimeout=5000;//timeout in 5 seconds, send will retry after 6 second wait
				try {
					socketWorker.Send(byteData,byteData.Length,SocketFlags.None);//this is a blocking call, will timeout in 5 seconds
				}
				catch(Exception ex) {
					throw new Exception("The outbound socket encountered a problem sending a message.\r\nException: "+ex.Message);
				}
				#region RecieveAndProcessAck
				//For MLLP V2, do a blocking Receive here, along with a timeout.
				byte[] ackBuffer=new byte[256];//plenty big enough to receive the entire ack/nack response
				socketWorker.ReceiveTimeout=5000;//5 second timeout. Database is polled every 6 seconds for a new message to send, but if already sending and waiting for an ack, the new thread will just return
				int byteCountReceived=0;
				try {
					byteCountReceived=socketWorker.Receive(ackBuffer);//blocking Receive
				}
				catch(Exception ex) {
					throw new Exception("Timeout or other error waiting to receive an acknowledgment.\r\nException: "+ex.Message);
				}
				char[] chars=new char[byteCountReceived];
				Encoding.UTF8.GetDecoder().GetChars(ackBuffer,0,byteCountReceived,chars,0);
				StringBuilder strbAckMsg=new StringBuilder();
				strbAckMsg.Append(chars);
				if(strbAckMsg.Length>0 && strbAckMsg[0]!=MLLP_START_CHAR) {
					list[0].Note=list[0].Note+"Malformed acknowledgment (length "+strbAckMsg.Length+"):\r\n"+strbAckMsg.ToString()+"\r\n================\r\n";
					HL7Msgs.Update(list[0]);
					throw new Exception("Malformed acknowledgment.");
				}
				else if(strbAckMsg.Length>=3
					&& strbAckMsg[strbAckMsg.Length-1]==MLLP_ENDMSG_CHAR//last char is the endmsg char.
					&& strbAckMsg[strbAckMsg.Length-2]==MLLP_END_CHAR)//the second-to-the-last char is the end char.
				{
					//we have a complete message
					strbAckMsg.Remove(0,1);//strip off the start char
					strbAckMsg.Remove(strbAckMsg.Length-2,2);//strip off the end chars
				}
				else if(strbAckMsg.Length>=2//so that the next line won't crash
					&& strbAckMsg[strbAckMsg.Length-1]==MLLP_END_CHAR)//the last char is the end char.
				{
					//we will treat this as a complete message, because the endmsg char is optional.
					strbAckMsg.Remove(0,1);//strip off the start char
					strbAckMsg.Remove(strbAckMsg.Length-1,1);//strip off the end char
				}
				else {
					list[0].Note=list[0].Note+"Malformed acknowledgment (length "+strbAckMsg.Length+"):\r\n"+strbAckMsg.ToString()+"\r\n================\r\n";
					HL7Msgs.Update(list[0]);
					throw new Exception("Malformed acknowledgment.");
				}
				MessageHL7 ackMsg=new MessageHL7(strbAckMsg.ToString());
				try {
					MessageParser.ProcessAck(ackMsg,IsVerboseLogging);
				}
				catch(Exception ex) {
					list[0].Note=list[0].Note+ackMsg.ToString()+"\r\nError processing acknowledgment.\r\n";
					HL7Msgs.Update(list[0]);
					throw new Exception("Error processing acknowledgment.\r\n"+ex.Message);
				}
				if(ackMsg.AckCode=="" || (sendMsgControlId!="" && ackMsg.ControlId=="")) {
					list[0].Note=list[0].Note+ackMsg.ToString()+"\r\nInvalid ACK message.  Attempt to resend.\r\n";
					HL7Msgs.Update(list[0]);
					throw new Exception("Invalid ACK message received.");
				}
				if(ackMsg.ControlId==sendMsgControlId && ackMsg.AckCode=="AA") {//acknowledged received (Application acknowledgment: Accept)
					list[0].Note=list[0].Note+ackMsg.ToString()+"\r\nMessage ACK (acknowledgment) received.\r\n";
				}
				else if(ackMsg.ControlId==sendMsgControlId && ackMsg.AckCode!="AA") {//ACK received for this message, but ack code was not acknowledgment accepted
					if(list[0].Note.Contains("NACK4")) {//this is the 5th negative acknowledgment, don't try again
						list[0].Note=list[0].Note+"Ack code: "+ackMsg.AckCode+"\r\nThis is NACK5, the message status has been changed to OutFailed. We will not attempt to send again.\r\n";
						list[0].HL7Status=HL7MessageStatus.OutFailed;
					}
					else if(list[0].Note.Contains("NACK")) {//failed sending at least once already
						list[0].Note=list[0].Note+"Ack code: "+ackMsg.AckCode+"\r\nNACK"+list[0].Note.Split(new string[] { "NACK" },StringSplitOptions.None).Length+"\r\n";
					}
					else {
						list[0].Note=list[0].Note+"Ack code: "+ackMsg.AckCode+"\r\nMessage NACK (negative acknowlegment) received. We will try to send again.\r\n";
					}
					HL7Msgs.Update(list[0]);//Add NACK note to hl7msg table entry
					return;
				}
				else {//ack received for control ID that does not match the control ID of message just sent
					list[0].Note=list[0].Note+"Sent message control ID: "+sendMsgControlId+"\r\nAck message control ID: "+ackMsg.ControlId
					+"\r\nAck received for message other than message just sent.  We will try to send again.\r\n";
					HL7Msgs.Update(list[0]);
					return;
				}
				#endregion
				list[0].HL7Status=HL7MessageStatus.OutSent;
				HL7Msgs.Update(list[0]);//set the status to sent and save ack message in Note field.
				if(_ecwDateTimeOldMsgsDeleted.Date<DateTime.Now.Date) {
					if(IsVerboseLogging) {
						EventLog.WriteEntry("DeleteOldMsgText Starting");
					}
					_ecwDateTimeOldMsgsDeleted=DateTime.Now;//If DeleteOldMsgText fails for any reason.  This will cause it to not get called until the next day instead of with every msg.
					HL7Msgs.DeleteOldMsgText();//this function deletes if DateTStamp is less than CURDATE-INTERVAL 4 MONTH.  That means it will delete message text only once a day, not time based.
					if(IsVerboseLogging) {
						EventLog.WriteEntry("DeleteOldMsgText Finished");
					}
				}
				list=HL7Msgs.GetOnePending();//returns 0 or 1 pending message
			}
		}