Beispiel #1
0
        /// <summary>
        /// Take the HL7 message string and turn it into an HL7 object using NHAPI
        /// </summary>
        /// <param name="hl7Message">Raw HL7 string</param>
        /// <returns>HL7 Object</returns>
        private static ORU_R30 ParseMessage(string hl7Message)
        {
            // use NHapi to parse message if message complete:
            PipeParser parser = new PipeParser();
            ORU_R30    hl7    = new ORU_R30();

            IMessage im = parser.Parse(hl7Message);

            hl7 = im as ORU_R30;

            // Device may send SPM which isn't officially part of ORU_R30, so we have to add it by hand
            hl7.addNonstandardSegment("SPM");

            ASCIIEncoding encoder  = new ASCIIEncoding();
            int           spmStart = hl7Message.IndexOf("SPM");
            int           spmEnd   = hl7Message.IndexOf(encoder.GetString(new byte[] { Constants.CR }), hl7Message.IndexOf("SPM"));

            parser.Parse((NHapi.Model.V25.Segment.SPM)hl7.GetStructure("SPM"), hl7Message.Substring(spmStart, spmEnd - spmStart), new EncodingCharacters('|', hl7.MSH.EncodingCharacters.Value));

            // Parse notes section
            if (hl7Message.Contains("NTE") && hl7Message.Contains("OBX|2") && hl7Message.IndexOf("OBX|2") > hl7Message.IndexOf("NTE"))
            {
                parser.Parse((NHapi.Model.V25.Segment.NTE)hl7.GetStructure("NTE"), hl7Message.Substring(hl7Message.IndexOf("NTE"), hl7Message.IndexOf("OBX|2") - hl7Message.IndexOf("NTE")), new EncodingCharacters('|', hl7.MSH.EncodingCharacters.Value));
            }

            return(hl7);
        }
Beispiel #2
0
        /// <summary>
        /// For debugging purposes: Gets raw messages from database that couldn't be parsed (testId = null) and tries parsing them again
        /// </summary>
        internal static void ReparseRawMessages()
        {
            var messages = DB.GetRawMessagesWithoutTestId();

            Console.WriteLine("Reparsing " + messages.Count + " raw messages");

            foreach (var msg in messages)
            {
                ORU_R30 hl7 = ParseMessage(msg.Message);

                if (hl7 != null)
                {
                    if (hl7.OBR.UniversalServiceIdentifier.Identifier.Value == "MTB-RIF")
                    {
                        var testId = DB.StoreParsedMessage(hl7, msg.Message, "192.168.1.110");

                        if (testId.HasValue)
                        {
                            DB.UpdateRawMessageTestId(msg.RawMessageId, testId.Value);
                        }

                        Console.WriteLine(testId);
                    }
                    else
                    {
                        Console.WriteLine("Non-Tb result");
                    }
                }
            }

            Console.ReadLine();
        }
Beispiel #3
0
        /// <summary>
        /// Returns a simple HL7 data item with all the data needed for the later HL7 ACK message
        /// </summary>
        /// <param name="hl7">HL7 object</param>
        /// <returns>HL7 data item</returns>
        private DataForHL7Acknowledgement GetDataForHL7Acknowledgement(ORU_R30 hl7)
        {
            DataForHL7Acknowledgement data = new DataForHL7Acknowledgement();

            data.SendingApplicationNamespaceID     = hl7.MSH.SendingApplication.NamespaceID.Value.ToString();
            data.SendingApplicationUniversalID     = hl7.MSH.SendingApplication.UniversalID.Value.ToString();
            data.SendingApplicationUniversalIDType = hl7.MSH.SendingApplication.UniversalIDType.Value.ToString();
            data.MessageControlID = hl7.MSH.MessageControlID.Value.ToString();
            return(data);
        }
Beispiel #4
0
        /// <summary>
        /// Take the HL7 message string and turn it into an HL7 object using NHAPI
        /// </summary>
        /// <param name="hl7Message">Raw HL7 string</param>
        /// <param name="networkStream">Reference to the network stream so we can write to it.</param>
        /// <returns>HL7 Object</returns>
        private ORU_R30 ParseMessage(string hl7Message, ref NetworkStream networkStream)
        {
            // use NHapi to parse message if message complete:
            PipeParser parser = new PipeParser();
            ORU_R30    hl7    = new ORU_R30();

            try
            {
                IMessage im = parser.Parse(hl7Message);
                hl7 = im as ORU_R30;
            }
            catch (Exception e)
            {
                // send NAK for this frame
                networkStream.WriteByte(Constants.EOT);
                Logger.Log("Error converting message: " + e.Message, LogLevel.Error, hl7Message);
                return(null);
            }

            try
            {
                // Device may send SPM which isn't officially part of ORU_R30, so we have to add it by hand
                hl7.addNonstandardSegment("SPM");
                int spmStart = hl7Message.IndexOf("SPM");
                int spmEnd   = hl7Message.IndexOf(this.encoder.GetString(new byte[] { Constants.CR }), hl7Message.IndexOf("SPM"));
                parser.Parse((NHapi.Model.V25.Segment.SPM)hl7.GetStructure("SPM"), hl7Message.Substring(spmStart, spmEnd - spmStart), new EncodingCharacters('|', hl7.MSH.EncodingCharacters.Value));
            }
            catch (Exception e)
            {
                // send NAK for this frame
                networkStream.WriteByte(Constants.EOT);
                Logger.Log("Error parsing specimen part of message: " + e.Message, LogLevel.Error, hl7Message);
            }

            try
            {
                // Parse notes section
                if (hl7Message.Contains("NTE") && hl7Message.Contains("OBX|2") && hl7Message.IndexOf("OBX|2") > hl7Message.IndexOf("NTE"))
                {
                    parser.Parse((NHapi.Model.V25.Segment.NTE)hl7.GetStructure("NTE"), hl7Message.Substring(hl7Message.IndexOf("NTE"), hl7Message.IndexOf("OBX|2") - hl7Message.IndexOf("NTE")), new EncodingCharacters('|', hl7.MSH.EncodingCharacters.Value));
                }
            }
            catch (Exception e)
            {
                // send NAK for this frame
                networkStream.WriteByte(Constants.EOT);
                Logger.Log("Error parsing notes part of message: " + e.Message, LogLevel.Error, hl7Message);
            }

            return(hl7);
        }
