AdvancedHMI Software
General Category => Support Questions => Topic started by: neptun2002 on July 26, 2014, 10:52:58 AM
-
Dear Archie I hope that you are in a good condition and I really appreciate your work its awesome project
I send a reply on July 15, 2014, 06:47:20 PM under the Topic: Connect to MODBUS device through USB port (Read 328 times)
About one Problem I found when I Tested the ModbusRTUCom Ver AdvancedHMIBeta363 But I Got no answer
I consider that you are busy too much but I hope you will find some time to read this Topic
Dear Archie the problem is related to Modbus functions 05 & 06
Function 05 Force Single Coil
Function 06 Preset Single Register
Which is as follows for Single Coil there is no need to send the Number of elements and also the command in this case should be FF 00 For Turning on Not 00 01
And also for Preset a Single Register there are no need to send the number of elements and the data length
So There are extra characters in the packet that the driver send to the slave over the serial port any way I summarized what I found in the attached picture pleas have a look
Waiting for your reply
Thank you So much
E.E. MB
-
I did take a look at it and fixed one of the issues. It takes a while to do testing with the ModbusRTU driver because my only means is to use a second computer with a simulator and I can only do that when I am in the office. I will try tomorrow to finish up the fix and post a new release.
-
Dear Archie it is about Tow weeks so far since my last replay . I was digging inside the AdvancedHmi Project and For me it was So hard As this is my first time with Visual Basic and .Net
any way during this periode I tried understand the way the project is connected and I did a lot of tests So I found Some Points Which I Hope they will be Helpful for you to make the fixing much more quick
Some of These points are accisple for me so I made the changes by my self the others wich I found are inside the library and as the source of the library is not available so it si very hard for me to make the changs but I put my suggestion about those points
as I do not have a full over view about all the project and the hierarchical connections
between the functions and the objects I tried my best to find a solutions wich are not changing any thing for the other objects
of course you can do it in the best perfect way I hope this will help
and again I repeat my high appreciation of this great work and wish you all the best
E.E.mb
'******************************************************************************
'AdvancedHMIBeta363\AdvancedHMIDrivers\Modbus\ModbusRTUCom.vb
'*
'*
'*Private Function ExtractData(ByVal RawData As List(Of Byte), ByVal startByte As Integer, ByVal address As ModbusAddress) As String()
'*
'*
'* Reference :
'*
'*
'*
'******************************************************************************
'***************************************
'* Extract the returned data
'***************************************
'Original Code********************************************************************************************************************************************************************
'Private Function ExtractData(ByVal RawData As List(Of Byte), ByVal startByte As Integer, ByVal address As ModbusAddress) As String()
'End Original Code****************************************************************************************************************************************************************
'My Code**************************************************************************************************************************************************************************
Private Function ExtractData(ByVal RawData As List(Of Byte), ByVal startByte As Integer, ByVal address As ModbusAddress, ByVal FunctinCode As Integer) As String()
'
Dim ThePlcAddres As String = address.Address
'End My Code ******************************************************************************************************************************************************************
Dim values(address.NumberOfElements - 1) As String
Dim NumberOfBytes As Integer = CInt(Math.Ceiling(address.BitsPerElement / 8))
'The Added Code****************************************************************
'******************************************************************************
'* Modbus RTU Communication driver
'*
'* Extract Data For Function 1 & 2
'*
'* Reference :
'*
'*
'*
'******************************************************************************
If ((FunctinCode = 1) Or (FunctinCode = 2)) Then
Dim InwichByte As Integer = 0
Dim WichBit As Integer = 0
For CurrentBit As Integer = 1 To (address.NumberOfElements)
InwichByte = CInt(Math.Ceiling(CurrentBit / 8))
WichBit = (CurrentBit Mod 8)
If Not m_TreatDataAsHex Then
values(CurrentBit - 1) = CStr(CLng(RawData(InwichByte) >> (WichBit - 1)) And 1)
Else
'values(i) = CalculationsAndConversions.ByteToHex(RawData(startByte + i * NumberOfBytes))
values(CurrentBit - 1) = CStr(CLng(RawData(InwichByte) >> (WichBit - 1)) And 1)
End If
Next
Return values
End If
'End Of Added Code*************************************************************
Dim i As Integer
While i < address.NumberOfElements And (startByte + i) < Math.Floor(RawData.Count / NumberOfBytes)
'Dim HexByte1 As String = Chr(RawData(startByte + i * NumberOfBytes)) & Chr(RawData(startByte + (i * NumberOfBytes) + 1))
If NumberOfBytes > 1 Then
'Dim HexByte2 As String = Chr(RawData(startByte + (i * NumberOfBytes) + 2)) & Chr(RawData(startByte + (i * NumberOfBytes) + 3))
If Not m_TreatDataAsHex Then
values(i) = CStr(RawData(startByte + i * NumberOfBytes) * 256 + RawData(startByte + i * NumberOfBytes + 1))
Else
values(i) = CalculationsAndConversions.ByteToHex(RawData(startByte + i * NumberOfBytes)) & CalculationsAndConversions.ByteToHex(RawData(startByte + i * NumberOfBytes + 1))
End If
Else
If Not m_TreatDataAsHex Then
values(i) = CStr(RawData(startByte + i * NumberOfBytes + 1))
Else
values(i) = CalculationsAndConversions.ByteToHex(RawData(startByte + i * NumberOfBytes))
End If
End If
i += 1
End While
Return values
End Function
'******************************************************************************
'AdvancedHMIBeta363\AdvancedHMIDrivers\Modbus\ModbusRTUCom.vb
'*
'*
'*Protected Sub DataLinkLayerDataReceived(ByVal sender As Object, ByVal e As PlcComEventArgs)
'*
'*
'* Reference :
'*
'*
'*
'******************************************************************************
Protected Sub DataLinkLayerDataReceived(ByVal sender As Object, ByVal e As PlcComEventArgs)
'* Not enough data to make up a FINS packet
'Dim TheCoilNomber As String = SavedResponse(e.TransactionNumber).PlcAddress
If e.RawData Is Nothing OrElse e.RawData.Count < 4 Then
Exit Sub
End If
Dim CoileNumber As Integer = (e.RawData(3) * 16 + e.RawData(2))
Dim RTU As New ModbusRTUFrame(New List(Of Byte)(e.RawData).ToArray, e.RawData.Count)
Dim sid As Integer = e.TransactionNumber
If Not TNS.IsMyTNS(sid) Then
Exit Sub
End If
TNS.ReleaseNumber(sid)
SavedResponse(sid) = e
'Dim HostFrame As New HostLinkFrame(e.RawData)
e.PlcAddress = SavedRequests(sid).Address
'* Is it a FINS excapsulated command?
'If HostFrame.HeaderCode = "FA" Then
'* Extract the RTU Frame
'Dim RTU As New ModbusRTUFrame(HostFrame.EncapsulatedData)
If RTU.PDU.ExceptionCode = 0 Then
If Not SavedRequests(sid).IsWrite Then
'* Extract the data
'Dim values() As String = ExtractData(New List(Of Byte)(Fins.GetEncapsulatedData), 0, SavedRequests(sid))
'Original Code******************************************************************************
'Dim values() As String = ExtractData(RTU.PDU.EncapsulatedData, 1, SavedRequests(sid))
'End Original Code**************************************************************************
'My Code************************************************************************************
'Private Function ExtractData(ByVal RawData As List(Of Byte), ByVal startByte As Integer, ByVal address As ModbusAddress) As String()
Dim _startByte As Integer = 1
Dim values() As String = ExtractData(RTU.PDU.EncapsulatedData, _startByte, SavedRequests(sid), RTU.PDU.FunctionCode)
'End My Code *******************************************************************************
For i As Integer = 0 To values.Length - 1
e.Values.Add(values(i))
Next
'*******************************************************************************************
'* Verify that enough elements were returned before continuing V1.11 6-MAR-12
If e.Values.Count < SavedRequests(sid).NumberOfElements Then
Exit Sub
End If
If m_SynchronizingObject IsNot Nothing Then
'* Is this from a subscription?
If Not SavedRequests(sid).InternallyRequested Then
Dim Parameters() As Object = {Me, e}
m_SynchronizingObject.BeginInvoke(drsd, Parameters)
Else
Dim i As Integer
'* 07-MAR-12 V1.12 If a subscription was deleted, then ignore
Dim SavedCount As Integer = PolledAddressList.Count
While i < PolledAddressList.Count
'* 06-MAR-12 V1.11 Make sure there are enough values returned (4th condition)
'* trap and ignore because subscription may change in the middle of processin
Try
If SavedRequests(sid).ReadFunctionCode = PolledAddressList(i).Address.ReadFunctionCode AndAlso _
SavedRequests(sid).Element <= PolledAddressList(i).Address.Element AndAlso _
(SavedRequests(sid).Element + SavedRequests(sid).NumberOfElements) >= (PolledAddressList(i).Address.Element + PolledAddressList(i).Address.NumberOfElements) AndAlso _
(PolledAddressList(i).Address.Element - SavedRequests(sid).Element + PolledAddressList(i).Address.NumberOfElements) <= SavedResponse(sid).Values.Count Then
Dim f As New PlcComEventArgs(New Byte() {0}, PolledAddressList(i).Address.Address, CUShort(sid))
Dim index As Integer = 0
While index < PolledAddressList(i).Address.NumberOfElements
f.Values.Add(SavedResponse(sid).Values(PolledAddressList(i).Address.Element - SavedRequests(sid).Element + index))
index += 1
End While
Dim x As Object() = {Me, f}
'm_SynchronizingObject.Invoke(PolledAddressList(i).dlgCallBack, x)
Try
'* 07-MAR-12 V1.12
If SavedCount = PolledAddressList.Count Then
m_SynchronizingObject.BeginInvoke(PolledAddressList(i).dlgCallBack, x)
End If
Catch ex As Exception
Dim debug = 0
End Try
End If
Catch ex As Exception
Dim debug = 0
End Try
i += 1
End While
End If
Else
RaiseEvent DataReceived(Me, e)
End If
End If
SavedRequests(sid).Responded = True
Else
If m_SynchronizingObject IsNot Nothing Then
Dim Parameters() As Object = {Me, New PlcComEventArgs(RTU.PDU.ExceptionCode, "Error Code " & CalculationsAndConversions.ByteToHex(CByte(RTU.PDU.ExceptionCode >> 8)))}
m_SynchronizingObject.BeginInvoke(errorsd, Parameters)
Else
RaiseEvent ComError(Me, New PlcComEventArgs(RTU.PDU.ExceptionCode, "Error Code " & CalculationsAndConversions.ByteToHex(CByte(RTU.PDU.ExceptionCode >> 8))))
End If
SavedRequests(sid).ErrorReturned = True
End If
'End If
End Sub
Namespace MfgControl.AdvancedHMI.Drivers.Modbus
Public Class ModbusPDUFrame
'******************************************************************************
'* Namespace MfgControl.AdvancedHMI.Drivers.Modbus
'* Public Class ModbusPDUFrame
'* Public Sub New(ByVal functionCode As Byte, ByVal address As ModbusAddress, ByVal data As Byte())
'*
'* Reference :
'*
'* Note:
'* My Suggestion Code
'******************************************************************************
Public Sub New(ByVal functionCode As Byte, ByVal address As ModbusAddress, ByVal data As Byte())
Me.FullPacket = New List(Of Byte)
Me.FunctionCode = functionCode
'****************************************************************************
'****************************************************************************
'****************************************************************************
If (Me.FunctionCode <> 5) And (Me.FunctionCode <> 6) Then
'The original Code ******************************************************
Me.m_EncapsulatedData = New List(Of Byte)(New Byte() {CByte((address.Element >> 8)), CByte((address.Element And &HFF)), CByte((address.NumberOfElements >> 8)), CByte((address.NumberOfElements And &HFF))})
Me.m_EncapsulatedData.Add(CByte((data.Length And &HFF)))
Dim num2 As Integer = (data.Length - 1)
Dim i As Integer = 0
Do While (i <= num2)
Me.m_EncapsulatedData.Add(data(i))
i += 1
Loop
'End Of the Original Code Block *****************************************
ElseIf Me.FunctionCode = 5 Then
If data(0) = 1 Then
Me.m_EncapsulatedData = New List(Of Byte)(New Byte() {CByte((address.Element >> 8)), CByte((address.Element And &HFF)), CByte(&HFF), CByte((&H0))})
Else
Me.m_EncapsulatedData = New List(Of Byte)(New Byte() {CByte((address.Element >> 8)), CByte((address.Element And &HFF)), CByte(&H0), CByte((&H0))})
End If
ElseIf Me.FunctionCode = 6 Then
'CByte((address.Element >> 8)), CByte((address.Element And &HFF)), CByte((address.NumberOfElements >> 8)), CByte((address.NumberOfElements And &HFF))
Me.m_EncapsulatedData = New List(Of Byte)(New Byte() {CByte((address.Element >> 8)), CByte((address.Element And &HFF))})
'Me.m_EncapsulatedData.Add(CByte((data.Length And &HFF)))
Dim num2 As Integer = (data.Length - 1)
Dim i As Integer = 0
Do While (i <= num2)
Me.m_EncapsulatedData.Add(data(i))
i += 1
Loop
End If
'****************************************************************************
'****************************************************************************
'****************************************************************************
End Sub
Later also I found This Bug
'******************************************************************************
'* Namespace MfgControl.AdvancedHMI.Drivers.Modbus
'* Public Class ModbusPDUFrame
'* Public Sub New(ByVal packet As Byte(), ByVal lengthOfThePacket As Integer, ByVal offset As Integer)
'*
'* Reference :
'*
'* Note:
'* My Suggestion Code
'******************************************************************************
Public Sub New(ByVal packet As Byte(), ByVal lengthOfThePacket As Integer, ByVal offset As Integer)
Me.FullPacket = New List(Of Byte)
'Original Code*********************************************************************************
'offset = 0 From caller
'End Original Code*****************************************************************************
If (offset < 0) Then
offset = 0
End If
'Original Code*********************************************************************************
'Me.m_FunctionCode = packet((0 + offset)) 'The original Bug
'End Original Code*****************************************************************************
'My Code***************************************************************************************
Me.m_FunctionCode = packet((1 + offset))
'End My Code **********************************************************************************
If ((Me.m_FunctionCode >= &H80) AndAlso (packet.Length > 1)) Then
Me.m_ExceptionCode = packet((1 + offset))
Else
Me.m_ExceptionCode = 0
End If
Me.m_EncapsulatedData = New List(Of Byte)
If ((lengthOfThePacket + offset) > packet.Length) Then
lengthOfThePacket = (packet.Length - offset)
End If
Dim newPacketLength As Integer = ((lengthOfThePacket + offset) - 1)
Dim i As Integer = (2 + offset)
Do While (i <= newPacketLength)
Me.m_EncapsulatedData.Add(packet(i))
i += 1
Loop
End Sub 'Stop ************************************************************************************
-
Hello Archi Thank you so much for the new version AdvancedHMIBetaV364 but unfortunately still has bugs you changed the class ModbusPDUFrame for the function 5 only
but for function 6 "writing a single Register" still has a problem the only value which is written to the register is 1 which is I think the number of elements pleas see my suggestion For ModbusPDUFrame on my previous reply it should work well
and also when using more buttons I gut an this error message
"Additional information: Index was out of range. Must be non-negative and less than the size of the collection."
I could not determine exactly what is it yet
But I think It is related to my last point in my previous reply
'Original Code********************************************************************************
'Me.m_FunctionCode = packet((0 + offset)) 'The original Bug
'End Original Code*****************************************************************************
'My Code*************************************************************************************
Me.m_FunctionCode = packet((1 + offset))
'End My Code *********************************************************************************
I think also Class ModbusRTUCom has an issue pleas notice the points I mentioned in my previous reply
thank you so much
-
check the discussion over here - http://advancedhmi.com/forum/index.php?topic=410.msg1433
with build 3.65 he just pushed, reading coils works great now. however still some weird issue with writing (function code 05)
-
Dear Archie Thank you so much for your efforts in this great project
I tested the new version 368 but unfortunately it is not working good like the previews one 367
so in my testing for function 1 one using a toggle switch it is not work So
I found one point that you have to have a look
'****************************************************************************************************
Namespace MfgControl.AdvancedHMI.Drivers.ModbusRTU
Public Class ModbusRTUDataLinkLayer
'****************************************************************************************************
'The original Code ***********************************************************************************
Public Function SendRTUFrame(ByVal RTUPacket As ModbusRTUFrame, ByVal OwnerObjectID As Long) As Integer
Dim num As Integer
Me.SendData(RTUPacket.GetByteStream, OwnerObjectID)
Return num
End Function
'****************************************************************************************************
'My Suggestion***************************************************************************************
Public Function SendRTUFrame(ByVal RTUPacket As ModbusRTUFrame, ByVal OwnerObjectID As Long) As Integer
Dim num As Integer = 0
num = Me.SendData(RTUPacket.GetByteStream, OwnerObjectID)
Return num
End Function
'****************************************************************************************************
also I found that the returned value from function num = Me.SendData(RTUPacket.GetByteStream, OwnerObjectID)
is not stable I mean it is not always reflect an index for valid data even if there is a response from PLC Pleas Have a look to the attached Pic.
also I can notice that is the traffic is slower may be this is the point ??
So pleas Have a deeper look
Thank you
-
Thanks for the additional feedback. I corrected the first problem. The second in your image shouldn't be a problem because WaitForResponse returns a -20 on a timeout and there will not try to access the SavedResponses
-
Yes Archie I realise this point what I mean that the CurrentTransactionNumber
as you can see in the pic
is not always correct especially if there are heavy traffic on the serial
but with ver 367 this problem was not exist
I could not exactly find the problem but I am sure it is in this function
Namespace MfgControl.AdvancedHMI.Drivers.ModbusRTU
Public Class ModbusRTUDataLinkLayer
Public Function GetNextTransactionNumber(ByVal max As Integer) As Integer
with ver 367 the NextTransactionNumber always comming right
pleas have a deeper look to the new method GetNextTransactionNumber
thank you so much
-
I guess I'm not understanding how you determine that it is not coming out correct. The transaction number is a sequential number that is tagged along with the packet, so the requesting routine can identify it as it's own packet and which packet.
Version 3.68 let's the data link layer generate the transaction number instead of a separate class. This closed a gap that allowed a disconnect between the packets that did not respond and releasing the transaction numbers for packets that did not get a response. For instance, let's say a transaction number was requested and the packet was put in the queue to be sent. In the mean time an error on the serial port could discard the packet and the transaction number would miss the notification. Then it raised the question of how long should it wait before it would be released. If it took too long, all the numbers could get tied up. If it was too soon, then packet responses would not be able to get to the right owner. By letting the data link layer generate this number, it could control the release of it much more efficiently.
I tested this by setting the driver PollRateOverride to 50ms and udping 5 numbers. It ran for 10 hours without missing a single packet.
-
Thank you so much Archie I appreciate your reply infact now I have bothe version the 367 works great but same on over 368 is not working good as I taled I just guess that is the point.
The setup that I am testing is 32 puttons from address 00001 to 00032
In toggel mode with plcvalue the same address and visible for all 00001
Plus eight holding registers 40001 to 40008
The problem occuers on click on the toggel switches sometimes it is OK sometimes
Read error always
Same arrangement on ver 367 do not miss a single click on the toggel switches
I will try to catch the problem if I can
Thank you so much
-
What is your baud rate? Did you change the value in PollRateOverride?
I will try to replicate this problem tonight or tomorrow.
-
No I did not change PollRateOverride
And
The baud rate is 9600
-
I tried to replicate this problem and the closest I have been able to come is to remove all of the delays. If the next write was sent less than 1ms after the last read, the PLC would not respond to it. This would occur once every few minutes. I am testing on a Click PLC with 38400 baud rate and a PollRateOverride of 50ms. I confirmed the PLC was actually not responding by using a serial port monitor program.
I modified the code to insert a minimum delay between the previous read and the next write. Each time a response is missed, it increases the delay by 1ms up to a maximum of 20ms. I let the program run for 5 hours and the delay increased to 7ms. After that it never missed another read or write.
I'll release these changes in version 3.69
Can you run a serial port monitor to see if your device is definitely not responding and if not, what is the time in between each packet? I am using Device Monitoring Studio by HHD software.
-
Thank you so much Archie for all these efforts
what you describe make cense if the packet is coming faster than the PLC then the PLC definitely will not respond
But I have to say what I noticed when I was debugging I noticed that the packet which is related to the button click is coming out after the error occurred in other words it sounds like if there are some thing wrong with the queue " Me.SendQue.Enqueue(item) "
Unfortunately I do not have (Device Monitoring Studio by HHD software) to test with.
it coast about 400$
So any way I think that I will wait until you relies the new 369 Ver to test again
thank you so much
-
Version 3.69 is now posted. I did a full implementation of the 3.5 characters of silence between packets as detailed in the Modbus specification. This should make the driver work with any device that closely complies with this.
-
This What I call PERFECT WORK Archie congratulations
thank you so much