steveohara / j2mod

Enhanced Modbus library implemented in the Java programming language
Apache License 2.0
265 stars 111 forks source link

How to query and get response of Preset Multiple Register ? Can you provide any test class ? #98

Closed shaileshsathe closed 5 years ago

shaileshsathe commented 5 years ago

I am using WriteMultipleRegistersRequest and WriteMultipleRegistersResponse class to read the Preset Multiple Registers. Is that correct function code classes am I using ? Because I am getting output as "Illegal Data Value". Can you provide any test class for the same ?

public static void main(String[] args){
        SerialConnection serialConnection = null;
        ModbusSerialTransaction trans = null;

        WriteMultipleRegistersRequest writeMultipleRegistersRequest = null;
        WriteMultipleRegistersResponse writeMultipleRegistersResponse = null;

       SerialParameters params = new SerialParameters();
        params.setPortName("COM5");
        params.setBaudRate("9600");
        params.setParity("None");
        params.setDatabits("8");
        params.setStopbits("1");
        params.setEncoding("rtu");
        params.setEcho(false);

        serialConnection = new SerialConnection(params);
        serialConnection.setTimeout(250);

try {
            serialConnection.open();
//query in HEX  03 10 CC 00 14 28 01 04 03 13 6B B2
     writeMultipleRegistersRequest = new WriteMultipleRegistersRequest(460, new Register[]{
                new SimpleInputRegister(0),//No of Registers Hi
                new SimpleInputRegister(20),//No of Registers Lo
                new SimpleInputRegister(40),//Log Download Bytes
                new SimpleInputRegister(1),//Parameter No
                new SimpleInputRegister(4),//Date
                new SimpleInputRegister(3),//Month
                new SimpleInputRegister(19)//Year
            });
            writeMultipleRegistersRequest.setUnitID(3);
            writeMultipleRegistersRequest.setHeadless();

            writeMultipleRegistersResponse = writeMultipleRegistersResponse(serialConnection, 
 writeMultipleRegistersRequest);
System.out.println("Hex >> " + writeMultipleRegistersResponse.getHexMessage());

} catch (IOException ex) {
            Logger.getLogger(ReadWritePresetRegisterTest.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ModbusException ex) {
            Logger.getLogger(ReadWritePresetRegisterTest.class.getName()).log(Level.SEVERE, null, ex);
        }
}

private static WriteMultipleRegistersResponse writeMultipleRegistersResponse(SerialConnection serialConnection, ModbusRequest modbusRequest) throws ModbusException {
        ModbusSerialTransaction modbusSerialTransaction = new ModbusSerialTransaction(serialConnection);
        modbusSerialTransaction.setRequest(modbusRequest);
        modbusSerialTransaction.setTransDelayMS(10);
               modbusSerialTransaction.execute();

        if (modbusSerialTransaction.getResponse() instanceof WriteMultipleRegistersResponse) {
            return (WriteMultipleRegistersResponse) modbusSerialTransaction.getResponse();
        }
        return null;

    }

When used third party tool for same query by typing HEX for each Register and sending the query getting expected response. Is anything I am missing ? Any help will be appreciated. Query fired using the tool was 03 10 CC 00 14 28 01 04 03 13 6B B2

shaileshsathe commented 5 years ago

Hi, Any update about this issue ?

steveohara commented 5 years ago

There is a test for this that uses a TCP connection which is working fine so it suggests it is an issue with the serial comms. I will add a serial test tonight and see if I can replicate it

shaileshsathe commented 5 years ago

Thanks Steve for looking into it.

Even I am using the WriteMultipleRegistersRequest and WriteMultipleRegistersResponse to write Holding Register with both TCP as well as RTU and it absolutely works fine. But when I am trying to request Preset Multiple Register. It says Illegal Data Value.

I could see the requested Hex Message through j2mod looks like "03 10 01 CA 00 06 0C 00 14 00 28 00 01 00 04 00 03 00 13"

and using third party tool where I manually enter each HEX, the request looks like "03 10 01 CC 00 14 28 01 04 03 13 6B B2"

I don't get where this 00 06 0C gets appended after Starting Address 01 CA. (Note :Here I have to used 01 CA =>(458) instead of 01 CC =>(460) since using J2Mod I have to lower the Starting address by 1 as per my experience. In this case 2 because 2 registers makes 1 value in this case). Also before each HEX 00 is getting prepended in the J2MOD request which I made as bold . Let me know If I do need to change my request using J2MOD.

shaileshsathe commented 5 years ago

Hi , Any update about this issue ? I have been stuck here since long. Please help me to move ahead with this.

steveohara commented 5 years ago

Apologies for the delay. I've just taken another look at this and can see that there are in fact unit tests for this and they are working fine. The unit test is testWriteMultipleRegisters() Preset Multiple Registers (FC16) is WriteMultipleRegistersRequest in j2mod which you say is working fine so I'm not sure what methods you are using.

The request message formed by j2mod follows the format as defined here; http://www.simplymodbus.ca/FC16.htm

03 10 01 CA 00 06 0C 00 14 00 28 00 01 00 04 00 03 00 13

03 The Slave Address 10 The Function Code 16 (Preset Multiple Registers, 10 hex - 16 ) 01CA The Data Address of the first register (0001 hex = 1 , + 40001 offset = register #40002) 0006 The number of registers to write 0C The number of data bytes to follow (6 registers x 2 bytes each = 12 bytes) 0014 The value to write to register 40459 0028 The value to write to register 40460 0001 The value to write to register 40461 0004 The value to write to register 40462 0003 The value to write to register 40463 0013 The value to write to register 40464

The exception you are getting indicates that the slave doesn't like your request i.e. you are trying to write to registers that cannot take those values - see http://www.simplymodbus.ca/exceptions.htm response code 03

shaileshsathe commented 5 years ago

Thanks for the answer Steve. Agreed that register cannot accept those values but how its accepting the values from third party tool "DockLight" and giving the response. Below is my code using J2MOD.

import com.ghgande.j2mod.modbus.ModbusException;
import com.ghgande.j2mod.modbus.io.ModbusSerialTransaction;
import com.ghgande.j2mod.modbus.msg.ModbusRequest;
import com.ghgande.j2mod.modbus.msg.ReadMultipleRegistersRequest;
import com.ghgande.j2mod.modbus.msg.ReadMultipleRegistersResponse;
import com.ghgande.j2mod.modbus.msg.WriteMultipleRegistersRequest;
import com.ghgande.j2mod.modbus.msg.WriteMultipleRegistersResponse;
import com.ghgande.j2mod.modbus.net.SerialConnection;
import com.ghgande.j2mod.modbus.procimg.Register;
import com.ghgande.j2mod.modbus.procimg.SimpleInputRegister;
import com.ghgande.j2mod.modbus.util.SerialParameters;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ReadWritePresetRegisterTest {
  public static void main(String args[]){
        SerialConnection serialConnection = null;
        WriteMultipleRegistersRequest writeMultipleRegistersRequest = null;
        WriteMultipleRegistersResponse writeMultipleRegistersResponse = null;

        SerialParameters params = new SerialParameters();
        params.setPortName("COM5");
        params.setBaudRate("9600");
        params.setParity("None");
        params.setDatabits("8");
        params.setStopbits("1");
        params.setEncoding("rtu");
        params.setEcho(false);

        serialConnection = new SerialConnection(params);
        serialConnection.setTimeout(500);

        try {
            serialConnection.open();
            //Query according to Meter Manual
            writeMultipleRegistersRequest = new WriteMultipleRegistersRequest(460, new Register[]{
                new SimpleInputRegister(20),  //No of Registers to be accessed
                new SimpleInputRegister(40), //Log Download Bytes
                new SimpleInputRegister(1),//Parameter No
                new SimpleInputRegister(4),//Date
                new SimpleInputRegister(3),//Month
                new SimpleInputRegister(19)//Year
            });
            writeMultipleRegistersRequest.setUnitID(3);
            writeMultipleRegistersRequest.setHeadless();
           System.out.println("Request  Hex >> " + writeMultipleRegistersRequest.getHexMessage());

           writeMultipleRegistersResponse = writeMultipleRegistersResponse(serialConnection, 
                    writeMultipleRegistersRequest); 

           System.out.println("Response Hex >> " + writeMultipleRegistersResponse.getHexMessage());
      } catch (ModbusException ex) {
            Logger.getLogger(ReadWritePresetRegisterTest.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex) {
            Logger.getLogger(ReadWritePresetRegisterTest.class.getName()).log(Level.SEVERE, null, ex);
        }
 }

 private static WriteMultipleRegistersResponse writeMultipleRegistersResponse(SerialConnection 
   serialConnection, ModbusRequest modbusRequest) throws ModbusException {
        ModbusSerialTransaction modbusSerialTransaction = new 
                              ModbusSerialTransaction(serialConnection);
        modbusSerialTransaction.setRequest(modbusRequest);
        modbusSerialTransaction.setTransDelayMS(10);
        modbusSerialTransaction.execute();

        if (modbusSerialTransaction.getResponse() instanceof WriteMultipleRegistersResponse) {
            return (WriteMultipleRegistersResponse) modbusSerialTransaction.getResponse();
        }
        return null;

    }
}

Hope I am using it in correct way. Let me know if I am missing something here. I used the same code earlier to write Holding Registers (FC:03) and values written successfully but when I use it for Preset Multiple Register (FC:16). It gives the error "Illegal Data Value". Any help would be really appreciated .

Below is the screenshot of Docklight tool with Request and Response.

image

steveohara commented 5 years ago

Your FC16 message to your slave device is incorrect - the reference I sent you is a depiction of the actual standard http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf page 30. Can you tell me the name, model and manufacturer of the slave device and perhaps I can figure it out.

shaileshsathe commented 5 years ago

Yes Steve.Please find attached Interface Definition of the product.You can check the page 53-54 and 56-57 for the queries I am working on.

LM_13xx_Interface_Definition.pdf

And one more question, I am trying the query on Page No. 56 according to j2mod code as follows. If it's not correct then please let me know what is the correct format to send this query using J2Mod. (Only Date,Month and Year is changed here)

//Query according to Meter Manual
       WriteMultipleRegistersRequest  writeMultipleRegistersRequest = new WriteMultipleRegistersRequest(460, new Register[]{
                new SimpleInputRegister(20),  //No of Registers to be accessed
                new SimpleInputRegister(40), //Log Download Bytes
                new SimpleInputRegister(1),//Parameter No
                new SimpleInputRegister(4),//Date
                new SimpleInputRegister(3),//Month
                new SimpleInputRegister(19)//Year
            });

Thanks in advance.

steveohara commented 5 years ago

I've checked the specification of the meter and it states that FC16 messages must be sent as per my previous explanation. Take a look at page 21.

I have no idea why your hand-cranked queries are working, because they shouldn't be, according to the meter spec.

Page 5 also states that you will get an Illegal DataValue response if your floating point value isn't valid when writing. Perhaps that is your root cause.

shaileshsathe commented 5 years ago

Yes. You are correct. But if you refer further this document till page no. 56, the meter offers the read multiple registers (Load Profile datalog) functionality using the same function code (FC16).

Here we are facing problem. As I reported my observation 12 days ago about additional bytes found in request(Query) stream I am trying to send to meter.

03 10 01 CA 00 06 0C 00 14 00 28 00 01 00 04 00 03 00 13 *bold bytes are getting added automatically.

I guess due to this extra bytes meter is not responding properly. Even I would like to know how to print received bytes in response from meter

My confidence is that as Docklight utility can send same query and get desired response from meter, My logic using J2MOD lib should also give the same response.

Please refer the below query I am sending to meter using J2MOD lib.

//Query according to Meter Manual
      WriteMultipleRegistersRequest  writeMultipleRegistersRequest = new WriteMultipleRegistersRequest(460, new Register[]{
               new SimpleInputRegister(20),  //No of Registers to be accessed =>20 Registers
               new SimpleInputRegister(40), //Log Download Bytes =>40 Bytes
               new SimpleInputRegister(1),  //Parameter No =>1
               new SimpleInputRegister(4),  //Date =>4
               new SimpleInputRegister(3),  //Month =>3
               new SimpleInputRegister(19)  //Year =>19
           });

Here we are accessing 20 Registers to read not writing 6 Parameters according to Page No.56 of Meter Spec.

Please correct me if I am doing anything wrong here.

Please advise on this.

Looking forward for your usual prompt reply with guidelines.

shaileshsathe commented 5 years ago

Apologies. Mistakenly closed the issue.

steveohara commented 5 years ago

Ok, I see what you're doing wrong. You are supplying the number of registers as a register value, similarly the number of bytes in the message. These are part of the Modbus protocol, not something you can supply yourself, they are calculated by j2mod based on the number of registers you are writing. Try this;

WriteMultipleRegistersRequest  writeMultipleRegistersRequest = new 
    WriteMultipleRegistersRequest(460, new Register[]{
               new SimpleInputRegister(1),  //Parameter No =>1
               new SimpleInputRegister(4),  //Date =>4
               new SimpleInputRegister(3),  //Month =>3
               new SimpleInputRegister(19)  //Year =>19
           });

If that doesn't work, try padding out the registers;

WriteMultipleRegistersRequest  writeMultipleRegistersRequest = new 
    WriteMultipleRegistersRequest(460, new Register[]{
               new SimpleInputRegister(1),  //Parameter No =>1
               new SimpleInputRegister(4),  //Date =>4
               new SimpleInputRegister(3),  //Month =>3
               new SimpleInputRegister(19),  //Year =>19
               new SimpleInputRegister(0),
               new SimpleInputRegister(0),
               new SimpleInputRegister(0),
               new SimpleInputRegister(0),
               new SimpleInputRegister(0),
               new SimpleInputRegister(0)
           });

BTW - Page 56 is telling you exactly the same thing as page 21. It is describing the FC16 Modbus protocol message :)

shaileshsathe commented 5 years ago

Thanks for the prompt response. Tried the way you mentioned, but still output is same "Illegal Data Value". Even I have a doubt about your reply. Here in my test query as per meter document, I need to read 20 Registers (0014) for Daily Energy (01CC in Table 17) category and Capacitive VAr energy (03 in Table 18) sub-category from date of 04/11/2017 date (040B11).

Here all these inputs are user inputs and defined at the time of query. So I guess these should be varying for each query. Where as you are suggesting to just supply Parameter No. and Date as input and J2MOD lib will do rest for me. How J2MOD can understand and take my categories, sub-categories, no. of registers and start date inputs required for query to meter for my desired expected output ? Please clarify and suggest. Your usual prompt reply is expected.

steveohara commented 5 years ago

j2mod is following the Modbus specification exactly so this isn't a j2mod problem. FC16 is a "Write Multiple Registers" so the number of registers and bytes in the protocol message is the number of registers/bytes you are sending, not expecting in the reply. Please read the Modbus specifications that I have sent you links for. A basic understanding of the protocol will at least help you see what the meter is doing.

Have you tried using other Modbus tools like modpoll? I can't believe they will work either but at least you will validate what you are doing.

I don't have anything else to suggest other than you contact the meter manufacturer.

shaileshsathe commented 5 years ago

I have tried with modpoll and mbpoll but it opens those 20 registers values to enter that will be written. So as per my knowledge it must be a customized request and response given on page no 56 which is not according to the Modbus standard. So I will close this issue. Thanks a lot for your time, effort and valuable guidance.