/// <summary> /// Create an Ack message based on a received message /// </summary> /// <param name="inboundMessage">received message</param> /// <param name="ackCode">Should be "AE" or "AR", can be "AA".</param> /// <param name="errorMessage">The reason the message was rejected or an error. If "AA" was supplied as ackCode the errorMessage should be null.</param> /// <returns>Created ACK message</returns> public static IMessage MakeACK(IMessage inboundMessage, string ackCode, string errorMessage) { IMessage ackMessage = null; // Get an object from the right ACK class string ackClassType = string.Format("NHapi.Model.V{0}.Message.ACK, NHapi.Model.V{0}", inboundMessage.Version.Remove(inboundMessage.Version.IndexOf('.'), 1)); Type x = Type.GetType(ackClassType); ackMessage = (IMessage)Activator.CreateInstance(x); Terser inboundTerser = new Terser(inboundMessage); ISegment inboundHeader = null; inboundHeader = inboundTerser.getSegment("MSH"); // Find the HL7 version of the inbound message: // string version = null; try { version = Terser.Get(inboundHeader, 12, 0, 1, 1); } catch (NHapi.Base.HL7Exception) { // I'm not happy to proceed if we can't identify the inbound // message version. throw new NHapi.Base.HL7Exception("Failed to get valid HL7 version from inbound MSH-12-1"); } // Create a Terser instance for the outbound message (the ACK). Terser terser = new Terser(ackMessage); // Populate outbound MSH fields using data from inbound message ISegment outHeader = (ISegment)terser.getSegment("MSH"); DeepCopy.copy(inboundHeader, outHeader); // Now set the message type, HL7 version number, acknowledgement code // and message control ID fields: string sendingApp = terser.Get("/MSH-3"); string sendingEnv = terser.Get("/MSH-4"); terser.Set("/MSH-3", CommunicationName); terser.Set("/MSH-4", EnvironmentIdentifier); terser.Set("/MSH-5", sendingApp); terser.Set("/MSH-6", sendingEnv); terser.Set("/MSH-7", DateTime.Now.ToString("yyyyMMddmmhh")); terser.Set("/MSH-9", "ACK"); terser.Set("/MSH-12", version); terser.Set("/MSA-1", ackCode == null ? "AA" : ackCode); terser.Set("/MSA-2", Terser.Get(inboundHeader, 10, 0, 1, 1)); // Set error message if (errorMessage != null) { terser.Set("/ERR-7", errorMessage); } return(ackMessage); }
/// <summary> /// Create an appropriate response given the results /// </summary> /// <param name="results">The results that matches the query</param> /// <param name="map">The HL7 query parameter mapping</param> /// <param name="request">The original request message</param> /// <param name="count">The number of results that the user requested</param> /// <param name="offset">The offset to the first result</param> /// <param name="queryId">The unique query identifier used</param> /// <returns>The constructed result message</returns> protected virtual IMessage CreateQueryResponse(Hl7MessageReceivedEventArgs request, Expression filter, Hl7QueryParameterType map, IEnumerable results, Guid queryId, int offset, int count, int totalResults) { var retVal = this.CreateACK(map.ResponseType, request.Message, "AA", "Query Success"); var omsh = retVal.GetStructure("MSH") as MSH; var qak = retVal.GetStructure("QAK") as QAK; var odsc = retVal.GetStructure("DSC") as DSC; var oqpd = retVal.GetStructure("QPD") as QPD; DeepCopy.copy(request.Message.GetStructure("QPD") as ISegment, oqpd); omsh.MessageType.MessageCode.Value = "RSP"; omsh.MessageType.MessageStructure.Value = retVal.GetType().Name; omsh.MessageType.TriggerEvent.Value = map.ResponseTrigger; omsh.MessageType.MessageStructure.Value = map.ResponseTypeXml; qak.HitCount.Value = totalResults.ToString(); qak.HitsRemaining.Value = (totalResults - offset - count > 0 ? totalResults - offset - count : 0).ToString(); qak.QueryResponseStatus.Value = totalResults == 0 ? "NF" : "OK"; qak.ThisPayload.Value = results.OfType <Object>().Count().ToString(); if (ApplicationServiceContext.Current.GetService <Core.Services.IQueryPersistenceService>() != null && Int32.Parse(qak.HitsRemaining.Value) > 0) { odsc.ContinuationPointer.Value = queryId.ToString(); odsc.ContinuationStyle.Value = "RD"; } // Process results retVal = map.QueryHandler.AppendQueryResult(results, filter, retVal, request, map.ScoreConfiguration, offset); return(retVal); }
/// <summary> /// Append query results to the message /// </summary> public virtual IMessage AppendQueryResult(IEnumerable results, Expression queryDefinition, IMessage currentResponse, Hl7MessageReceivedEventArgs evt, String matchConfiguration = null, int offset = 0) { var patients = results.OfType <Patient>(); if (patients.Count() == 0) { return(currentResponse); } var retVal = currentResponse as RSP_K21; var pidHandler = SegmentHandlers.GetSegmentHandler("PID"); var pd1Handler = SegmentHandlers.GetSegmentHandler("PD1"); var nokHandler = SegmentHandlers.GetSegmentHandler("NK1"); var matchService = ApplicationServiceContext.Current.GetService <IRecordMatchingService>(); var matchConfigService = ApplicationServiceContext.Current.GetService <IRecordMatchingConfigurationService>(); // Return domains var rqo = evt.Message as QBP_Q21; List <AssigningAuthority> returnDomains = new List <AssigningAuthority>(); foreach (var rt in rqo.QPD.GetField(8).OfType <Varies>()) { var rid = new CX(rqo.Message); DeepCopy.copy(rt.Data as GenericComposite, rid); var authority = rid.AssigningAuthority.ToModel(); returnDomains.Add(authority); } if (returnDomains.Count == 0) { returnDomains = null; } // Process results int i = offset + 1; foreach (var itm in patients) { var queryInstance = retVal.GetQUERY_RESPONSE(retVal.QUERY_RESPONSERepetitionsUsed); pidHandler.Create(itm, queryInstance, returnDomains?.ToArray()); pd1Handler.Create(itm, queryInstance, null); nokHandler.Create(itm, queryInstance, null); queryInstance.PID.SetIDPID.Value = (i++).ToString(); // QRI? if (matchService != null && !String.IsNullOrEmpty(matchConfiguration)) { var score = matchService.Score <Patient>(itm, queryDefinition as Expression <Func <Patient, bool> >, matchConfiguration); queryInstance.QRI.CandidateConfidence.Value = score.Score.ToString(); queryInstance.QRI.AlgorithmDescriptor.Identifier.Value = matchConfiguration; } } return(retVal); }
public static void MakeACK(ISegment inboundHeader, string ackCode, IMessage ackMessage, string errorMessage) { if (!inboundHeader.GetStructureName().Equals("MSH")) { throw new NHapi.Base.HL7Exception("Need an MSH segment to create a response ACK (got " + inboundHeader.GetStructureName() + ")"); } // Find the HL7 version of the inbound message: string version = null; try { version = Terser.Get(inboundHeader, 12, 0, 1, 1); } catch (NHapi.Base.HL7Exception) { // I'm not happy to proceed if we can't identify the inbound // message version. throw new NHapi.Base.HL7Exception("Failed to get valid HL7 version from inbound MSH-12-1"); } // Create a Terser instance for the outbound message (the ACK). Terser terser = new Terser(ackMessage); // Populate outbound MSH fields using data from inbound message ISegment outHeader = (ISegment)terser.getSegment("MSH"); DeepCopy.copy(inboundHeader, outHeader); // Now set the message type, HL7 version number, acknowledgement code // and message control ID fields: string sendingApp = terser.Get("/MSH-3"); string sendingEnv = terser.Get("/MSH-4"); // Make sure you fill the MSH-3 and MSH-4 with the correct values // for you application, preferably with configuration terser.Set("/MSH-3", "HL7Client"); terser.Set("/MSH-4", "EnvironmentIdentifier"); terser.Set("/MSH-5", sendingApp); terser.Set("/MSH-6", sendingEnv); terser.Set("/MSH-7", DateTime.Now.ToString("yyyyMMddmmhh")); terser.Set("/MSH-9", "ACK"); terser.Set("/MSH-12", version); terser.Set("/MSA-1", ackCode == null ? "AA" : ackCode); terser.Set("/MSA-2", Terser.Get(inboundHeader, 10, 0, 1, 1)); // Set error message if (errorMessage != null) { terser.Set("/ERR-7", errorMessage); } }
/// <summary> /// Rewrite a QPD query to an HDSI query /// </summary> public virtual NameValueCollection ParseQuery(QPD qpd, Hl7QueryParameterType map) { NameValueCollection retVal = new NameValueCollection(); // Control of strength String strStrength = (qpd.GetField(4, 0) as Varies)?.Data.ToString(), algorithm = (qpd.GetField(5, 0) as Varies)?.Data.ToString(); Double?strength = String.IsNullOrEmpty(strStrength) ? null : (double?)Double.Parse(strStrength); // Query parameters foreach (var itm in MessageUtils.ParseQueryElement(qpd.GetField(3).OfType <Varies>(), map, algorithm, strength)) { try { retVal.Add(itm.Key, itm.Value); } catch (Exception e) { throw new HL7ProcessingException("Error processing query parameter", "QPD", "1", 3, 0, e); } } // Return domains foreach (var rt in qpd.GetField(8).OfType <Varies>()) { try { var rid = new CX(qpd.Message); DeepCopy.copy(rt.Data as GenericComposite, rid); var authority = rid.AssigningAuthority.ToModel(); if (authority.Key == this.m_configuration.LocalAuthority.Key) { retVal.Add("_id", rid.IDNumber.Value); } else { retVal.Add($"identifier[{authority.DomainName}]", "!null"); } } catch (Exception e) { throw new HL7ProcessingException("Error processing return domains", "QPD", "1", 8, 0, e); } } retVal.Add("obsoletionTime", "null"); return(retVal); }
public static IMessage MakeACK(ISegment inboundHeader, string ackCode) { if (!inboundHeader.GetStructureName().Equals("MSH")) { throw new NHapi.Base.HL7Exception( "Need an MSH segment to create a response ACK (got " + inboundHeader.GetStructureName() + ")"); } // Find the HL7 version of the inbound message: // string version = null; try { version = Terser.Get(inboundHeader, 12, 0, 1, 1); } catch (NHapi.Base.HL7Exception) { // I'm not happy to proceed if we can't identify the inbound // message version. throw new NHapi.Base.HL7Exception("Failed to get valid HL7 version from inbound MSH-12-1"); } IMessage ackMessage = new ACK(); // Create a Terser instance for the outbound message (the ACK). Terser terser = new Terser(ackMessage); // Populate outbound MSH fields using data from inbound message ISegment outHeader = (ISegment)terser.getSegment("MSH"); DeepCopy.copy(inboundHeader, outHeader); // Now set the message type, HL7 version number, acknowledgement code // and message control ID fields: string sendingApp = terser.Get("/MSH-3"); string sendingEnv = terser.Get("/MSH-4"); terser.Set("/MSH-3", CommunicationName); terser.Set("/MSH-4", EnvironmentIdentifier); terser.Set("/MSH-5", sendingApp); terser.Set("/MSH-6", sendingEnv); terser.Set("/MSH-7", DateTime.Now.ToString("yyyyMMddmmhh")); terser.Set("/MSH-9", "ACK"); terser.Set("/MSH-12", version); terser.Set("/MSA-1", ackCode == null ? "AA" : ackCode); terser.Set("/MSA-2", Terser.Get(inboundHeader, 10, 0, 1, 1)); return(ackMessage); }
/// <summary> /// Rewrite a QPD query to an HDSI query /// </summary> public virtual NameValueCollection ParseQuery(QPD qpd, Hl7QueryParameterType map) { NameValueCollection retVal = new NameValueCollection(); // Query domains foreach (var rt in qpd.GetField(3).OfType <Varies>()) { try { var rid = new CX(qpd.Message); DeepCopy.copy(rt.Data as GenericComposite, rid); var authority = rid.AssigningAuthority.ToModel(); if (authority.Key == m_configuration.LocalAuthority.Key) { retVal.Add("_id", rid.IDNumber.Value); } else { retVal.Add($"identifier[{authority.DomainName}].value", rid.IDNumber.Value); } } catch (Exception e) { throw new HL7ProcessingException("Error processing patient identity", "QPD", "1", 3, 4, e); } } //// Return domains // This just verifies the return domains foreach (var rt in qpd.GetField(4).OfType <Varies>()) { try { var rid = new CX(qpd.Message); DeepCopy.copy(rt.Data as GenericComposite, rid); var authority = rid.AssigningAuthority.ToModel(); } catch (Exception e) { throw new HL7ProcessingException("Error processing what domains returned", "QPD", "1", 4, 4, e); } } return(retVal); }
/// <summary> /// Create an Ack message based on a received message /// </summary> /// <param name="inboundMessage">received message</param> /// <param name="ackResult">Send AA, AE or AR message.</param> /// <param name="errorMessage">The reason the message was rejected or an error. If "AA" was supplied as ackCode the errorMessage should be null.</param> /// <returns>Created ACK message</returns> public IMessage MakeACK(IMessage inboundMessage, AckTypes ackResult, string errorMessage) { //this should avoid an unhandled null reference exception in "inboundMessage.Version", because people tend to send the inboudMessage without a check if (inboundMessage == null) { throw new ArgumentNullException("Either process the valid message while retreiving the ack or handle invalid message differently"); } IMessage ackMessage = null; // Get an object from the right ACK class string ackClassType = string.Format("NHapi.Model.V{0}.Message.ACK, NHapi.Model.V{0}", inboundMessage.Version.Replace(".", "")); Type x = Type.GetType(ackClassType); if (x != null) { ackMessage = (IMessage)Activator.CreateInstance(x); } else { // Fix for V2.2 and V2.1 Since tha ACK message class is missing there in NHapi if (inboundMessage.Version == "2.1") { ackMessage = (IMessage) new NHapiTools.Base.CustomImplementation.V21.Messages.ACK(); } if (inboundMessage.Version == "2.2") { ackMessage = (IMessage) new NHapiTools.Base.CustomImplementation.V22.Messages.ACK(); } } Terser inboundTerser = new Terser(inboundMessage); ISegment inboundHeader = null; inboundHeader = inboundTerser.getSegment("MSH"); // Find the HL7 version of the inbound message: string version = null; try { version = Terser.Get(inboundHeader, 12, 0, 1, 1); } catch (NHapi.Base.HL7Exception) { // I'm not happy to proceed if we can't identify the inbound // message version. throw new NHapi.Base.HL7Exception("Failed to get valid HL7 version from inbound MSH-12-1"); } // Create a Terser instance for the outbound message (the ACK). Terser terser = new Terser(ackMessage); // Populate outbound MSH fields using data from inbound message ISegment outHeader = (ISegment)terser.getSegment("MSH"); DeepCopy.copy(inboundHeader, outHeader); // Now set the message type, HL7 version number, acknowledgement code // and message control ID fields: string sendingApp = terser.Get("/MSH-3"); string sendingEnv = terser.Get("/MSH-4"); terser.Set("/MSH-3", appCommunicationName); terser.Set("/MSH-4", environmentIdentifier); terser.Set("/MSH-5", sendingApp); terser.Set("/MSH-6", sendingEnv); terser.Set("/MSH-7", DateTime.Now.ToString("yyyyMMddmmhh")); terser.Set("/MSH-9", "ACK"); terser.Set("/MSH-9-3", "ACK"); terser.Set("/MSH-12", version); terser.Set("/MSA-1", Enum.GetName(typeof(AckTypes), ackResult)); terser.Set("/MSA-2", Terser.Get(inboundHeader, 10, 0, 1, 1)); // Set error message if (errorMessage != null) { terser.Set("/ERR-1-1", errorMessage); } return(ackMessage); }
/// <summary> /// Append query results to the message /// </summary> public virtual IMessage AppendQueryResult(IEnumerable results, Expression queryDefinition, IMessage currentResponse, Hl7MessageReceivedEventArgs evt, String matchConfiguration = null, int offset = 0) { var patients = results.OfType <Patient>(); if (patients.Count() == 0) { return(currentResponse); } var retVal = currentResponse as RSP_K23; var rqo = evt.Message as QBP_Q21; // Return domains List <AssigningAuthority> returnDomains = new List <AssigningAuthority>(); foreach (var rt in rqo.QPD.GetField(4).OfType <Varies>()) { var rid = new CX(rqo.Message); DeepCopy.copy(rt.Data as GenericComposite, rid); var domain = rid.AssigningAuthority.ToModel(); returnDomains.Add(domain); } var matchService = ApplicationServiceContext.Current.GetService <IRecordMatchingService>(); var matchConfigService = ApplicationServiceContext.Current.GetService <IRecordMatchingConfigurationService>(); // Process results int i = offset + 1; foreach (var itm in patients) { var queryInstance = retVal.QUERY_RESPONSE; // Expose PK? if (returnDomains.Count == 0 || returnDomains.Any(d => d.Key == this.m_configuration.LocalAuthority.Key)) { queryInstance.PID.GetPatientIdentifierList(queryInstance.PID.PatientIdentifierListRepetitionsUsed).FromModel(new EntityIdentifier(this.m_configuration.LocalAuthority, itm.Key.ToString())); queryInstance.PID.GetPatientIdentifierList(queryInstance.PID.PatientIdentifierListRepetitionsUsed - 1).IdentifierTypeCode.Value = "PI"; } foreach (var id in itm.LoadCollection <EntityIdentifier>("Identifiers")) { if (returnDomains.Count == 0 || returnDomains.Any(o => o.Key == id.AuthorityKey)) { queryInstance.PID.GetPatientIdentifierList(queryInstance.PID.PatientIdentifierListRepetitionsUsed).FromModel(id); } } if (returnDomains.Any(rid => rid.Key == this.m_configuration.LocalAuthority.Key)) { int idx = queryInstance.PID.PatientIdentifierListRepetitionsUsed; queryInstance.PID.GetPatientIdentifierList(idx).IDNumber.Value = itm.Key.Value.ToString(); queryInstance.PID.GetPatientIdentifierList(idx).AssigningAuthority.NamespaceID.Value = this.m_configuration.LocalAuthority.DomainName; queryInstance.PID.GetPatientIdentifierList(idx).AssigningAuthority.UniversalID.Value = this.m_configuration.LocalAuthority.Oid; queryInstance.PID.GetPatientIdentifierList(idx).AssigningAuthority.UniversalIDType.Value = "ISO"; } // No identifiers found in the response domains if (queryInstance.PID.PatientIdentifierListRepetitionsUsed > 0) { queryInstance.PID.SetIDPID.Value = (i++).ToString(); queryInstance.PID.GetPatientName(0).NameTypeCode.Value = "S"; } else { (currentResponse.GetStructure("QAK") as QAK).HitCount.Value = "0"; (currentResponse.GetStructure("QAK") as QAK).HitsRemaining.Value = "0"; (currentResponse.GetStructure("QAK") as QAK).ThisPayload.Value = "0"; (currentResponse.GetStructure("QAK") as QAK).QueryResponseStatus.Value = "NF"; } } return(retVal); }