Homebrewed software to communicate with measuring device via Modbus TCP

This post details a similar issue as you've run into:
https://bytes.com/topic/visual-basic-net/answers/349731-integer-byte

It seems that Visual Basic performs overflow checking by default. This is why you are seeing the overflow exception.

Visual Basic is not really intended for low-level data manipulation, so it tries to protect the programmer from doing it. This, of course, makes it much harder when you actually need to perform low-level data manipulation using the language.

Try changing the following two lines
mantissa = CSByte(Registers(1))
mantissa = mantissa << 16


to this single line
mantissa = (Registers(1) << 24) >> 8

What the line above does is shifts out all unused bits (24 of them, since you are using the Integer data type, which is 32-bits) and then shifts the 8 bits we care about to their final position (bits 23...16) while sign-extending the 8-bit value into a 16-bit value (located at bits 31...16). Note that Visual Basic uses an arithmetic shift for this operation, so if the 8-bit value is negative, the bits shifted in will be a value of 1, otherwise the bits shifted in will be 0.
 
Dear sir,

The overflow exception issue seems to be eliminated within possible range of input register contence(input signal)!
This is in favour of replacement code lines:
mantissa=CSByte(Registers(1))
mantissa=mantissa<<16
with the single code line: mantissa=(Registers(1)<<24)>>8 as you kindly suggested.
Please have a look at screnshots below:
21watts.PNG

60watts.PNG

Each screnshot name corresponds to P1 value seen on the display of MC750, when hitting buttons "Refresh" and "Data in PU".
The code runs smoothly without throwing any exceptions, but it seems that data on MC display is unlike to what we see rightwards "Data in Physical Units" label. Please have a look at 7jan.zip archive.

As you stated yesterday 'VB is not really intended for low-level data manipulation' therefore I have been thinking on trying C# that is installed alongside VB.NET and also uses .NET library(MS Visual Studio 2010 Ultimate), maybe it is possible try C#.

Faithfully yours,
N.Qv.
 

Attachments

Unless you have more experience programming in C#, I recommend that you keep using VB.NET. While there are advantages of using a more advanced language like C#, C# (like VB.NET) is also not designed for low-level data manipulation.

The data conversion code is correct and is working properly. Assuming you are reading the right registers from the device (using ModbusClient.ReadInputRegisters(141, 2)), I think your issue now is that you've swapped the order of your registers when outputting the values to your textboxes. In other words, the value shown in your "Input Register 1" textbox should be in the "Input Register 2" textbox, and the value shown in your "Input Register 2" textbox should be in the "Input Register 1" textbox.

How are you assigning the text for these textboxes in your code? The first register (30142) should be assigned to your "Input Register 1" textbox (TextBox4 variable) and the second register (30143) should be assigned to your "Input Register 2" textbox (TextBox5 variable).

Remember, the first register (30142) is the least-significant word (bits 15...0) of the 32-bit value and contains the lower part of the mantissa. The second register (30143) is the most-significant word (bits 31...16) of the 32-bit value and contains the upper part of the mantissa and the exponent.

As a related recommendation, it would be more efficient to remove your "Data in PU" button and simply have the "Refresh" button update all 3 value textboxes (Input Register 1, Input Register 2, and Data in Physical Units) in a single function. This is more efficient because you do not need to perform text to integer conversion for calculating the Data in Physical Units because you can directly use the Registers array variable that is returned by your call to ModbusClient.ReadInputRegisters(141, 2).

If you still have issues with your code after correcting the above, please attach your entire code file. It is very difficult to help you when I need to guess what your code is doing.
 
Good afternoon dear sir,

As you properly noticed and directed me the order of assigning data for further conversion into physical unit was swapped.
It was not correct order I input the registers content into textboxes(before I output them for use in perform of data conversion into physical unit ). That's why the correctly written code below led to unproper conversion.
It was: Registers(0)=Integer.Parse(TextBox4.Text)
Registers(1)=Integer.Parse(TextBox5.Text)
After I toggled output order to:
Registers(0)=Integer.Parse(TextBox5.Text)
Registers(1)=Integer.Parse(TextBox4.Text)

After applying your recomendations and this above, with the help of Goodness, the data conversion gets to perform properly(within possible range of instrument), and outputs data(TextBox6) that matches the data on the MC750 display!

Please have an attached archive files extracted to have a look at the complete operating code of the soft.

The screnshots below shows how the soft operates now:
18watts.PNG

174watts.PNG

Yes, as you stated it would be more efficient to remove "Data in PU" button, and consolidate data extracting and conversion tasks under "Refresh" button click. I separated these tasks to let the soft run without throwing exceptions while having errors on data manipulation codelines. I try to remove "Data in PU" button.

Yours faithfully,

N. Qv.
 

Attachments

Looking at your code, your problem wasn't that the text boxes were reversed. You're still reading the wrong registers. I mentioned this back in post #52. That's also why I stated the qualifier, "Assuming you are reading the right registers from the device (using ModbusClient.ReadInputRegisters(141, 2))" in my last post.

