Search code examples
javastringchecksumastm

How to write and send an ASTM frame to medical equipment


I am currently working on the ASTM protocol to send orders tests request to medical instrument. But I cannot send a message to the equipment correctly. To be more explicit, I want for example to send these frames:

        String h1, s2, s3, s4, s5, s6 = "";
        h1 = "H|@^\\|ODM-IdfDGIWA-36|||GeneXpert PC^GeneXpert^4.8|||||LIS||P|1394-97|20070521100245";
        s2 = "P|1";
        s3 = "O|1|SID-818||^^^TestId-12|S|20070812140500|||||A||||ORH||||||||||Q";
        s4 = "O|2|SID-818||^^^TestId-14|S|20070812140600|||||A||||ORH||||||||||Q";
        s5 = "O|3|SID-818||^^^TestId-16|S|20070812140700|||||A||||ORH||||||||||Q";
        s6 = "L|1|F";

and here is how I am doing now:

    writeMeBytes(outToServer, h1.getBytes());
    writeMeBytes(outToServer, s2.getBytes());
    writeMeBytes(outToServer, s3.getBytes());
    writeMeBytes(outToServer, s4.getBytes());
    writeMeBytes(outToServer, s5.getBytes());
    writeMeBytes(outToServer, s6.getBytes());


public static void writeMeBytes(DataOutputStream dos, byte [] b){
    if (b.length >0){
        int j = 0;
        while (j <= b.length-1) {
            try {
                dos.write(b[j++]);
            } catch (IOException ex) {
                Logger.getLogger(SimpleServer.class.getName()).log(Level.SEVERE, null, ex);
            }

        }
    }
}

I turn it into byte and then send byte after byte.

Except that I do not see any change on the receiver side.

Update according to @Muhammad Answer

This is what I did to send order to GeneXpert DX System

public class SimpleServer {

private static ServerSocket server;
private static Socket connection;

public static void main(String args[]) throws IOException, InterruptedException {
    server = new ServerSocket(12221);
    boolean stopped = false;

    System.out.println(" start... ");
    connection = server.accept();
    System.out.println("wait for connection");
    BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    DataOutputStream outToClient = new DataOutputStream(connection.getOutputStream());
    String currentMsg = "";
    int clientIntMessage;

    String h1, s2, s3, s4, s5, s6 = "";
    h1 = "1H|@^\\|ODM-IdfDGIWA-36|||GeneXpert PC^GeneXpert^4.8|||||LIS||P|1394-97|20070521100245" + ProtocolASCII.LF
            + "P|1" + ProtocolASCII.LF
            + "O|1|SID-818||^^^TestId-12|S|20070812140500|||||A||||ORH||||||||||Q" + ProtocolASCII.LF
            + "L|1|F" + ProtocolASCII.LF;
    s2 = "P|1";
    s3 = "O|1|SID-818||^^^TestId-12|S|20070812140500|||||A||||ORH||||||||||Q";
    //s4 = "O|2|SID-818||^^^TestId-14|S|20070812140600|||||A||||ORH||||||||||Q";
    //s5 = "O|3|SID-818||^^^TestId-16|S|20070812140700|||||A||||ORH||||||||||Q";
    s6 = "L|1|F";

    String retmsg = h1;
    //logException("OrderMessae   :" + retmsg);
    retmsg = ProtocolASCII.STX + retmsg + ProtocolASCII.CR + ProtocolASCII.ETX + ProtocolMessage.getCheckSum(retmsg) + ProtocolASCII.CR + ProtocolASCII.LF;

    clientIntMessage = inFromClient.read();
    //while (clientIntMessage != ProtocolASCII.EOT) {
    while (true) {
        currentMsg += String.valueOf(Character.toChars(clientIntMessage));
        if (clientIntMessage == ProtocolASCII.ENQ) {
            outToClient.writeBytes("" + ProtocolASCII.ACK);
            System.out.println(" <--- LIS [ACK] on DX [ENQ]");
        } else if (clientIntMessage == ProtocolASCII.ACK) {
            System.out.println(" ---> DX [ACK]");
            // Send your order message here
            outToClient.writeBytes(retmsg);
        } else if (clientIntMessage == ProtocolASCII.CR) {
            System.out.println(currentMsg);
            outToClient.writeBytes("" + ProtocolASCII.ACK);
        } else if (clientIntMessage == ProtocolASCII.NAK) {
            System.out.println(" ---> DX sent [NAK] ");
            System.out.println(" --- LIS now wait 10 sec... ");
            Thread.sleep(10000);
            outToClient.writeBytes("" + ProtocolASCII.ENQ);
            System.out.println(" <--- LIS [ENQ] ");
        } else if (clientIntMessage == ProtocolASCII.EOT) {
            System.out.println(" ---> DX END OF TRANSMISSION");
            outToClient.writeBytes("" + ProtocolASCII.ENQ);
            System.out.println(" <--- LIS [ENQ] ");
        }

        if (stopped) {
            break;
        }
        clientIntMessage = inFromClient.read();
    }
    connection.close();
    stopped = true;
}}

