You should look at testClient.cpp:
You could use the main() function as a model for your constructor. It will be passed just the IP address of the modbus server (ipAddr). The constructor will then have lines like this:
drvAsynIPPortConfigure("DMC2_IP", ipAddr, 0, 0, 0);
modbusInterposeConfig("DMC2", modbusLinkTCP, 5000, 0);
drvModbusAsyn *pModbus = new drvModbusAsyn("DMC2", "DMC2_IP", 0, 2, -1, 256, dataTypeUInt16, 0, "DMC2_stepper");
You now have a pointer to a modbus driver, and can call its public functions.
Your driver then just makes calls like this to do the actual Modbus IO:
epicsInt16 data[45];
pModbus->doModbusIO(0, MODBUS_READ_MULTIPLE_REGISTERS, 0x6000, data, 45);
I think this should be simpler than the model you are using now.
Mark
From: Tech-talk <tech-talk-bounces at aps.anl.gov> on behalf of Mark Rivers via Tech-talk <tech-talk at aps.anl.gov>
Sent: Saturday, November 23, 2024 10:01 AM
To: shenzb at ihep.ac.cn <shenzb at ihep.ac.cn>
Cc: tech-talk at aps.anl.gov <tech-talk at aps.anl.gov>
Subject: Re: RE: [SPAM] Re: autoConnect could not connect when reading a register by modbus relative addressing
-
In the ANG1Axis::poll
method, the first step is to execute a forced read operation.
-
-
ANG1Driver.cpp: line
546
-
>> status = pasynInt32SyncIO->write(pasynUserForceRead_,
1, DEFAULT_CONTROLLER_TIMEOUT);
-
-
I would like to ask
why this operation must be performed in order to start a new communication. What is the purpose of the member pasynUserForceRead_?
If you look at the startup script for the ANG1 example IOC, you will see that the poll time is set to 100 ms.
That means that the modbus driver has created a background thread that is polling those registers at 10 Hz. I believe that the purpose of the pasynUserForceRead_ is to force an immediate read in case the 10 Hz poller data is too stale.
Note that around the time that this motor driver was written I added support for "absolute" addressing to the Modbus driver. That is documented here:
You enable absolute addressing by passing -1 as the start address in the drvModbusAsynConfigure command. In that case your driver can call this function in the driver:
asynStatus doModbusIO(int slave, int function, int start, epicsUInt16 *data, int len);
In the mode you are currently using you need to create multiple modbus drivers in your startup script, each for a single function code and address range. That is convenient when using Modbus with standard EPICS records (ai, ao, mbbi, mbbo, etc.). However,
in the case of a motor driver absolute addressing may be better.
In the absolute mode you can create a single driver and use if for any function code and any address. That might make more sense for this application. You can even create the modbus driver inside the constructor of your motor driver, hiding it from the user.
You just need to pass the asyn port name of the IP port to your constructor.
Mark
From: shenzb at ihep.ac.cn <shenzb at ihep.ac.cn>
Sent: Saturday, November 23, 2024 2:38 AM
To: Mark Rivers <rivers at cars.uchicago.edu>
Cc: tech-talk at aps.anl.gov <tech-talk at aps.anl.gov>
Subject: Re: RE: [SPAM] Re: autoConnect could not connect when reading a register by modbus relative addressing
Dear Mark,
In the ANG1Axis::poll method, the first step is to execute a forced read operation.
ANG1Driver.cpp: line 546
>> status = pasynInt32SyncIO->write(pasynUserForceRead_, 1, DEFAULT_CONTROLLER_TIMEOUT);
I would like to ask why this operation must be performed in order to start a new communication. What is the purpose of the member pasynUserForceRead_?
The comment in ANF2Driver.cpp at line 504 mentions it, but it also says not sure why this is necessary.
Could you help explain the principle behind it?
By the way, regarding the modbus slave simulator mentioned before, since I haven't received the motor controller yet, the simulator is quite useful for me to start coding.
This is the simulator I am using:
https://modbustools.com/modbus_slave.html
Best regards,
Zhibang
-----原始邮件-----
发件人: "Mark Rivers" <rivers at cars.uchicago.edu>
发送时间: 2024-11-23 07:58:23 (星期六)
收件人: "shenzb at ihep.ac.cn" <shenzb at ihep.ac.cn>
抄送: "tech-talk at aps.anl.gov" <tech-talk at aps.anl.gov>
主题: RE: [SPAM] Re: autoConnect could not connect when reading a register by modbus relative addressing
- I change the modbusLength to 4 as what is done in motorAMCI. And I continuously read the values of 5 neighbor registers, like:
If you set the length to 4 then you can’t read 5 registers, you can only read 4. You need to increase the length to at least 5.
This is the first I heard that you are using a Modbus simulator, not the actual motor controller. Is this correct?
- The modbus slave simulator shows that the master and slave only communicated once. And the autoConnect issue is reported at the last time.
Looking back at your original message I understand why they only communicated once. You set the poll time in this command to 0, which means it does not poll.
- drvModbusAsynConfigure("DM2C_1_1_In_Word", "L0", 1, 3, 24576, 2, 0, 0, "DM2C_stepper")
You need to set a non-zero poll time. For example, this will poll every 100 ms.
- drvModbusAsynConfigure("DM2C_1_1_In_Word", "L0", 1, 3, 24576, 2, 0, 100, "DM2C_stepper")
Mark
Mark
From: shenzb at ihep.ac.cn <shenzb at ihep.ac.cn>
Sent: Friday, November 22, 2024 4:44 PM
To: Mark Rivers <rivers at cars.uchicago.edu>
Cc: tech-talk at aps.anl.gov
Subject: Re: [SPAM] Re: autoConnect could not connect when reading a register by modbus relative addressing
Dear Mark,
Thank you for your response. I can tell when will this issue happen with an example.
I change the modbusLength to 4 as what is done in motorAMCI. And I continuously read the values of 5 neighbor registers, like:
> status = pC_->readReg16(0, &read_val, DEFAULT_CONTROLLER_TIMEOUT);
> status = pC_->readReg16(1, &read_val, DEFAULT_CONTROLLER_TIMEOUT);
> status = pC_->readReg16(2, &read_val, DEFAULT_CONTROLLER_TIMEOUT);
> status = pC_->readReg16(3, &read_val, DEFAULT_CONTROLLER_TIMEOUT);
> status = pC_->readReg16(4, &read_val, DEFAULT_CONTROLLER_TIMEOUT);
The modbus slave simulator shows that the master and slave only communicated once. And the autoConnect issue is reported at the last time.
> readReg16: reg = 0, value= 2020
> readReg16: reg = 1, value= 2021
> readReg16: reg = 2, value= 2022
> readReg16: reg = 3, value= 2023
> 2024/11/23 06:24:07.881 DM2C_1_1_In_Word 4 autoConnect could not connect:
> readReg16: reg = 4, value= 2023
I mean this autoConnect error occurs when I want to send message for the second time.
Best regards,
Zhibang
------------------
发件人: "Mark Rivers" <rivers at cars.uchicago.edu>
发送时间: 2024-11-22 23:03:21
收件人: "tech-talk at aps.anl.gov"
<tech-talk at aps.anl.gov>,
沈治邦 <shenzb at ihep.ac.cn>
主题: [SPAM] Re: autoConnect could not connect
when reading a register by modbus relative addressing
I think I see the problem.
-
drvModbusAsynConfigure("DM2C_1_1_In_Word", "L0", 1, 3, 24576, 2, 0, 0, "DM2C_stepper")
That creates a driver that will use function code 3 to read from register location 24576 (0x6000). It will read 2 registers (the Modbus length argument), so it will read
registers 0x6000 and 0x6001.
・
status = pC_->readReg16(44, &read_val, DEFAULT_CONTROLLER_TIMEOUT);
However, here you are telling it to read relative register 44 (0x2c) which is absolute register 0x602c. That is invalid because you only configured the driver to read 2 registers.
You need to increase the number of registers in the drvModbusAsynConfigure to include all registers you will be accessing.
Mark
Hi,
I am encountering an issue while developing a modbus communication-based motor controller program, similar to the module motorAMCI.
I am attempting to read the values in registers with function code 3 by relative address. For instance, if I want to read the value located in 0x602C, which represents the current position
of the motor. I set the starting address to 0x6000 by the command in st.cmd
drvModbusAsynConfigure("DM2C_1_1_In_Word", "L0", 1, 3, 24576, 2, 0, 0, "DM2C_stepper")
Then to read the motor position, I called the function in driver.cpp with sentence
status = pC_->readReg16(44, &read_val, DEFAULT_CONTROLLER_TIMEOUT);
24657 is 0x6000 and 44 is 0x2C, then I believe that the value at 0x602c could be accessed.
The definition of the readReg16 is the same with the method ANG1Controller::readReg16()
Consequently, I am unable to retrieve the correct register value, and an error is showed:
2024/11/22 17:57:00.751 DM2C_1_1_In_Word 2 autoConnect could not connect:
Could someone please help me figure out what might be causing this issue?
Thank you!
Best regards,
Shen Zhibang
|