Beispiel #5
0
        /// <summary>
        /// This is a thread for the client that has connected
        /// </summary>
        /// <param name="client">Client (GX-machine)</param>
        private void HandleClientComm(object client)
        {
            //TCP client that connected
            TcpClient tcpClient = (TcpClient)client;


            //Get reference to stream for client so we can read/write data
            NetworkStream networkStream = tcpClient.GetStream();

            //log that new client connected
            Logger.Log(string.Format("New client connected: {0}", tcpClient.Client.RemoteEndPoint.ToString()), LogLevel.Info);

            //variable to keep track of the bytes we received in the client stream
            int bytesRead;

            //variable for constructing the message
            string hl7Message = string.Empty;

            //this will store all messages we receive for later ACK:
            List <DataForHL7Acknowledgement> dataForAck = new List <DataForHL7Acknowledgement>();
            bool awaitingHl7Ack = false;

            //keep track of connection state:
            ConnectionState connState = ConnectionState.Neutral;

            while (true)
            {
                bytesRead = 0;
                string message = string.Empty;

                try
                {
                    //blocks until a client sends a message
                    //bytesRead = networkStream.Read(message, 0, 1024 * 1024);
                    if (networkStream.CanRead)
                    {
                        byte[]        msg = new byte[1];//TODO: set a higher value here or some other method to prevent reading lots of null-bytes/ Research: how to get perfect buffer size?
                        StringBuilder myCompleteMessage = new StringBuilder();

                        // Incoming message may be larger than the buffer size.
                        do
                        {
                            bytesRead = networkStream.Read(msg, 0, msg.Length);

                            myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(msg, 0, msg.Length));
                        }while (networkStream.DataAvailable);

                        message = myCompleteMessage.ToString();
                    }
                    else
                    {
                        Console.WriteLine("Sorry.  You cannot read from this NetworkStream.");
                    }
                }
                catch
                {
                    //a socket error has occurred
                    break;
                }

                if (bytesRead == 0)
                {
                    //the client has disconnected from the server
                    break;
                }

                //message has successfully been received
                ASCIIEncoding encoder = new ASCIIEncoding();

                //Logger.Log(encoder.GetString(message), LogLevel.Info);

                //if we got an ENQ, respond with ACK:
                if (message[0] == Constants.ENQ)
                {
                    Logger.Log("Sending ACK in response to ENQ...", LogLevel.Info);
                    networkStream.WriteByte(Constants.ACK);
                    networkStream.Flush();

                    connState = ConnectionState.Receiving;
                }
                //if eot of full message, confirm end of receiving and start transmission of HL7 ack
                else if (connState == ConnectionState.Receiving && message[0] == Constants.EOT)
                {
                    //send enq to go from neutral state to transmission:
                    Logger.Log("Sending ENQ...", LogLevel.Info);
                    networkStream.WriteByte(Constants.ENQ);
                    networkStream.Flush();

                    connState = ConnectionState.Sending;
                }
                //did we get an eot in response to our hl7-eot?
                else if (connState == ConnectionState.Sending && message[0] == Constants.EOT)
                {
                    //do nothing (we're back in neutral state)
                    Logger.Log("Received EOT from GX", LogLevel.Info);

                    //set vars back to neutral
                    connState = ConnectionState.Neutral;
                }
                //did we receive an ack in response to our enq and do we have messages to send acks for?
                else if (connState == ConnectionState.Sending && message[0] == Constants.ACK)
                {
                    //GX acknowledged last message -> remove from list:
                    if (dataForAck.Any() && awaitingHl7Ack)
                    {
                        var data = dataForAck.First();
                        dataForAck.Remove(data);
                    }

                    if (!dataForAck.Any())
                    {
                        //send eot
                        Logger.Log("Sending EOT in response to ACK for HL7-ACK...", LogLevel.Info);
                        networkStream.WriteByte(Constants.EOT);
                        networkStream.Flush();
                        awaitingHl7Ack = false;
                        connState      = ConnectionState.Neutral;
                    }
                    else
                    {
                        //get first data in list to acknowledge
                        var data = dataForAck.First();
                        awaitingHl7Ack = true;


                        PipeParser parser = new PipeParser();



                        //also need to send ack of complete message:
                        Logger.Log("Sending ack of entire message", LogLevel.Info);

                        NHapi.Model.V25.Message.ACK ack = new NHapi.Model.V25.Message.ACK();

                        //MSH-part of message acknowledgement:
                        ack.MSH.SendingApplication.NamespaceID.Value       = "LIS";//TODO: set to "bigpicture"
                        ack.MSH.ReceivingApplication.NamespaceID.Value     = data.SendingApplicationNamespaceID;
                        ack.MSH.ReceivingApplication.UniversalID.Value     = data.SendingApplicationUniversalID;
                        ack.MSH.ReceivingApplication.UniversalIDType.Value = data.SendingApplicationUniversalIDType;
                        //ack.MSH.DateTimeOfMessage.Time.Set(DateTime.Now, "yyyyMMddHHmmss");
                        string guid = Guid.NewGuid().ToString();
                        ack.MSH.MessageControlID.Value              = data.MessageControlID;
                        ack.MSH.ProcessingID.ProcessingID.Value     = "P";
                        ack.MSH.AcceptAcknowledgmentType.Value      = "NE";
                        ack.MSH.ApplicationAcknowledgmentType.Value = "NE";

                        //MSA-part
                        ack.MSA.AcknowledgmentCode.Value = "CA";
                        ack.MSA.MessageControlID.Value   = data.MessageControlID;
                        //ack.MSA.TextMessage.Value = "Test";



                        PipeParser parserResponse = new PipeParser();

                        string hl7Response = parserResponse.Encode(ack);


                        //wrapping message in hl7 llp start and end chars?
                        //hl7Response = encoder.GetString(new byte[] { PIPE }) + hl7Response + encoder.GetString(new byte[] { SEPARATOR }) + encoder.GetString(new byte[] { CR });

                        //get checksum for sending:
                        byte[] hl7ResponseForChecksum = encoder.GetBytes("1" + hl7Response + encoder.GetString(new byte[1] {
                            Constants.ETX
                        }));
                        int checksum = 0;
                        for (int i = 0; i < hl7ResponseForChecksum.Length; i++)
                        {
                            checksum += hl7ResponseForChecksum[i];
                        }

                        //convert checksum to hex so we can get eight least significant bits:
                        string checksumHex = checksum.ToString("X");


                        //wrapping message in astm control chars
                        // "0" = Frame Number
                        hl7Response = encoder.GetString(new byte[1] {
                            Constants.STX
                        }) + "1" + hl7Response + encoder.GetString(new byte[1] {
                            Constants.ETX
                        }) + checksumHex.Substring(checksumHex.Length - 2, 1) + checksumHex.Substring(checksumHex.Length - 1, 1) + encoder.GetString(new byte[1] {
                            Constants.CR
                        }) + encoder.GetString(new byte[1] {
                            Constants.LF
                        });

                        //hl7Response = encoder.GetString(hl7Response );


                        byte[] response = encoder.GetBytes(hl7Response);

                        networkStream.Write(response, 0, response.Length);
                        networkStream.Flush();
                    }
                }
                else if (connState == ConnectionState.Receiving && message[0] == Constants.STX && (message.Any(m => m == Constants.ETX) || message.Any(m => m == Constants.ETB))) //we received a message frame
                {
                    //TODO: verify that this is the frame number we expected, return nak if not
                    int  frameNumber = Convert.ToInt32(message[1]) - 48;
                    bool isEndFrame  = false;

                    Logger.Log(string.Format("Received {0}frame (Nr. {1})", isEndFrame ? "end" : "", frameNumber), LogLevel.Info);

                    //get text of message. It's the 2nd byte up until the ETB or ETX control character:
                    if (message.Any(m => m == Constants.ETX))
                    {
                        isEndFrame = true;
                    }

                    int indexOfEndMessage = message.IndexOf(isEndFrame ? Encoding.ASCII.GetString(new byte[] { Constants.ETX }) : Encoding.ASCII.GetString(new byte[] { Constants.ETB }));// Array.IndexOf(message, isEndFrame ? Constants.ETX : Constants.ETB);

                    //string encodedMessage = encoder.GetString(message, 2, indexOfEndMessage);



                    //TODO: verify checksum, send NAK if not correct
                    int cs1 = message[indexOfEndMessage + 1];
                    int cs2 = message[indexOfEndMessage + 2];

                    int checksum = 0;
                    for (int i = 1; i < indexOfEndMessage + 1; i++)
                    {
                        checksum += message[i];
                    }

                    //check checksum; send nak if not correct:
                    if (!checksum.ToString("X").EndsWith(message[indexOfEndMessage + 1].ToString() + message[indexOfEndMessage + 2].ToString()))
                    {
                        //send NAK for this frame
                        networkStream.WriteByte(Constants.NAK);
                        networkStream.Flush();
                        Logger.Log("Sent NAK because checksum incorrect", LogLevel.Warning);
                    }
                    else
                    {
                        hl7Message += message.Substring(2, indexOfEndMessage - 2);

                        //use nhapi to parse message if message complete:
                        if (isEndFrame)
                        {
                            PipeParser parser = new PipeParser();
                            ORU_R30    hl7    = new ORU_R30();

                            try
                            {
                                IMessage im = parser.Parse(hl7Message);
                                hl7 = im as ORU_R30;
                            }
                            catch (Exception e)
                            {
                                //send NAK for this frame
                                networkStream.WriteByte(Constants.EOT);
                                networkStream.Flush();
                                Logger.Log("Error converting message: " + e.Message, LogLevel.Error, hl7Message);
                                return;
                            }

                            try
                            {
                                //genexpert sends spm which isn't officially part of ORU_R30:
                                hl7.addNonstandardSegment("SPM");

                                //TODO: validate
                                parser.Parse((NHapi.Model.V25.Segment.SPM)hl7.GetStructure("SPM"), hl7Message.Substring(hl7Message.IndexOf("SPM"), 40), new EncodingCharacters('|', hl7.MSH.EncodingCharacters.Value));
                            }
                            catch (Exception e)
                            {
                                //send NAK for this frame
                                networkStream.WriteByte(Constants.EOT);
                                networkStream.Flush();
                                Logger.Log("Error parsing specimen part of message: " + e.Message, LogLevel.Error, hl7Message);
                                return;
                            }

                            //get all values that are of interest to us:
                            if (hl7 != null)
                            {
                                //make sure it's a Tb-result:
                                if (hl7.OBR.UniversalServiceIdentifier.Identifier.Value == "MTB-RIF")
                                {
                                    //log message:
                                    //Logger.Log(string.Format("HL7 Message: {0}", hl7Message), LogLevel.Info);

                                    //try to store in db:
                                    try
                                    {
                                        //store in database
                                        using (BigPictureEntities bpe = new BigPictureEntities())
                                        {
                                            //first, see if we know this machine already and create it if we don't:
                                            string instrumentSerial = ((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(4)).EntityIdentifier.Value;
                                            device device           = bpe.devices.FirstOrDefault(d => d.Serial == instrumentSerial);

                                            //create device if we don't have it yet
                                            if (device == null)
                                            {
                                                device            = new device();
                                                device.Serial     = instrumentSerial;
                                                device.InsertedBy = device.UpdatedBy = "BigPicture Listener";
                                                device.InsertedOn = device.InsertedOn = DateTime.Now;
                                                bpe.devices.Add(device);
                                            }

                                            //Add test to database if it doesn't exist already (=re-upload)
                                            string cartridgeSerial = ((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(2)).EntityIdentifier.Value;
                                            test   test            = bpe.tests.FirstOrDefault(t => t.CartridgeSerial == cartridgeSerial);

                                            //create test if not already exists
                                            if (test == null)
                                            {
                                                test                 = new test();
                                                test.InsertedOn      = DateTime.Now;
                                                test.InsertedBy      = "BigPicture Listener";
                                                test.CartridgeSerial = cartridgeSerial;
                                                bpe.tests.Add(test);
                                            }

                                            //fill test with new data we got:
                                            test.AssayHostTestCode       = hl7.OBR.UniversalServiceIdentifier.Identifier.Value;
                                            test.AssayName               = ((NHapi.Model.V25.Datatype.CE)hl7.GetOBSERVATION(0).OBX.GetField(3).GetValue(0)).Identifier.ExtraComponents.getComponent(1).Data.ToString();
                                            test.AssayVersion            = ((NHapi.Model.V25.Datatype.CE)hl7.GetOBSERVATION(0).OBX.GetField(3).GetValue(0)).Identifier.ExtraComponents.getComponent(2).Data.ToString();
                                            test.CartridgeExpirationDate = DateTime.ParseExact(((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(0)).EntityIdentifier.Value.ToString(), "yyyyMMdd", CultureInfo.CurrentCulture);
                                            test.ComputerName            = ((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(5)).EntityIdentifier.Value.ToString();
                                            test.SenderUser              = ((NHapi.Model.V25.Datatype.XCN)hl7.GetOBSERVATION(0).OBX.GetField(16)[0]).FamilyName.Surname.ToString();
                                            test.SenderVersion           = hl7.MSH.SendingApplication.Components[2].ToString();
                                            test.deployment              = device.deployment;
                                            test.MessageSentOn           = hl7.MSH.DateTimeOfMessage.Time.GetAsDate();
                                            test.ModuleSerial            = ((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(3)).EntityIdentifier.Value.ToString();
                                            //test.Notes = ;
                                            //test.PatientId = ;
                                            test.ReagentLotId = ((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(1)).EntityIdentifier.Value.ToString();
                                            test.ResultText   = ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(0).OBX.GetField(5).GetValue(0)).Data.ToString() + "|" + ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(19).OBX.GetField(5).GetValue(0)).Data.ToString() + "|";
                                            test.SampleId     = ((NHapi.Model.V25.Datatype.EI)(((NHapi.Model.V25.Datatype.EIP)((NHapi.Model.V25.Segment.SPM)hl7.GetStructure("SPM")).SpecimenID)[0])).EntityIdentifier.Value;
                                            test.SystemName   = hl7.MSH.SendingApplication.Components[0].ToString();
                                            //test.TestEndedOn = ;
                                            test.TestStartedOn = hl7.ORC.DateTimeOfTransaction.Time.GetAsDate();
                                            test.UpdatedBy     = "BigPicture Listener";
                                            test.UpdatedOn     = DateTime.Now;

                                            //normalize test results. TODO: handle if not exits yet
                                            //TB-result:
                                            //TODO: Throw godo error/log when not present in db:
                                            string     resultTestCodeTb   = ((NHapi.Model.V25.Datatype.CE)hl7.GetOBSERVATION(0).OBX.GetField(3).GetValue(0)).Identifier.ExtraComponents.getComponent(0).Data.ToString().Replace("4", "");
                                            int        resultTestCodeIdTb = bpe.resulttestcodes.First(r => r.ResultTestCode1 == resultTestCodeTb).ResultTestCodeId;
                                            testresult testResultTb       = new testresult();
                                            testResultTb.InsertedBy       = testResultTb.UpdatedBy = "BigPicture Listener";
                                            testResultTb.InsertedOn       = testResultTb.UpdatedOn = DateTime.Now;
                                            testResultTb.Result           = ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(0).OBX.GetField(5).GetValue(0)).Data == null || ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(0).OBX.GetField(5).GetValue(0)).Data.ToString() == null ? "" : ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(0).OBX.GetField(5).GetValue(0)).Data.ToString();
                                            testResultTb.TestId           = test.TestId;
                                            testResultTb.ResultTestCodeId = resultTestCodeIdTb;
                                            bpe.testresults.Add(testResultTb);

                                            //Rif-resistance-result:
                                            //TODO: Throw godo error/log when not present in db:
                                            string     resultTestCodeRif   = ((NHapi.Model.V25.Datatype.CE)hl7.GetOBSERVATION(19).OBX.GetField(3).GetValue(0)).Identifier.ExtraComponents.getComponent(0).Data.ToString();
                                            int        resultTestCodeIdRif = bpe.resulttestcodes.First(r => r.ResultTestCode1 == resultTestCodeRif).ResultTestCodeId;
                                            testresult testResultRif       = new testresult();
                                            testResultRif.InsertedBy       = testResultRif.UpdatedBy = "BigPicture Listener";
                                            testResultRif.InsertedOn       = testResultRif.UpdatedOn = DateTime.Now;
                                            testResultRif.Result           = ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(19).OBX.GetField(5).GetValue(0)).Data == null || ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(19).OBX.GetField(5).GetValue(0)).Data.ToString() == null ? "" : ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(19).OBX.GetField(5).GetValue(0)).Data.ToString();
                                            testResultRif.TestId           = test.TestId;
                                            testResultRif.ResultTestCodeId = resultTestCodeIdRif;
                                            bpe.testresults.Add(testResultRif);

                                            //finally, save everything to db:
                                            bpe.SaveChanges();
                                        }
                                    }
                                    catch (Exception e)
                                    {
                                        Logger.Log("Store in DB failed: " + e.Message, LogLevel.Error, hl7Message);
                                    }
                                }
                            }


                            //add to list of received messages for later HL7 ACK:
                            DataForHL7Acknowledgement data = new DataForHL7Acknowledgement();
                            data.SendingApplicationNamespaceID     = hl7.MSH.SendingApplication.NamespaceID.Value.ToString();
                            data.SendingApplicationUniversalID     = hl7.MSH.SendingApplication.UniversalID.Value.ToString();
                            data.SendingApplicationUniversalIDType = hl7.MSH.SendingApplication.UniversalIDType.Value.ToString();
                            data.MessageControlID = hl7.MSH.MessageControlID.Value.ToString();
                            dataForAck.Add(data);

                            //empty message-var for the next message to be sent
                            hl7Message = string.Empty;

                            //for some reason, our system is too quick for GX. need a delay here (really!)
                            //TODO: Double-check that this is really necessary
                            //Thread.Sleep(1000);

                            //send ACK for this frame
                            networkStream.WriteByte(Constants.ACK);
                            networkStream.Flush();
                            Logger.Log("Sent ACK of endframe", LogLevel.Info);
                        }
                        else
                        {
                            //send ACK for this frame
                            networkStream.WriteByte(Constants.ACK);
                            networkStream.Flush();
                            Logger.Log("Sent ACK of regular frame", LogLevel.Info);
                        }
                    }
                }
                else
                {
                    //send ACK in any other case
                    networkStream.WriteByte(Constants.ACK);
                    networkStream.Flush();
                    Logger.Log("Sent ACK for random frame", LogLevel.Info);
                }
            }
            tcpClient.Close();
        }