Your code still has the following line
Registers = ModbusClient.ReadInputRegisters(142,2)

This line needs to be
Registers = ModbusClient.ReadInputRegisters(141,2)

You need to change 142 to 141.

You then also need to change the following lines...
Registers(0)=Integer.Parse(TextBox5.Text)
Registers(1)=Integer.Parse(TextBox4.Text)


...back to
Registers(0)=Integer.Parse(TextBox4.Text)
Registers(1)=Integer.Parse(TextBox5.Text)


Currently, your code is reading registers 30143 and 30144, so you're reading half of the Real Power P1 value and half of the Real Power P2 value. Probably the only reason it's working is because the P1 and P2 values are either the same or very similar.

Additionally, the comments in your code regarding registers 30142 and 30143 are incorrect. As I stated in my last post, the first register (30142) is the least-significant word (bits 15...0) of the 32-bit value and contains the lower part of the mantissa. The second register (30143) is the most-significant word (bits 31...16) of the 32-bit value and contains the upper part of the mantissa and the exponent. I recommend you correct these comments to avoid confusion in the future.

Also, if you would like to share your code, please attach your "Form1.vb" file instead of taking screenshots.
 
Dear sir,
I applied the changes you've kindly suggested.
It is showing the right data.
I'll try to be more attentive.
The file "Form1.vb" is attached to this post.

Just before going into further changes to the designed program, I run the modbus poll, extract the content of two successive registers, choose the binary representations, and had an explicit look forward what happens to each bit, when composing one decimal value of 2 binary register content.
Registers.PNG

binary representation.PNG


binary to signed decimal.png
I think that this helps in understanding transformations over registers, since it clearly shows where to apply the bit mask.

Faithfully yours,
N.Qv.
 

Attachments

Dear jschulze,

As you kindly suggested me in one of previous post, it is sufficient if exists only 'Refresh' button on the form of this program.
Thus, 'Data in PU' button which I placed in order to have your data manipulation code have debugged separetely without affecting the rest of software was supposed to being off. I consolidated the code of the program under 'Refresh' button, its clicking accomplishes now: connecting to MC, reading two successive input registers, extracting bits, sign extending, calculating, displaying and then disconnecting. After having done this program runs. Please have a look at screenshots below:
1.PNG

2.PNG

3.PNG

Yours faiithfully,
N.Qv.
 
From post #65
> you're reading half of the Real Power P1 value and half of the Real Power P2 value. Probably the only reason it's working is because the P1 and P2 values are either the same or very similar.

This has come up a couple times in the past on this forum. The assessment is correct.

It intrigues me because it is an example of a digital data value being close, but not exactly what it should be. It's one thing to use the wrong floating point format in interpreting the bit string with a result that is unbelievable, off by hundreds of orders of magnitude. It's another to see a fluctuating process value that is within reason, but it's the wrong value. Many slaves apparently don't 'catch' a FC 03 single read of 2 registers that overlap the 'boundary' of one valid value and the next valid value. But it is what it is.
 
nikoloz,

I'm happy to hear that you've gotten everything working with just a single "Refresh" button in your program and you now have a better understanding of the data conversion after viewing the raw bits of the registers.

I've said it before, but it's worth stating again, the data encoding used on this power monitor is the most complex and confusing of any Modbus device I've encountered. Not to mention the confusion and complications with the Modbus connection to the device when accessing the standard port 502 versus the user-defined port 10001.

Well done completing this task and achieving successful communications and results. I wish you well and hope that you are able to achieve your goal of accessing telemetry data from your substations using this software as a starting point.
 
Dear jschulze, dear gentlemen!

I Heartily thank you for having helping and supporting me.
You practically exposing me to the essence of transformations over data bits(data casting), connecting via the Modbas protocol, etc.
I have been trying getting into further understanding the deep meaning of what you have introduced me.

May God bless you!
Staying with you sincire,

N.Qv.
 
Dear gentlemen,

I made some changes to the program, intending to implement additional mode: "Multy Action".
It means refreshing every 5 sec.
"Stop multy" means disabling "Multy Action" mode.
By the way hitting "Refresh" means its manual single action.
Please have a look at screnshots below:
1.PNG

2.PNG

3.PNG

Yours faithfully,
N.Qv.
 
Dear jschulze, dear gentlemen!

I accomplished a new version of the 'MC_GET_DATA' software.
I strictly followed your recomendations while using 0-based adressing. r0=r1-30000-1.
I have added 'Multy Action' mode which performs reading input registers every 5 seconds. Also 'Single Action' mode makes data refresh upon button single hit.
Also in this version2 user is enabled to change input register to refer to, from text box and change device ID's.
All comments are re-written in English. Editing comments helps better understanding. Input register reading functionality runs under 'Try-Catch ex-End Try' directive which prevents throwing exceptions and software undesired stall. Alongside the error count is performed to have user informed how many errors have occured while reading input registers.
I want to share it with you, you can have it in the archive.
MC_GET_DATA_VERSION_2.PNG

Faithfully yours,
N.Qv.
 

Attachments

Top