And this is the result I get from the console:

start... 
wait for connection
 <--- LIS [ACK] on DX [ENQ]
1H|@^\|ODM-rQTcjIWA-66||GeneXpert PC^GeneXpert^4.8|||||LIS||P|1394-97|20180314003724
Q|1|ALL||||||||||O@N
L|1|N
B5
 ---> DX [EOT] 
 <--- LIS [ENQ] 
 ---> DX [ACK]
 ---> DX sent [NAK] 
 --- LIS now wait 10 sec... 

DX is the machine software and LIS is the host. Whenever I try to send the ENQ, the machine answers me with NAK.

UPDATE 2
It seem to be working. But now windows events shows me an error about why my records orders don't appear in the GeneXpert DX host record list. The header record have been sent first.

Windows event logs after following @Muhammad *UPDATE 2**

GeneXpert DX STATUS


Solution

  • Before answering, lets discuss machine mechanism for Bidirectional.

    String h1, s2, s3, s4, s5, s6 = "";
        h1 = "H|@^\\|ODM-IdfDGIWA-36|||GeneXpert PC^GeneXpert^4.8|||||LIS||P|1394-97|20070521100245";
        s2 = "P|1";
        s3 = "O|1|SID-818||^^^TestId-12|S|20070812140500|||||A||||ORH||||||||||Q";
        s4 = "O|2|SID-818||^^^TestId-14|S|20070812140600|||||A||||ORH||||||||||Q";
        s5 = "O|3|SID-818||^^^TestId-16|S|20070812140700|||||A||||ORH||||||||||Q";
        s6 = "L|1|F";
    

    First, viewing above String message, there isn't any tag numbering as every machine I have done so far requires Tag number. For example:

    1H|\^&|||CS-2500^00-08^22029^^^CP^BV981798||||||||E1394-97
    2P|1|||00000152556|^JOHN^ABC||19440601|M|||||^Dr.Shaukat Khanum Hospital|||||||||^^^EAST
    3O|1|000038^01^0012586236^B||^^^051^^100.00¥^^^044^^100.00|R|201803081225236|||||N
    4L|1|N
    

    So, you must have NUMBERING for each tag.

    Second, Machine sends following query at first time:

    1H|\^&|||CS-2500^^22029^^^CP^BV981798||||||||E1394-9711
    2Q|1|000038^01^   0012365845B||^^^040^PT-INN\^^^050^APTT-FS|0|201803081227007F
    3L|1|NF9
    

    In, Query(2Q) Tag, 000038 rack id, 01 rack sequence number, 001H18074618 sample id (read from Barcode), further information can be verified from host or LIS manual provided by machine vendor.

    Third, When we receive this message, we will make message for machine that I have described above (writing again below) with additional checksum information. Again, this checksum can be found in host or LIS manual of machine.

    1H|\^&|||CS-2500^00-08^22029^^^CP^BV981798||||||||E1394-97
    2P|1|||00000152556|^JOHN^ABC||19440601|M|||||^Dr.Shaukat Khanum Hospital|||||||||^^^EAST
    3O|1|000038^01^12345678^B||^^^051^^100.00¥^^^044^^100.00|R|201803081225236|||||N
    4L|1|N
    

    Example of Checksum calculation. Note that it may vary from machine to machine.

      public static String getCheckSum(String msg) {
        int sum = 0;
        for (int i = 0; i < msg.length(); i++) {
            sum += msg.charAt(i);
        }
        sum += 16; //adding CR and ETX AND ETB
        sum = sum % 256;
        String checksum = Integer.toHexString(sum).toUpperCase();
        if (checksum.length() == 1) {
            checksum = "0" + checksum;
        }
        //System.out.println("\n Check Sum is ="+checksum);
        return checksum;
    }
    

    And here is our complete message that will be sent to machine:

    String retmsg = "3O|1|" + rackId + "^" + positionNumber + "^" + sampleId + "^B||" + testIds + "|" + priority + "|" + sysDate + "|||||" + orderType + "";
        logException("OrderMessae   :" + retmsg);
        retmsg = ProtocolASCII.STX + retmsg + ProtocolASCII.CR + ProtocolASCII.ETX + ProtocolMessage.getCheckSum(retmsg) + ProtocolASCII.CR + ProtocolASCII.LF;
    

    Last but not least, I don't know what is the machine as I don't have manual but I sense that you don't need to send every message one by one. You can send all at once.

    here is the code snippet of sending and receiving message to and from machine.

    while (clientIntMessage != ProtocolASCII.EOT) {
                        clientIntMessage = inFromClient.read();
                        currentMsg += String.valueOf(Character.toChars(clientIntMessage));
                        // System.out.println(currentMsg);
                        if (clientIntMessage == ProtocolASCII.ENQ) {
                            outToClient.writeBytes("" + ProtocolASCII.ACK);
                            System.out.println("[ACK] on Analyzer [ENQ]");
                        } else if (clientIntMessage == ProtocolASCII.ACK) {                        
                            System.out.println("Analyzer [ACK]");
                            // Send your order message here
                             outToClient.writeBytes(retmsg);
                            }
                        } else if (clientIntMessage == ProtocolASCII.LF) {
                            outToClient.writeBytes("" + ProtocolASCII.ACK);
                        } else if (clientIntMessage == ProtocolASCII.NAK) {
                            System.out.println(" Analyzer sent [NAK] ");
                        }
    
                    }
    

    Where ProtocolASCII.ACK is '\006', ProtocolASCII.ENQ is '\005' and ProtocolASCII.EOT is '\004'.

    The code is pretty much self explanatory and I am using it in production.

    Can you tell us which machine you are interfacing? It may help you if I have already integrated.

    Thanks . If further assistance is required, let me know.

    For your reference:

    public class ProtocolASCII {
    
      public static char STX = '\002';
      public static char ETX = '\003';
      public static char ETB = '\027';
      public static char EOT = '\004';
      public static char ENQ = '\005';
      public static char ACK = '\006';
      public static char NAK = '\025';
      public static char CR = '\r';
      public static char LF = '\n';
      public static char MOR = '>';
      public static char FS = '\034';
      public static char GS = '\035';
      public static char RS = '\036';
      public static char SFS = '\027';
      public static char VT = 0x0B; //END OF BLOCK 011
    
    
    }
    

    Update:

    From your comments:

    String retmsg = "3O|1|" + rackId + "^" + positionNumber + "^" + sampleId + "^B||" + testIds + "|" + priority + "|" + sysDate + "|||||" + orderType + ""; Where testIds is the list of tests to perform.

    example multitest order send 4O|1||4^1^ 12345678^B|^^^^WBC\^^^^RBC\^^^^HGB\^^^^HCT\^^^^MCV\^^^^MCH\^^^^MCHC\^^^^PLT\^^^^RDW-SD\^^^^RDW-CV\^^^^PDW\^^^^MPV\^^^^P-LCR\^^^^PCT\^^^^NEUT#\^^^^LYMPH#\^^^^MONO#\^^^^EO#\^^^^BASO#\^^^^NEUT%\^^^^LYMPH%\^^^^MONO%\^^^^EO%\^^^^BASO%\^^^^NRBC#\^^^^NRBC%\^^^^IG#\^^^^IG%|||||||N||||||||||||||F

    Get test codes from LIS manual or from company engineer and make pattern accordingly.

    Update 2

    Socket clientSocket = null;
    public static char STX = '\002';
    public static char ETX = '\003';
    public static char ETB = '\027';
    public static char EOT = '\004';
    public static char ENQ = '\005';
    public static char ACK = '\006';
    public static char NAK = '\025';
    public static char CR = '\r';
    public static char LF = '\n';
    public static char MOR = '>';
    public static char FS = '\034';
    public static char GS = '\035';
    public static char RS = '\036';
    public static char SFS = '\027';
    public static char VT = 0x0B; //END OF BLOCK 011
    public static Vector<String> vecMessages = new Vector<String>();
    private static int currentMsgCount = 0;
    private static ServerSocket server;
    private static Socket connection;
    Message mes = new Message();
    
    public static void main(String args[]) throws IOException, InterruptedException {
        server = new ServerSocket(12221);
        boolean stopped = false;
    
        System.out.println(" start... ");
        connection = server.accept();
        System.out.println("wait for connection");
        BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        DataOutputStream outToClient = new DataOutputStream(connection.getOutputStream());
        String currentMsg = "";
        int clientIntMessage;
    
    //    String h1, s2, s3, s4, s5, s6 = "";
    //    h1 = "1H|@^\\|ODM-IdfDGIWA-36|||GeneXpert     PC^GeneXpert^4.8|||||LIS||P|1394-97|20070521100245" + ProtocolASCII.LF
    //            + "P|1" + ProtocolASCII.LF
    //            + "O|1|SID-818||^^^TestId-12|S|20070812140500|||||A||||ORH||||||||||Q" + ProtocolASCII.LF
    //            + "L|1|F" + ProtocolASCII.LF;
    //    s2 = "P|1";
    //    s3 = "O|1|SID-818||^^^TestId-12|S|20070812140500|||||A||||ORH||||||||||Q";
    //    //s4 = "O|2|SID-818||^^^TestId-14|S|20070812140600|||||A||||ORH||||||||||Q";
    //    //s5 = "O|3|SID-818||^^^TestId-16|S|20070812140700|||||A||||ORH||||||||||Q";
    //    s6 = "L|1|F";
    //
    //    String retmsg = h1;
    //    //logException("OrderMessae   :" + retmsg);
    //    retmsg = ProtocolASCII.STX + retmsg + ProtocolASCII.CR + ProtocolASCII.ETX + ProtocolMessage.getCheckSum(retmsg) + ProtocolASCII.CR + ProtocolASCII.LF;
    
    
    
        clientIntMessage = inFromClient.read();
            //while (clientIntMessage != ProtocolASCII.EOT) {
            while (true) {
    
                while (clientIntMessage != EOT) {
                    clientIntMessage = inFromClient.read();
                    currentMsg += String.valueOf(Character.toChars(clientIntMessage));
                    // System.out.println(currentMsg);
                    if (clientIntMessage == ENQ) {
                        outToClient.writeBytes("" + ACK);
                        System.out.println("[ACK] on Analyzer [ENQ]");
                    } else if (clientIntMessage == ACK) {
    
                        System.out.println("Analyzer [ACK]");
                        if (vecMessages.size() == currentMsgCount) {
                            vecMessages.clear();
                            currentMsgCount = 0;
                            outToClient.writeBytes("" + EOT);
                            System.out.println("Host [EOT]");
                        } else {
                            String msg = (String) vecMessages.get(currentMsgCount++);
    
                            outToClient.writeBytes(msg);
    //                                System.out.println("Msg " + msg.substring(0, msg.length() - 4));
                        }
                    } else if (clientIntMessage == LF) {
                        outToClient.writeBytes("" + ACK);
                    } else if (clientIntMessage == NAK) {
                        System.out.println(" Analyzer sent [NAK] ");
                    }
    
                }
    
                System.out.println(currentMsg);
                mes.parser(currentMsg);
    
                clientIntMessage = 0;
                currentMsg = "";
    
            }
    //        connection.close();
    //        stopped = true;
        }
    
        public static class Message {
    //        machine Send This Query   ==>6.3.2.1.5 Example of Upload Message – Instrument System Sends Host Query
    //        H|@^\|b4a88d9adab947a7b3dca2b534119c25||ICU^GeneXpert^1.0|||||LIS||P|1394-97|20070521100245 
    //        Q|1|PatientID-556^SpecimenID-888||||||||||O@N 
    //        L|1|N 
    
            public static Vector<String> vecMessages = new Vector<String>();
    //        make message for machine
    
            public String HeaderMessage() {
                String retmsg = "H|@^\\|ccc6ade20d3623314sffa3e287f2314ad||LIS|||||ICU^GeneXpert^1.0||P|1394-97|20070521100245";
    //        System.out.println("HeaderMessage  :" + retmsg);
                retmsg = STX + retmsg + CR + ETX + getCheckSum(retmsg) + CR + LF;
    
                return retmsg;
            }
            //  P|1       
            //      6.3.1.3.3 Patient Information Record     Find in Document and make it as per document
    
            public String PatientMessage(Patient pat) {  //(Patient pat)
    
                String retmsg = "P|1|||" + pat.getMRNO() + "|^" + pat.getPatientName() + "||" + pat.getDOB() + "|" + pat.getGender() + "|||||^Dr.SKM-LAS||||||||||||^^^EAST";
    
                retmsg = STX + retmsg + CR + ETX + getCheckSum(retmsg) + CR + LF;
    
                return retmsg;
            }
    //                6.3.1.3.4 Test Order Record      Find in Document
    
            public String orderMessage(String sampleId, String testIds, String orderType, String rackId, String positionNumber, String priority) {
                DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmSS");
                String sysDate = dateFormat.format(new Date());
    
                String retmsg = "3O|1|" + rackId + "^" + positionNumber + "^" + sampleId + "^B||" + testIds + "|" + priority + "|" + sysDate + "|||||" + orderType + "";
    
                retmsg = STX + retmsg + CR + ETX + getCheckSum(retmsg) + CR + LF;
    
                return retmsg;
            }
    
    //        6.3.1.3.5 Message Terminator Record    Find in Document
            public String terminatorMessage(String type) {
                String retmsg = "L|1|" + type;
    
                retmsg = STX + retmsg + CR + ETX + getCheckSum(retmsg) + CR + LF;
                return retmsg;
            }
    
            public String getCheckSum(String msg) {
                int sum = 0;
                for (int i = 0; i < msg.length(); i++) {
                    sum += msg.charAt(i);
                }
                sum += 16; //adding CR and ETX AND ETB
                sum = sum % 256;
                String checksum = Integer.toHexString(sum).toUpperCase();
                if (checksum.length() == 1) {
                    checksum = "0" + checksum;
                }
                //System.out.println("\n Check Sum is ="+checksum);
                return checksum;
            }
    
            public void parser(String input) {
    //        Use StringTokenizer for split or split
    
                if (input.charAt(1) == 'Q' || input.charAt(2) == 'Q') {
    
                    //Q|1|PatientID-556^SpecimenID-888||||||||||O@N 
    //                        Split it and get information which machine send in Query SampleId and other
                    String rackId = "get from Query to check document";
                    String positionNumber = "get from Query to check document";
                    String sampleId = "get from Query to check document";
    
    //                        this.FetchOrders1(machineId, sampleId);     // for dummy Sample Run
                    this.FetchOrders1("abc", sampleId);
                    this.setMesType("Q");
    
                }
    
            }
    
            public void FetchOrders1(String machineId, String sampleId) {
                try {
                    this.vecMessages.add(HeaderMessage());
                    this.vecMessages.add(PatientMessage()); //Define patient information
                    this.vecMessages.add(orderMessage(sampleId, "test", "N", "rackId", "positionNumber", "R"));
                    this.vecMessages.add(terminatorMessage("N"));
    
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
        }`