Beispiel #6
0
        /// <summary>
        /// Store the parsed HL7 object in the DB
        /// </summary>
        /// <param name="hl7">The HL7 object</param>
        /// <param name="hl7Message">The raw HL7 message</param>
        /// <param name="senderIp">IP that message was sent from.</param>
        /// <returns>ID of test that was inserted into DB. Null if insert unsuccessful</returns>
        internal static int?StoreParsedMessage(ORU_R30 hl7, string hl7Message, string senderIp)
        {
            // try to store in db:
            try
            {
                // testId we'll return:
                int?testId = null;

                // store in database
                using (GxAlertEntities bpe = new GxAlertEntities())
                {
                    string deviceSerial = ((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(5)).EntityIdentifier.Value;
                    string hostId       = hl7.MSH.ReceivingApplication.NamespaceID.Value.ToString();

                    // if we don't have a deployment for this device and hostId yet, create one:
                    deployment deployment = bpe.deployments.FirstOrDefault(l => l.HostId == hostId && l.devicedeploymenthistories.Any(h => h.device.Serial == deviceSerial));
                    if (deployment == null)
                    {
                        deployment            = new deployment();
                        deployment.HostId     = hostId;
                        deployment.Approved   = false;
                        deployment.Insertedby = deployment.Updatedby = ConfigurationManager.AppSettings["appName"];
                        deployment.InsertedOn = deployment.UpdatedOn = DateTime.Now;
                        bpe.deployments.Add(deployment);

                        // notify admins of new machine:
                        new Notifications().SendNewDeploymentNotification(hostId, deviceSerial, senderIp);
                    }

                    // create device if we don't have it yet
                    device device = bpe.devices.FirstOrDefault(d => d.Serial == deviceSerial);
                    if (device == null)
                    {
                        //get device type id for GeneXpert
                        int deviceTypeId = bpe.devicetypes.First(d => d.Name.Contains("GeneXpert")).DeviceTypeId;

                        device = new device();
                        device.DeviceTypeId = deviceTypeId;
                        device.Serial       = deviceSerial;
                        device.InsertedBy   = device.UpdatedBy = ConfigurationManager.AppSettings["appName"];
                        device.InsertedOn   = device.UpdatedOn = DateTime.Now;
                        bpe.devices.Add(device);
                    }

                    // keep device deployment history:
                    if (device.deployment != deployment)
                    {
                        device.deployment = deployment;

                        devicedeploymenthistory history = new devicedeploymenthistory();
                        history.device     = device;
                        history.deployment = deployment;
                        history.InsertedBy = ConfigurationManager.AppSettings["appName"];
                        history.InsertedOn = DateTime.Now;
                        bpe.devicedeploymenthistories.Add(history);
                    }

                    // Add test to database if it doesn't exist already (=re-upload)
                    string cartridgeSerial = ((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(2)).EntityIdentifier.Value;
                    test   test            = bpe.tests.FirstOrDefault(t => t.CartridgeSerial == cartridgeSerial);

                    // create test if not already exists
                    if (test == null)
                    {
                        test                 = new test();
                        test.TestTypeId      = 4;//GeneXpert TB/RIF Result
                        test.InsertedOn      = DateTime.Now;
                        test.InsertedBy      = ConfigurationManager.AppSettings["appName"];
                        test.CartridgeSerial = cartridgeSerial;
                        bpe.tests.Add(test);
                    }

                    // fill test with new data we got:
                    test.AssayHostTestCode       = hl7.OBR.UniversalServiceIdentifier.Identifier.Value;
                    test.AssayName               = ((NHapi.Model.V25.Datatype.CE)hl7.GetOBSERVATION(0).OBX.GetField(3).GetValue(0)).Identifier.ExtraComponents.getComponent(1).Data.ToString();
                    test.AssayVersion            = ((NHapi.Model.V25.Datatype.CE)hl7.GetOBSERVATION(0).OBX.GetField(3).GetValue(0)).Identifier.ExtraComponents.getComponent(2).Data.ToString();
                    test.CartridgeExpirationDate = ((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(0)).EntityIdentifier.Value != null?DateTime.ParseExact(((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(0)).EntityIdentifier.Value.ToString(), "yyyyMMdd", CultureInfo.CurrentCulture) : new DateTime?();

                    test.ComputerName     = ((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(5)).EntityIdentifier.Value.ToString();
                    test.SenderUser       = ((NHapi.Model.V25.Datatype.XCN)hl7.GetOBSERVATION(0).OBX.GetField(16)[0]).FamilyName.Surname.ToString();
                    test.SenderVersion    = hl7.MSH.SendingApplication.Components[2].ToString();
                    test.SenderIp         = senderIp;
                    test.deployment       = device.deployment;
                    test.MessageSentOn    = hl7.MSH.DateTimeOfMessage.Time.GetAsDate();
                    test.ModuleSerial     = ((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(3)).EntityIdentifier.Value.ToString();
                    test.InstrumentSerial = ((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(4)).EntityIdentifier.Value;

                    var notes1 = ((NHapi.Model.V25.Datatype.FT)hl7.GetNTE(0).GetComment(0)).ExtraComponents.getComponent(1).Data;
                    var notes2 = ((NHapi.Model.V25.Datatype.FT)hl7.GetNTE(0).GetComment(0)).ExtraComponents.getComponent(2).Data;
                    test.Notes = ((notes1 != null ? notes1.ToString() + " " : string.Empty) + (notes2 != null ? notes2.ToString() : string.Empty)).Trim();
                    test.Notes = string.IsNullOrWhiteSpace(test.Notes) ? null : test.Notes;

                    test.PatientId     = hl7.PID.GetPatientIdentifierList(0).IDNumber.Value;
                    test.ReagentLotId  = ((NHapi.Model.V25.Datatype.EI)hl7.GetOBSERVATION(0).OBX.GetField(18).GetValue(1)).EntityIdentifier.Value.ToString();
                    test.ResultText    = ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(0).OBX.GetField(5).GetValue(0)).Data.ToString() + "|" + ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(19).OBX.GetField(5).GetValue(0)).Data.ToString() + "|";
                    test.SampleId      = ((NHapi.Model.V25.Datatype.EI)((NHapi.Model.V25.Datatype.EIP)((NHapi.Model.V25.Segment.SPM)hl7.GetStructure("SPM")).SpecimenID)[0]).EntityIdentifier.Value;
                    test.SystemName    = hl7.MSH.SendingApplication.Components[0].ToString();
                    test.TestStartedOn = hl7.GetTIMING_QTY(0).TQ1.StartDateTime.Time.GetAsDate();

                    // for some reason, we got a handful of results (we have over 10000 now) that have an invalid end date.
                    // we'll just use the start date for those
                    try
                    {
                        test.TestEndedOn = hl7.GetTIMING_QTY(0).TQ1.EndDateTime.Time.GetAsDate();
                    }
                    catch
                    {
                        test.TestEndedOn = test.TestStartedOn;
                    }


                    test.UpdatedBy = ConfigurationManager.AppSettings["appName"];
                    test.UpdatedOn = DateTime.Now;

                    // normalize test results.
                    // TB-result:
                    string     resultTestCodeTb   = ((NHapi.Model.V25.Datatype.CE)hl7.GetOBSERVATION(0).OBX.GetField(3).GetValue(0)).Identifier.ExtraComponents.getComponent(0).Data.ToString();
                    int        resultTestCodeIdTb = bpe.resulttestcodes.First(r => r.ResultTestCode1 == resultTestCodeTb).ResultTestCodeId;
                    testresult resultTb           = bpe.testresults.FirstOrDefault(t => t.TestId == test.TestId && t.ResultTestCodeId == resultTestCodeIdTb);

                    if (resultTb == null)
                    {
                        resultTb                  = new testresult();
                        resultTb.InsertedBy       = ConfigurationManager.AppSettings["appName"];
                        resultTb.InsertedOn       = DateTime.Now;
                        resultTb.TestId           = test.TestId;
                        resultTb.ResultTestCodeId = resultTestCodeIdTb;
                        bpe.testresults.Add(resultTb);
                    }

                    resultTb.Result    = ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(0).OBX.GetField(5).GetValue(0)).Data == null || ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(0).OBX.GetField(5).GetValue(0)).Data.ToString() == null ? string.Empty : ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(0).OBX.GetField(5).GetValue(0)).Data.ToString();
                    resultTb.UpdatedBy = ConfigurationManager.AppSettings["appName"];
                    resultTb.UpdatedOn = DateTime.Now;

                    // Rif-resistance-result:
                    string     resultTestCodeRif   = ((NHapi.Model.V25.Datatype.CE)hl7.GetOBSERVATION(19).OBX.GetField(3).GetValue(0)).Identifier.ExtraComponents.getComponent(0).Data.ToString();
                    int        resultTestCodeIdRif = bpe.resulttestcodes.First(r => r.ResultTestCode1 == resultTestCodeRif).ResultTestCodeId;
                    testresult resultRif           = bpe.testresults.FirstOrDefault(t => t.TestId == test.TestId && t.ResultTestCodeId == resultTestCodeIdRif);

                    if (resultRif == null)
                    {
                        resultRif                  = new testresult();
                        resultRif.InsertedBy       = ConfigurationManager.AppSettings["appName"];
                        resultRif.InsertedOn       = DateTime.Now;
                        resultRif.TestId           = test.TestId;
                        resultRif.ResultTestCodeId = resultTestCodeIdRif;
                        bpe.testresults.Add(resultRif);
                    }

                    resultRif.Result    = ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(19).OBX.GetField(5).GetValue(0)).Data == null || ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(19).OBX.GetField(5).GetValue(0)).Data.ToString() == null ? string.Empty : ((NHapi.Base.Model.Varies)hl7.GetOBSERVATION(19).OBX.GetField(5).GetValue(0)).Data.ToString();
                    resultRif.UpdatedBy = ConfigurationManager.AppSettings["appName"];
                    resultRif.UpdatedOn = DateTime.Now;

                    // finally, save everything to db:
                    bpe.SaveChanges();

                    testId = test.TestId;
                }

                return(testId);
            }
            catch (Exception e)
            {
                Logger.Log("Store in DB failed: " + e.Message, LogLevel.Error, hl7Message);
                return(null);
            }
        }
Beispiel #7
0
        /// <summary>
        /// This is a thread for the client that has connected
        /// </summary>
        /// <param name="client">Client (medical device)</param>
        private void HandleClientComm(object client)
        {
            #region Local Variables
            // TCP client that connected
            TcpClient tcpClient = (TcpClient)client;

            // Get reference to stream for client so we can read/write data
            NetworkStream networkStream = tcpClient.GetStream();

            // log that new client connected
            Logger.Log(string.Format("New client connected: {0}", tcpClient.Client.RemoteEndPoint.ToString()), LogLevel.Info);

            // variable to keep track of the bytes we received in the client stream
            int bytesRead;

            // variable for constructing the message
            string hl7Message = string.Empty;

            // this will store all messages we receive for later ACK:
            List <DataForHL7Acknowledgement> dataForAck = new List <DataForHL7Acknowledgement>();

            // keep track of whether we're awaiting acknowledgement of our HL7-ACK
            bool awaitingHl7Ack = false;

            // keep track of connection state:
            ConnectionState connState = ConnectionState.Neutral;

            this.sendNotificationsDelegate = new SendNotificationsDelegate(new Notifications().SendNotifications);

            #endregion

            while (true)
            {
                #region Lower-level stuff (dealing with streams and receiving it as a message)
                bytesRead = 0;
                string message = string.Empty;

                try
                {
                    if (networkStream.CanRead)
                    {
                        byte[] messagePart     = new byte[1024];
                        string completeMessage = string.Empty;
                        string lf = this.encoder.GetString(new byte[] { Constants.LF });

                        // Incoming message may be larger than the buffer size; keep reading until complete
                        do
                        {
                            bytesRead = networkStream.Read(messagePart, 0, messagePart.Length);

                            completeMessage = string.Concat(completeMessage, this.encoder.GetString(messagePart, 0, bytesRead));
                        }while (!completeMessage.EndsWith(lf) &&
                                completeMessage.Length != 1 &&
                                bytesRead != 0);

                        /* Read from the stream until we received everything.
                         * If we're reading an HL7 message, that means that the message ends with a line feed.
                         * If it's just the low-level ASTM stuff, the length will be one
                         * That way we can avoid the nasty workaround of putting a 10ms sleep in the read-loop*/

                        message = completeMessage;
                    }
                    else
                    {
                        Logger.Log("Cannot read from this NetworkStream.", LogLevel.Error);
                    }
                }
                catch (Exception e)
                {
                    // a socket error has occurred
                    Logger.Log("A socket error has occurred: " + e.Message, LogLevel.Error);
                    break;
                }

                // did client disconnect?
                if (bytesRead == 0)
                {
                    Logger.Log(string.Format("Client disconnected (IP: {0})", tcpClient.Client.RemoteEndPoint.ToString()), LogLevel.Info);
                    tcpClient.Close();
                    Thread.CurrentThread.Abort();
                    break;
                }
                #endregion

                // finally, handle the message itself:
                if (message[0] == Constants.ENQ)
                {
                    Logger.Log("Sending ACK in response to ENQ...", LogLevel.Info);
                    networkStream.WriteByte(Constants.ACK);
                    connState = ConnectionState.Receiving;
                }
                else if (connState == ConnectionState.Receiving && message[0] == Constants.EOT)
                {
                    // if EOT of full message, confirm end of receiving and start transmission of HL7 ACK
                    Logger.Log("Sending ENQ...", LogLevel.Info);
                    networkStream.WriteByte(Constants.ENQ);
                    connState = ConnectionState.Sending;
                }
                else if (connState == ConnectionState.Sending && message[0] == Constants.EOT)
                {
                    // We got an EOT in response to our hl7-EOT? - do nothing (we're back in neutral state)
                    Logger.Log("Received EOT from Device", LogLevel.Info);
                    connState = ConnectionState.Neutral;
                }
                else if (connState == ConnectionState.Sending && message[0] == Constants.ACK)
                {
                    // Device acknowledged last message -> remove from list:
                    if (dataForAck.Any() && awaitingHl7Ack)
                    {
                        dataForAck.Remove(dataForAck.First());
                    }

                    // if no more messages to acknowledge, send EOT
                    if (!dataForAck.Any())
                    {
                        Logger.Log("Sending EOT in response to ACK for HL7-ACK...", LogLevel.Info);
                        networkStream.WriteByte(Constants.EOT);
                        awaitingHl7Ack = false;
                        connState      = ConnectionState.Neutral;
                    }
                    else
                    {
                        Logger.Log("Sending ACK of entire message...", LogLevel.Info);

                        awaitingHl7Ack = true;

                        // build ACK-response from message data
                        string hl7Response = this.ConstructHL7Acknowledgement(dataForAck.First());

                        // get checksum in hexadecimal for sending:
                        string checksumHex = new Util().GetChecksum(this.encoder.GetBytes("1" + hl7Response + this.encoder.GetString(new byte[1] {
                            Constants.ETX
                        })), 0).ToString("X");

                        // wrapping message in astm control chars ("1" = Frame Number)
                        hl7Response = this.encoder.GetString(new byte[1] {
                            Constants.STX
                        }) + "1" + hl7Response +
                                      this.encoder.GetString(new byte[1] {
                            Constants.ETX
                        }) +
                                      checksumHex.Substring(checksumHex.Length - 2, 1) + checksumHex.Substring(checksumHex.Length - 1, 1) +
                                      this.encoder.GetString(new byte[1] {
                            Constants.CR
                        }) + this.encoder.GetString(new byte[1] {
                            Constants.LF
                        });

                        // finally, convert message to byte array and write to stream
                        byte[] response = this.encoder.GetBytes(hl7Response);
                        networkStream.Write(response, 0, response.Length);
                    }
                }
                else if (connState == ConnectionState.Receiving &&
                         message[0] == Constants.STX &&
                         (message.Any(m => m == Constants.ETX) || message.Any(m => m == Constants.ETB)))
                {
                    int  frameNumber = Convert.ToInt32(message[1]) - 48; // TODO: verify that this is the frame number we expected, return NAK if not
                    bool isEndFrame  = message.Any(m => m == Constants.ETX);

                    Logger.Log(string.Format("Received {0}frame (Nr. {1})", isEndFrame ? "end" : string.Empty, frameNumber), LogLevel.Info);

                    // get end index of actual message so we can calculate checksum
                    int indexOfEndMessage = message.IndexOf(isEndFrame ? Encoding.ASCII.GetString(new byte[] { Constants.ETX }) : Encoding.ASCII.GetString(new byte[] { Constants.ETB })); // Array.IndexOf(message, isEndFrame ? Constants.ETX : Constants.ETB);
                    int checksum          = new Util().GetChecksum(message, 1, indexOfEndMessage + 1);

                    // check checksum; send NAK if not correct (the two bytes following the end of the message should equal the 2 least significant chars in the checksum in hex)
                    if (!checksum.ToString("X").EndsWith(message[indexOfEndMessage + 1].ToString() + message[indexOfEndMessage + 2].ToString()))
                    {
                        networkStream.WriteByte(Constants.NAK);
                        Logger.Log("Sent NAK because checksum incorrect", LogLevel.Warning);
                    }
                    else
                    {
                        hl7Message += message.Substring(2, indexOfEndMessage - 2);

                        // if we're dealing with an end frame, the message is complete -> parse and store in db
                        if (isEndFrame)
                        {
                            int?    testId = null;
                            ORU_R30 hl7    = this.ParseMessage(hl7Message, ref networkStream);

                            if (hl7 != null)
                            {
                                if (hl7.OBR.UniversalServiceIdentifier.Identifier.Value == "MTB-RIF")
                                {
                                    testId = DB.StoreParsedMessage(hl7, hl7Message, tcpClient.Client.RemoteEndPoint.ToString());

                                    this.sendNotificationsDelegate.BeginInvoke(testId, null, null);
                                }
                                else
                                {
                                    Logger.Log(string.Format("Received non-TB result. Assay Test Code: {0}", hl7.OBR.UniversalServiceIdentifier.Identifier.Value), LogLevel.Error);
                                }

                                // add to list of received messages for later HL7 ACK:
                                dataForAck.Add(this.GetDataForHL7Acknowledgement(hl7));
                            }

                            /* Not sure if we even have to handle this... should be enough to simply not send ACK
                             * else if (hl7Message.Contains("QCN"))
                             * {
                             *  // did we receive a request for test orders?
                             *  PipeParser parser = new PipeParser();
                             *  IMessage im = parser.Parse(hl7Message);
                             *  QCN_J01 qcn = im as QCN_J01;
                             *
                             *  if (qcn != null)
                             *  {
                             *      // send NAK for this frame
                             *      networkStream.WriteByte(Constants.NAK);
                             *      Logger.Log("Sent NAK of QCN message", LogLevel.Warning);
                             *  }
                             * }*/

                            DB.StoreRawMessage(hl7Message, testId);

                            // empty message-var for the next message to be sent
                            hl7Message = string.Empty;

                            // send ACK for this frame
                            networkStream.WriteByte(Constants.ACK);
                            Logger.Log("Sent ACK of endframe", LogLevel.Info);
                        }
                        else
                        {
                            // send ACK for this frame
                            networkStream.WriteByte(Constants.ACK);
                            Logger.Log("Sent ACK of regular frame", LogLevel.Info);
                        }
                    }
                }
                else
                {
                    // send ACK in any other case
                    networkStream.WriteByte(Constants.ACK);
                    Logger.Log(string.Format("Sent ACK for random frame ({0})", message[0]), LogLevel.Info, message);
                }
            }

            tcpClient.Close();
        }