diff --git a/Modbus-CAPL/MakeConfig.cfg b/Modbus-CAPL/MakeConfig.cfg index c3ea76a..859bc73 100644 --- a/Modbus-CAPL/MakeConfig.cfg +++ b/Modbus-CAPL/MakeConfig.cfg @@ -1,4 +1,4 @@ -;CANoe Version |4|7|1|35745 MakeConfig +;CANoe Version |4|7|1|52148 MakeConfig Version: 8.2.40 Build 40 32 PRO 5 @@ -13,10 +13,10 @@ VGlobalParameters 2 Begin_Of_Object 20 0 3,100,200,500 -1000000 1.000000 1 1000 1 1 0 0 1 1 1 0 0 0 1 0 0 0 +1000000 1.000000 0 1000 1 1 0 0 1 1 1 0 0 0 1 0 0 0 1 0 -0 1 +0 0 ResetSignalsOnMeasurementStart=1 VDatabaseContainerStreamer 3 Begin_Of_Object 5 @@ -642,7 +642,7 @@ Begin_Of_Multi_Line_String Copyright (c) 2001-2006 Actipro Software LLC. All rights reserved. http://www.ActiproSoftware.com/Products/DotNet/ ---> +--> End_Of_Serialized_Data 3 End_Of_Object VDesktop 3 0 @@ -659,7 +659,7 @@ VUniqueBox 4 Begin_Of_Object VBoxRoot 5 Begin_Of_Object 1 3 -0 2 0 1 -1 -1 -1 -1 22 22 1522 983 +0 2 0 1 -1 -1 -1 -1 -227 39 1073 1000 1 @@ -678,7 +678,7 @@ END_OF_DOCK_INFO 1582 856 END_OF_DESKTOP_DATA 6 -0 1 -1 -1 -1 -1 22 22 1522 983 +0 1 -1 -1 -1 -1 -227 39 1073 1000 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 32767 0 0 0 0 0 0 0 0 0 0 -1 -1 0 0 0 0 0 0 END_OF_DOCK_INFO 0 @@ -686,7 +686,7 @@ END_OF_DOCK_INFO 0 0 1 -999 592 +1188 696 END_OF_DESKTOP_DATA END_OF_DESKTOP_DATA_COLLECTION 0 @@ -2516,7 +2516,7 @@ VUniqueBox 16 Begin_Of_Object VBoxRoot 17 Begin_Of_Object 1 1 -1 1 0 1 -1 -1 -1 -1 0 339 1188 688 +1 1 0 1 -1 -1 -1 -1 0 285 1188 634 1 @@ -2535,7 +2535,7 @@ END_OF_DOCK_INFO 999 591 END_OF_DESKTOP_DATA 6 -0 1 -1 -1 -1 -1 0 339 1188 688 +0 1 -1 -1 -1 -1 0 285 1188 634 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 32767 0 0 0 0 0 0 0 0 0 0 -1 -1 0 0 0 0 0 0 END_OF_DOCK_INFO 1 @@ -2543,7 +2543,7 @@ END_OF_DOCK_INFO 0 0 1 -1188 696 +1188 634 END_OF_DESKTOP_DATA END_OF_DESKTOP_DATA_COLLECTION 0 @@ -2894,7 +2894,7 @@ VUniqueBox 4 Begin_Of_Object VBoxRoot 5 Begin_Of_Object 1 3 -0 0 0 1 -1 -1 -1 -1 0 0 1188 338 +0 0 0 1 -1 -1 -1 -1 0 0 1188 284 1 @@ -2913,7 +2913,7 @@ END_OF_DOCK_INFO 803 901 END_OF_DESKTOP_DATA 6 -0 1 -1 -1 -1 -1 0 0 1188 338 +0 1 -1 -1 -1 -1 0 0 1188 284 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 32767 0 0 0 0 0 0 0 0 0 0 -1 -1 0 0 0 0 0 0 END_OF_DOCK_INFO 1 @@ -2921,7 +2921,7 @@ END_OF_DOCK_INFO 0 0 1 -1188 696 +1188 634 END_OF_DESKTOP_DATA END_OF_DESKTOP_DATA_COLLECTION 0 @@ -2961,7 +2961,7 @@ End_Of_Object VGrMnBox 3 VDOLocalInfoStruct 3 Begin_Of_Object 3 1 -83 +92 VDAOBus 4 Begin_Of_Object 1 1 @@ -3024,7 +3024,7 @@ EOF_ASSEMBLYDATA 1 "include\CAPL\MakeConfig.cbf" VIPBStackSetting 8 Begin_Of_Object 3 -0 +1 1 VIPBAdapterSetting 9 Begin_Of_Object 4 @@ -3075,7 +3075,7 @@ End_Of_Serialized_Data 7 End_Of_Object VProgrammedNode 7 0 0 -Startdelay 0 0 0 +Startdelay 1 0 10 Jitter 0 0 1 0 0 0 0 1 1 ETHERNET_IL.DLL @@ -3091,7 +3091,7 @@ VSimulinkModelViewerConfiguration 7 Begin_Of_Object End_Of_Object VSimulinkModelViewerConfiguration 7 1 0 -3530245935 +2517525802 0 NodeSignalPanelBustypeCount 0 End_Of_Object VSimulationNode 6 @@ -3129,7 +3129,7 @@ NULL End_Of_Object VDOLocalInfoStruct 3 0.000000 0 0 -1 1 0 59420 1 233 1 2882400001 98 331 309 611 2882400002 0 0 0 0 0 0 1 2882400001 1270 1270 311 311 2882400002 0 0 0 1664573080 0 0 3 +1 1 0 59420 1 233 1 2882400001 98 331 371 619 2882400002 0 0 0 0 0 0 1 2882400001 1270 1270 373 373 2882400002 0 0 0 339969840 0 409869284 3 SS_BEGIN_COMMON_INFO 1 0 @@ -3141,7 +3141,7 @@ Ethernet 11 1 1 -7573328 1 0 1 0 0 1 0 0 0 2000 1 +340356552 1 0 1 0 0 1 0 0 71 2000 1 SS_BEGIN_COMMON_INFO 1 3 @@ -3190,7 +3190,7 @@ END_OF_DOCK_INFO END_OF_DESKTOP_DATA 6 0 1 -1 -1 0 0 0 700 662 1043 -6 1 1010 180 0 0 300 180 300 180 0 61440 1 12180 1904 0 0 0 0 260 0 0 0 -1 -1 32767 0 59422 0 0 0 0 0 0 0 1 10 0 0 1 201 0 59419 1 +6 1 1010 180 0 0 300 180 300 180 0 61440 1 12180 1904 0 0 0 0 260 0 0 0 -1 -1 32767 0 59422 0 0 0 0 0 0 0 1 10 0 0 1 263 0 59419 1 END_OF_DOCK_INFO 1 1 @@ -3564,6 +3564,9 @@ End FiltersEnd 0 0 + + + END_OF_WORKSPACE_MEMBER_DATA END_OF_WORKSPACE_MEMBER 1 @@ -3684,7 +3687,7 @@ VIPBGlobalSettings 2 Begin_Of_Object 1 VIPBStackSetting 3 Begin_Of_Object 3 -0 +1 1 VIPBAdapterSetting 4 Begin_Of_Object 4 diff --git a/Modbus-CAPL/ModbusNet.cfg b/Modbus-CAPL/ModbusNet.cfg index 5be2b34..4e806df 100644 --- a/Modbus-CAPL/ModbusNet.cfg +++ b/Modbus-CAPL/ModbusNet.cfg @@ -1,4 +1,4 @@ -;CANoe Version |4|7|1|55201 ModbusNet +;CANoe Version |4|7|1|38816 ModbusNet Version: 8.2.40 Build 40 32 PRO 10 @@ -67,6 +67,7 @@ DialogBegin 1 285 569 816 1103 SymbolExplorerDialogBegin + 1 HistoryBegin 1 0 @@ -745,9 +746,11 @@ Begin_Of_Multi_Line_String kPersistNoLineBreak ey="{28077F35-C142-4ACC-B040-1BF0AB026C11}" Guid="ac9be154-bd12-4ff9-b255-03e05277dbe2" DockedSize="201, 281" FloatingLocation="111, 442" FloatingSize="1192, 514" HasOptions="False" ImageIndex="-1" Text="Trace" TitleBarText="Trace"> +Window Key="{49714911-9568-49CC-A9CE-3B0905658C4A}" Guid="db27ffca-d17e-40f0-a70b-be70fe5eb4ec" State="DockableInsideHost" DockedSize="381, 0" FloatingLocation="1151, 79" FloatingSize="300, 180"> End_Of_Serialized_Data 3 End_Of_Object VDesktop 3 VDesktop 3 Begin_Of_Object @@ -1192,7 +1195,7 @@ End_Of_Serialized_Data 15 End_Of_Object VPredefinedSignalObject 15 [MeasurementObject] Eth 1::Rx Bus load -"%" 1 800080 0. 100. -100. 100. 10 -5 0 0 36000000 0 1 0 0 +"%" 1 800080 0. 100. -100. 100. 10 -5 0 0 36000000 1 1 0 0 VPredefinedSignalObject 15 Begin_Of_Object 1 VHostSignal 16 Begin_Of_Object @@ -1243,7 +1246,7 @@ End_Of_Serialized_Data 15 End_Of_Object VPredefinedSignalObject 15 [MeasurementObject] Eth 1::Tx Bus load -"%" 1 80 0. 100. -100. 100. 10 -5 0 0 36000000 0 1 0 0 +"%" 1 80 0. 100. -100. 100. 10 -5 0 0 36000000 1 1 0 0 VPredefinedSignalObject 15 Begin_Of_Object 1 VHostSignal 16 Begin_Of_Object @@ -1379,7 +1382,7 @@ End_Of_Serialized_Data 15 End_Of_Object VSysVarObject 15 [MeasurementObject] Client_2::InputBits_[0] -"" 223 b86b8 -1. 1. -100. 100. 1 0 0 0 36000000 1 1 0 0 +"" 223 b86b8 -1. 1. -100. 100. 1 0 0 0 36000000 0 1 0 0 VSysVarObject 15 Begin_Of_Object 1 VHostSignal 16 Begin_Of_Object @@ -1413,7 +1416,7 @@ End_Of_Serialized_Data 15 End_Of_Object VSysVarObject 15 [MeasurementObject] Client_2::InputBits_[1] -"" 223 d7ff -1. 1. -100. 100. 1 0 0 0 36000000 1 1 0 0 +"" 223 d7ff -1. 1. -100. 100. 1 0 0 0 36000000 0 1 0 0 VSysVarObject 15 Begin_Of_Object 1 VHostSignal 16 Begin_Of_Object @@ -1447,7 +1450,7 @@ End_Of_Serialized_Data 15 End_Of_Object VSysVarObject 15 [MeasurementObject] Client_3::InputBits_[5] -"" 223 9314ff 0. 1. -100. 100. 1 0 0 0 36000000 1 1 0 0 +"" 223 9314ff 0. 1. -100. 100. 1 0 0 0 36000000 0 1 0 0 VSysVarObject 15 Begin_Of_Object 1 VHostSignal 16 Begin_Of_Object @@ -1481,7 +1484,7 @@ End_Of_Serialized_Data 15 End_Of_Object VSysVarObject 15 [MeasurementObject] Client_2::InputRegisters_[0] -"" 223 ff00 2746. 6071. -100. 100. 500 0 0 0 36000000 1 1 0 0 +"" 223 ff00 2746. 6071. -100. 100. 500 0 0 0 36000000 0 1 0 0 VSysVarObject 15 Begin_Of_Object 1 VHostSignal 16 Begin_Of_Object @@ -1515,9 +1518,9 @@ End_Of_Serialized_Data 15 End_Of_Object VSysVarObject 15 [MeasurementObject] Client_3::InputRegisters_[3] -"" 223 228b22 8987. 19894. -100. 100. 1000 0 0 0 36000000 1 1 0 0 +"" 223 228b22 8987. 19894. -100. 100. 1000 0 0 0 36000000 0 1 0 0 [GraphWindow:x_x_x_x_x_x_WindowBk_Grid_AxisBk_XAxisFr_YAxisFr_x_x_x_x_x_x] -5210346.6428800002 5634098.14585 240215.71337000001 200000 36000000 1 ffffff b2b2b2 ffffff 0 0 0 0 1 1 1 0 +0 423751.50296999997 423751.50296999997 200000 36000000 1 ffffff b2b2b2 ffffff 0 0 0 0 1 1 1 0 0 30 5000 0 0 100 @@ -1528,7 +1531,7 @@ Client_3::InputRegisters_[3] 0 1 41943040 -8 +1 1416 25200245 Grafik-Fenster 1 "" @@ -1773,7 +1776,7 @@ End_Of_Object VTraceAnalysisSingleFilter 17 1 End_Of_Object VTraceAnalysisFilterGroup 16 End_Of_Object VTraceFilterCfg 15 -1 +0 1 0 0 @@ -1796,7 +1799,7 @@ End_Of_Serialized_Data 14 6 1 14 -ver=2: FT TF TF FF FT FF;F T Config;F T Ethernet1;F T GLLogger;T F _Statistics +ver=2: FT TF TF FT FT FT;F T Config;F T Ethernet1;F T GLLogger;T F _Statistics End_Of_Serialized_Data 14 7 0 @@ -1837,29 +1840,29 @@ End_Of_Serialized_Data 14 22 1 14 -ver=2: FF +ver=2: FT End_Of_Serialized_Data 14 23 1 14 -ver=2: FF +ver=2: FT End_Of_Serialized_Data 14 24 1 14 -ver=2: FF +ver=2: FT End_Of_Serialized_Data 14 25 0 26 1 14 -ver=2: FF +ver=2: FT End_Of_Serialized_Data 14 27 1 14 -ver=2: FF +ver=2: FT End_Of_Serialized_Data 14 0 2 @@ -3734,7 +3737,7 @@ End_Of_Serialized_Data 14 0 0 290 -1 +0 160 1 "" End_Of_Object VTraceControlCfg 14 @@ -4471,7 +4474,7 @@ End_Of_Serialized_Data 14 End_Of_Object VSysVarObject 14 [Begin_of_Item] 2 18 -1 1 10 0 0 16777215 +1 1 3 0 0 16777215 0 1000 0 0 [End_of_Item] VSysVarObject 14 Begin_Of_Object @@ -4543,7 +4546,7 @@ End_Of_Serialized_Data 14 End_Of_Object VSysVarObject 14 [Begin_of_Item] 2 17 -1 1 2 0 0 16777215 +1 1 3 0 0 16777215 0 1000 0 0 [End_of_Item] VSysVarObject 14 Begin_Of_Object @@ -4879,7 +4882,7 @@ END_OF_DOCK_INFO 0 0 1 -0 1 -32088 -32000 -1 -1 147 402 1041 893 +0 1 -1 -1 -1 -1 -117 402 777 893 0 1 776 389 @@ -4933,7 +4936,7 @@ End_Of_Object VGrMnBox 3 VDOLocalInfoStruct 3 Begin_Of_Object 3 1 -218 +228 VDAOBus 4 Begin_Of_Object 1 1 @@ -5045,7 +5048,7 @@ End_Of_Object VIPBStackSetting 8 NDebugger::VDebuggerHost 8 Begin_Of_Object 2 0 -26 +27 NDebugger::VFile 9 Begin_Of_Object 1 1 "ModbusTcpCommon.cin" @@ -5176,6 +5179,11 @@ NDebugger::VFile 9 Begin_Of_Object 1 "include\CAPL\include\ModbusStructs.cin" 42 End_Of_Object NDebugger::VFile 9 +NDebugger::VFile 9 Begin_Of_Object +1 + 1 "include\CAPL\include\DeviceInformation.cin" +43 +End_Of_Object NDebugger::VFile 9 VNETStandaloneComponent 9 Begin_Of_Object 1 VNETControlBox 10 Begin_Of_Object @@ -5409,7 +5417,7 @@ VSimulinkModelViewerConfiguration 7 Begin_Of_Object End_Of_Object VSimulinkModelViewerConfiguration 7 1 0 -3569770309 +2097296340 0 NodeSignalPanelBustypeCount 0 End_Of_Object VSimulationNode 6 @@ -5544,7 +5552,7 @@ VSimulinkModelViewerConfiguration 7 Begin_Of_Object End_Of_Object VSimulinkModelViewerConfiguration 7 1 0 -3569770309 +2097296340 0 NodeSignalPanelBustypeCount 0 End_Of_Object VSimulationNode 6 @@ -5803,7 +5811,7 @@ NULL End_Of_Object VDOLocalInfoStruct 3 0.000000 0 0 -1 1 0 59420 1 176 1 2882400001 243 443 430 885 2882400002 0 0 0 0 0 20 1 2882400001 1121 1321 432 632 2882400002 0 0 0 0 0 0 3 +1 1 0 59420 1 176 1 2882400001 -21 179 430 885 2882400002 0 0 0 0 0 20 1 2882400001 857 1057 432 632 2882400002 0 0 0 0 0 0 3 SS_BEGIN_COMMON_INFO 1 0 @@ -6217,14 +6225,14 @@ SymbSelHeaderMgrBegin SymbSelHeaderMgrEnd End Begin -3 0 -1 +3 8 16 3 Modbus modbus Systemvariablen - ( 3 ( 0 ) 0 ) + ( 3 ( 1 ( 3 ( 0 ) 0 ) 0 ) 0 ) SymbSelHeaderMgrBegin 1 4 0 1 200 0 0 diff --git a/Modbus-CAPL/include/CAPL/MakeConfig.can b/Modbus-CAPL/include/CAPL/MakeConfig.can index 5987596..de2422b 100644 --- a/Modbus-CAPL/include/CAPL/MakeConfig.can +++ b/Modbus-CAPL/include/CAPL/MakeConfig.can @@ -1,23 +1,12 @@ /*@!Encoding:1252*/ includes { + #include "include/DeviceInformation.cin" #include "include/ModbusUdpClientCommon.cin" - #include "include/ModbusFunctions.cin" } variables { - struct device // A structure that contains information about an Modbus device - { - char Ip[16]; // String: The IP address - char IpLsb[4]; // String: The last byte of the IP address. Used as index of node name - char IpNet[4]; // String: The second last byte of the IP. Used as index of net - enum Vendor Vendor; // The Vendor (Wago / B&R) - word SerialCode; // Serial Code - word DeviceCode; // Device Code - struct deviceIOs DeviceIOs; // A structure with more information about IOs - }; - char[16] gIps[long]; // List IP addresses. These will be analysed char gScanFirstIp[16]; // The first IP address that will be scanned char gScanLastIp[16]; // The first IP address that will not be scanned anymore. @@ -46,10 +35,10 @@ on preStart strncpy(gIps[3], "192.168.1.8", 16); */ - // Scan a range of IPs for devices. Start and Stop go here - // Please note: Currently .255 will be skipped! Don't use it for devices and as stop address + // Scan a range of IPs for devices (if nothing was set above). Start and Stop go here + // Please note: Currently .255 will be skipped! Don't use it for devices strncpy(gScanFirstIp, "192.168.1.2", 16); - strncpy(gScanLastIp, "192.168.1.10", 16); + strncpy(gScanLastIp, "192.168.1.20", 16); // Name of the project strncpy(name, "Modbus", elCount(name)); @@ -58,7 +47,7 @@ on preStart strncpy(fnSysvar, "include/SysVars/generated.vsysvar", elCount(fnSysvar)); strncpy(fnDbc, "include/DBC/generated.dbc", elCount(fnDbc)); - OutputDebugLevel = Mute; + OutputDebugLevel = Error; } on start @@ -99,10 +88,10 @@ void DetectDevices() { write("Scanning from %s to %s with timeout of %d ms", gScanFirstIp, gScanLastIp, @sysvar::Config::Modbus::RequestTimeout); - gScanFirst = ipGetAddressAsNumber(gScanFirstIp); - gScanLast = ipGetAddressAsNumber(gScanLastIp); + gScanFirst = ipGetAddressAsNumber(gScanFirstIp); // We have to use big endian here + gScanLast = swapDWord(ipGetAddressAsNumber(gScanLastIp)); // But not here :) - write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24); + writeLineEx(0, 0, "%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24); ModbusConnectTo(gScanFirst, @sysvar::Config::Modbus::Port); // Open socket and set variables ModbusReadBits(0, 1); // Start device detection } @@ -110,52 +99,36 @@ void DetectDevices() /// void DetectDevicesNext() { - // next IP - // Note: IP address is stored as big endian, comments are notated as little endian :) - // 0xFE...... --> Skip xxx.xxx.xxx.255 which is broadcast address in 192.168.xxx.0 nets - - // If first three bytes are full (123.255.255.255), set those to 0 and increment the first byte (124.0.0.0) - if ((gScanFirst & 0xFFFFFF00) == 0xFEFFFF00) + gScanFirst = swapDWord(gScanFirst); + gScanFirst++; + if ((gScanFirst & 0xFF) == 0xFF) // .255 { - gScanFirst &= 0x000000FF; - gScanFirst += 0x00000001; - write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24); - } - // If first two bytes are full (124.111.255.255), set those to 0 and increment the second byte (124.112.0.0) - else if ((gScanFirst & 0xFFFF0000) == 0xFEFF0000) - { - gScanFirst &= 0x0000FFF; - gScanFirst += 0x00000100; - write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24); - } - // If first last byte is full (124.112.222.255), set it to 0 and increment the third byte (124.112.223.0) - else if ((gScanFirst & 0xFF000000) == 0xFE000000) - { - gScanFirst &= 0x00FFFFFF; - gScanFirst += 0x00010000; - write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24); - } - // Else simply increment the LSB - else - { - gScanFirst += 0x01000000; + gScanFirst++; + writeLineEx(0, 0, "%d.%d.%d.%d ", gScanFirst >> 24, (gScanFirst >> 16) & 0xFF, (gScanFirst >> 8) & 0xFF, gScanFirst & 0xFF); } - if (gScanFirst == gScanLast) // If this is the last address we stop the detection + if (gScanFirst > gScanLast) { @sysvar::Config::Modbus::MaxTransmissionCount = gMaxTransmissionCount; MakeIpNets(); return; } - writeEx(1, 1, "."); // Write something so the user knows something is happening + gScanFirst = swapDWord(gScanFirst); + + writeEx(0, 0, "."); // Write something so the user knows something is happening gRemoteIP = gScanFirst; // Don't open new socket, it takes too much time. This means we should use UDP here! ModbusReadBits(0, 1); // Scan the next device } /// void OnModbusReadBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) { - DetectDevicesNext(); // Timeout! We will go to the next device + switch (error) + { + case FinalTimeout: + case Exception: + DetectDevicesNext(); // Timeout! We will go to the next device + } } /// void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[], struct ModbusReqRead mbreq) @@ -250,21 +223,11 @@ void OnModbusReadRegistersFailed(enum ModbusRequestError error, enum ModbusExcep case Exception: memcpy_n2h(mbreq, gQueueAck[mbap.TxID].Buffer); - if (mbreq.Address == 0x1000 && ex == IllegalDataAddress) // We requested Wago SerialCode and it didn't work --> Not Wago --> B&R + if (mbreq.Address == 0x1000 && ex == IllegalDataAddress) // We requested Wago SerialCode and it didn't work --> Not Wago --> B&R. Not future proof { gIpsSorted[ips[ADi]].Vendor = Wago; // request information - ADn = 10; - ModbusReadRegisters(0x2011, 1); // Serial Code - ModbusReadRegisters(0x2012, 1); // Device Code - ModbusReadRegisters(0x1022, 1); // Number of AOs (= size in bits) - ModbusReadRegisters(0x1023, 1); // Number of AIs (= size in bits) - ModbusReadRegisters(0x1024, 1); // Number of DOs - ModbusReadRegisters(0x1025, 1); // Number of DIs - ModbusReadRegisters(0x2030, 65); // Connected IO 1 - ModbusReadRegisters(0x2031, 64); // Connected IO 2 - ModbusReadRegisters(0x2032, 64); // Connected IO 3 - ModbusReadRegisters(0x2033, 63); // Connected IO 4 + ADn = DeviceGetInformation(Wago); return; } @@ -283,83 +246,16 @@ void OnModbusReadRegistersFailed(enum ModbusRequestError error, enum ModbusExcep /// void OnModbusReadRegistersSuccess(struct ModbusResReceiveRegisters mbres, struct ModbusReqRead mbreq) { - byte i; - if (mbreq.Address == 0x1000) // We detected a B&R device { gIpsSorted[ips[ADi]].Vendor = BuR; // request further information - ADn = 5; - ModbusReadRegisters(0x1083, 1); // Product Code - ModbusReadRegisters(0x1101, 1); // Number of AIs - ModbusReadRegisters(0x1103, 1); // Number of AOs - ModbusReadRegisters(0x1105, 1); // Number of DIs - ModbusReadRegisters(0x1107, 1); // Number of DOs + ADn = DeviceGetInformation(BuR); return; } - switch (gIpsSorted[ips[ADi]].Vendor) - { - case Wago: - // Parse the received data - switch (mbreq.Address) - { - case 0x2011: - gIpsSorted[ips[ADi]].serialCode = mbres.Data[0]; - break; - case 0x2012: - gIpsSorted[ips[ADi]].deviceCode = mbres.Data[0]; - break; - case 0x1022: - gIpsSorted[ips[ADi]].DeviceIOs.OutputRegisters = mbres.Data[0] / 16; - break; - case 0x1023: - gIpsSorted[ips[ADi]].DeviceIOs.InputRegisters = mbres.Data[0] / 16; - break; - case 0x1024: - gIpsSorted[ips[ADi]].DeviceIOs.OutputBits = mbres.Data[0]; - break; - case 0x1025: - gIpsSorted[ips[ADi]].DeviceIOs.InputBits = mbres.Data[0]; - break; - case 0x2030: - case 0x2031: - case 0x2032: - case 0x2033: - for (i = 0; i < mbreq.Count; i++) - { - if (mbres.Data[i] == 0x0000) // No more devices --> end - break; - ParseDeviceCode(mbres.Data[i], gIpsSorted[ips[ADi]].Vendor, gIpsSorted[ips[ADi]].DeviceIOs); - } - break; - } - break; - case BuR: - // Parse the received data - switch (mbreq.Address) - { - case 0x1083: - gIpsSorted[ips[ADi]].serialCode = mbres.Data[0]; - break; - case 0x1101: - gIpsSorted[ips[ADi]].DeviceIOs.InputRegisters = mbres.Data[0] - 3; // X20BC0087 has 3 AIs when no module is connected... hö? - break; - case 0x1103: - gIpsSorted[ips[ADi]].DeviceIOs.OutputRegisters = mbres.Data[0]; - break; - case 0x1105: - gIpsSorted[ips[ADi]].DeviceIOs.InputBits = mbres.Data[0] * 8; // Unfortunately this is quite imprecise: - // in the process image one module will always fill a whole number of bytes. - // So 4 12DI modules not allocate not 4*12 bit = 6 byte, but 4*16 bit = 64 bit = 8 byte - break; - case 0x1107: - gIpsSorted[ips[ADi]].DeviceIOs.OutputBits = mbres.Data[0] * 8; - break; - } - break; - } + DeviceParseRegister(gIpsSorted[ips[ADi]], mbreq.Address, mbres.Data, mbreq.Count); if (--ADn == 0) // If we received all registers AnalyzeDevicesNext(); @@ -414,9 +310,9 @@ void GenSysvars() for (long ipN : gIpsSorted) { - if (((ipN >> 16) & 0xFF) != net) continue; + DeviceInit(gIpsSorted[ipN].Vendor); PutString(" \n"); - } - else - { - PutString((word)2048); - PutString("\" maxValuePhys=\""); - PutString((word)2048); - PutString("\" />\n"); - } + PutString((word)gDevRegMaxCount); + PutString("\" maxValuePhys=\""); + PutString((word)gDevRegMaxCount); + PutString("\" />\n"); // InputBits PutString(" \n"); - } - else - { - PutString((word)16384); - PutString("\" maxValuePhys=\""); - PutString((word)16384); - PutString("\" />\n"); - } + PutString((word)gDevBitMaxCount); + PutString("\" maxValuePhys=\""); + PutString((word)gDevBitMaxCount); + PutString("\" />\n"); // OutputRegisters PutString(" \n"); - } - else - { - PutString((word)2048); - PutString("\" maxValuePhys=\""); - PutString((word)2048); - PutString("\" />\n"); - } + PutString((word)gDevRegMaxCount); + PutString("\" maxValuePhys=\""); + PutString((word)gDevRegMaxCount); + PutString("\" />\n"); // OutputBits PutString(" \n"); - } - else - { - PutString((word)16384); - PutString("\" maxValuePhys=\""); - PutString((word)16384); - PutString("\" />\n"); - } + PutString((word)gDevBitMaxCount); + PutString("\" maxValuePhys=\""); + PutString((word)gDevBitMaxCount); + PutString("\" />\n"); PutString(" \n"); // Namespace Data diff --git a/Modbus-CAPL/include/CAPL/ModbusClient.can b/Modbus-CAPL/include/CAPL/ModbusClient.can index 881e738..078d912 100644 --- a/Modbus-CAPL/include/CAPL/ModbusClient.can +++ b/Modbus-CAPL/include/CAPL/ModbusClient.can @@ -3,6 +3,7 @@ includes { #include "include\ModbusUdpClientCommon.cin" + #include "include\DeviceInformation.cin" } variables @@ -10,51 +11,20 @@ variables msTimer gtRead; } -// Get information of local network interface such like ip address - on preStart { writeClear(0); setStartdelay(10); - OutputDebugLevel = Warning; + OutputDebugLevel = MbDebug; } on start { - word outputBits, outputRegs, outputBitAddr, outputRegAddr; - - outputBits = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits; - outputRegs = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters; - - switch ((enum Vendor)@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::Vendor) - { - case Wago: - outputBitAddr = 0x200; - outputRegAddr = 0x200; - break; - case BuR: - outputBitAddr = 0x000; - outputRegAddr = 0x800; - break; - } - + DeviceInit(@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::Vendor); ModbusInit(); - while (outputBits > 0) - { - ModbusReadOutBits(outputBitAddr, 2000); - outputBits -= 2000; - } - if (outputBits > 0) - ModbusReadOutBits(outputBitAddr, outputBits); - - while (outputRegs > 0) - { - ModbusReadRegisters(outputRegAddr, 123); - outputRegs -= 123; - } - if (outputRegs > 0) - ModbusReadRegisters(outputRegAddr, outputRegs); + ModbusReadOutBits(gDevOutputBitAddr, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits); + ModbusReadOutRegisters(gDevOutputRegAddr, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters); setTimerCyclic(gtRead, 1, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config::Interval); } @@ -121,20 +91,28 @@ void OnModbusWriteRegistersFailed(enum ModbusRequestError error, enum ModbusExce void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[], struct ModbusReqRead mbreq) { - word i; + word i, offset; - switch(mbreq.Address) + switch (mbres.Header.FuncCode) // We assume that we separate between 0x01 and 0x02 even though the address space may be the same { - case 0x200: // set output bits + case 0x01: // Read output bits sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits"); - for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits; i++) - @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits[i] = bitStatus[i]; + + offset = mbreq.Address - gDevOutputBitAddr; // Get the offset to the base output bit address + for (i = 0; i < mbreq.Count; i++) + @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits[i + offset] = bitStatus[i]; + sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits"); break; - default: // set input bits + + + case 0x02: // Read input bits sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputBits"); - for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputBits; i++) - @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::InputBits[i] = bitStatus[i]; + + offset = mbreq.Address - gDevInputBitAddr; // Get the offset to the base input bit address + for (i = 0; i < mbreq.Count; i++) + @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::InputBits[i + offset] = bitStatus[i]; + sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputBits"); break; } @@ -142,29 +120,30 @@ void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[] void OnModbusReadRegistersSuccess(struct ModbusResReceiveRegisters mbres, struct ModbusReqRead mbreq) { - char str[20*5]; - long fehler; - byte i; + word i, offset; - switch (mbreq.Address) + switch (mbres.Header.FuncCode) // We assume that we separate between 0x03 and 0x04 even though the address space may be the same { - case 0x200: // set output registers + case 0x03: // Read output registers sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputRegisters"); - for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters; i++) - @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputRegisters[i] = mbres.Data[i]; + + offset = mbreq.Address - gDevOutputRegAddr; // Get the offset to the base output register address + for (i = 0; i < mbreq.Count; i++) + @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputRegisters[i + offset] = mbres.Data[i]; + sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputRegisters"); break; - case 0x000: // set input registers + + + case 0x04: // Read input registers sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputRegisters"); - for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters; i++) - @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::InputRegisters[i] = mbres.Data[i]; + + offset = mbreq.Address - gDevInputRegAddr; // Get the offset to the base input bit address + for (i = 0; i < mbreq.Count; i++) + @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::InputRegisters[i + offset] = mbres.Data[i]; + sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputRegisters"); break; - default: - // Not recognized - dbin_to_strhex(mbres.Data, str); - writeLineEx(0, 1, "<%NODE_NAME%> OnModbusReceiveRegisters: Received %d bytes at 0x%04X: %s", mbres.ByteCount, mbreq.Address, str); - break; } } @@ -204,10 +183,9 @@ void OnModbusClientPanics(enum FatalErrors reason) // Key events ------------------------------------------------------------------------- on timer gtRead { - if (@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters > 0) - ModbusReadRegisters(0x0000, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters); - if (@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputBits > 0) - ModbusReadBits(0x0000, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputBits); + ModbusReadRegisters(0x0000, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters); + ModbusReadBits(0x0000, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputBits); + this.Cancel(); } on sysvar %BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits diff --git a/Modbus-CAPL/include/CAPL/include/DeviceInformation.cin b/Modbus-CAPL/include/CAPL/include/DeviceInformation.cin new file mode 100644 index 0000000..019694e --- /dev/null +++ b/Modbus-CAPL/include/CAPL/include/DeviceInformation.cin @@ -0,0 +1,285 @@ +/*@!Encoding:1252*/ +variables +{ + word gDevOutputBitAddr, gDevOutputRegAddr; + word gDevInputBitAddr, gDevInputRegAddr; + word gDevBitMaxCount, gDevRegMaxCount; + word gDevReceiveWindow; + + enum Vendor + { + Wago = 23, + BuR = 2 + }; + struct deviceIOs + { + byte InputRegisters; + word InputBits; + byte OutputRegisters; + word OutputBits; + char Modules[1024]; + }; + struct device // A structure that contains information about an Modbus device + { + char Ip[16]; // String: The IP address + char IpLsb[4]; // String: The last byte of the IP address. Used as index of node name + char IpNet[4]; // String: The second last byte of the IP. Used as index of net + enum Vendor Vendor; // The Vendor (Wago / B&R) + word SerialCode; // Serial Code + word DeviceCode; // Device Code + struct deviceIOs DeviceIOs; // A structure with more information about IOs + }; +} + +// This is for the normal client and for making the sysvars +/// +void DeviceInit(byte vendor) +{ + switch ((enum Vendor) vendor) + { + case Wago: + gDevInputBitAddr = 0x0000; // Wago inputs start at 0x000 + gDevInputRegAddr = 0x0000; + gDevOutputBitAddr = 0x0200; // Wago outputs start at 0x200 + gDevOutputRegAddr = 0x0200; + gDevBitMaxCount = 0x0100; // Wago allows up to 256 inputs + gDevRegMaxCount = 0x0100; + gDevReceiveWindow = 5; // Wago can handle 5 requests simultaneously + break; + case BuR: + gDevInputBitAddr = 0x0000; // B&R inputs start at 0x000 + gDevInputRegAddr = 0x0000; + gDevOutputBitAddr = 0x0000; // B&R digital outputs start at 0x000 + gDevOutputRegAddr = 0x0800; // B&R analog outputs start at 0x800 + gDevBitMaxCount = 0x4000; // B&R allows up to 16348 digital inputs + gDevRegMaxCount = 0x0800; // B&R allows up to 2048 analog inputs + gDevReceiveWindow = 1; // B&R can only handle 1 request at a time + break; + } +} + + + +// This is for making the sysvars (MakeConfig) +/// +void DeviceParseCode(word dev, enum Vendor vendor, struct deviceIOs dios) +{ + byte input; + byte numChannels; + char module[10]; + + switch(vendor) + { + case Wago: // if this is a Wago device + if (dev & 0x8000) // Digital Module + { + numChannels = (dev >> 8) & 0x007F; + + if (dev & 0x0001) // Input Module + { + input = 1; + strncpy(module, "DI%d,", elCount(module)); + } + else if (dev & 0x0002) // Output Module + { + input = 0; + strncpy(module, "DO%d,", elCount(module)); + } + else // mhm. What is it? + { + writeDbg(AlgoError, "ParseDeviceCode: Device code 0x%X cannot be decoded", dev); + OnModbusClientPanics(DeviceCodeUnknown); + } + } + else + { + // http://www.wago.com/wagoweb/documentation/navigate/nm0dx__d.htm + // http://www.wago.com/wagoweb/documentation/navigate/nm0dy__d.htm + switch (dev) + { + case 881: // devices that have no inputs/outputs + return; + case 491: // devices that have 1 inputs + input = 1; + numChannels = 1; + break; + case 452: // devices that have 2 inputs + case 465: + case 470: + case 472: + case 480: + case 454: + case 473: + case 474: + case 466: + case 484: + case 485: + case 492: + case 482: + case 475: + case 467: + case 477: + case 478: + case 456: + case 479: + case 476: + case 483: + case 461: + case 481: + case 462: + case 469: + case 487: + input = 1; + numChannels = 2; + break; + case 493: // devices that have 3 inputs + case 494: + case 495: + input = 1; + numChannels = 3; + break; + case 459: // devices that have 4 inputs + case 453: + case 455: + case 468: + case 457: + case 464: + case 460: + case 463: + input = 1; + numChannels = 4; + break; + case 552: // devices that have 2 inputs + case 585: + case 563: + case 554: + case 550: + case 560: + case 562: + case 556: + input = 0; + numChannels = 2; + case 555: // devices that have 4 inputs + case 553: + case 557: + case 559: + input = 0; + numChannels = 4; + default: // unknown device. Ouch! + writeDbg(AlgoInfo, "Connected device: 750-%d", dev); + return; + } + if (input) + strncpy(module, "AI%d,", elCount(module)); + else + strncpy(module, "AO%d,", elCount(module)); + } + break; // switch(vendor) + default: + writeDbg(AlgoError, "ParseDeviceCode: Unknown vendor id: %d", vendor); + OnModbusClientPanics(VendorIdUnknown); + return; + } + + snprintf(module, elCount(module), module, numChannels); + strncat(dios.Modules, module, elCount(dios.Modules)); +} + +// This function requests more information from the device and return the number of expected results +/// +byte DeviceGetInformation(enum Vendor vendor) +{ + switch (vendor) + { + case Wago: + ModbusReadRegisters(0x2011, 1); // Serial Code + ModbusReadRegisters(0x2012, 1); // Device Code + ModbusReadRegisters(0x1022, 1); // Number of AOs (= size in bits) + ModbusReadRegisters(0x1023, 1); // Number of AIs (= size in bits) + ModbusReadRegisters(0x1024, 1); // Number of DOs + ModbusReadRegisters(0x1025, 1); // Number of DIs + ModbusReadRegisters(0x2030, 65); // Connected IO 1 + ModbusReadRegisters(0x2031, 64); // Connected IO 2 + ModbusReadRegisters(0x2032, 64); // Connected IO 3 + ModbusReadRegisters(0x2033, 63); // Connected IO 4 + return 10; + case BuR: + ModbusReadRegisters(0x1083, 1); // Product Code + ModbusReadRegisters(0x1101, 1); // Number of AIs + ModbusReadRegisters(0x1103, 1); // Number of AOs + ModbusReadRegisters(0x1105, 1); // Number of DIs + ModbusReadRegisters(0x1107, 1); // Number of DOs + return 5; + } + return 0; +} + +// This function parses the received registers +/// +void DeviceParseRegister(struct device device, word address, word data[], word count) +{ + byte i; + + switch (device.Vendor) + { + case Wago: + // Parse the received data + switch (address) + { + case 0x2011: + device.serialCode = data[0]; + break; + case 0x2012: + device.deviceCode = data[0]; + break; + case 0x1022: + device.DeviceIOs.OutputRegisters = data[0] / 16; // Wago returns the size in bits allocated by the module + break; + case 0x1023: + device.DeviceIOs.InputRegisters = data[0] / 16; + break; + case 0x1024: + device.DeviceIOs.OutputBits = data[0]; + break; + case 0x1025: + device.DeviceIOs.InputBits = data[0]; + break; + case 0x2030: + case 0x2031: + case 0x2032: + case 0x2033: + for (i = 0; i < count; i++) + { + if (data[i] == 0x0000) // No more devices --> end + break; + DeviceParseCode(data[i], device.Vendor, device.DeviceIOs); + } + break; + } + break; + case BuR: + // Parse the received data + switch (address) + { + case 0x1083: + device.serialCode = data[0]; + break; + case 0x1101: + device.DeviceIOs.InputRegisters = data[0]; + break; + case 0x1103: + device.DeviceIOs.OutputRegisters = data[0]; + break; + case 0x1105: + device.DeviceIOs.InputBits = data[0] * 8; // Unfortunately this is quite imprecise: + // in the process image one module will always fill a whole number of bytes. + // So 4 12DI modules not allocate not 4*12 bit = 6 byte, but 4*16 bit = 64 bit = 8 byte + // See Modbus X20BC0087 documentation v1.11 p. 22 + break; + case 0x1107: + device.DeviceIOs.OutputBits = data[0] * 8; + break; + } + break; + } +} \ No newline at end of file diff --git a/Modbus-CAPL/include/CAPL/include/ModbusClientCommon.cin b/Modbus-CAPL/include/CAPL/include/ModbusClientCommon.cin index 7189d5b..af2d93d 100644 --- a/Modbus-CAPL/include/CAPL/include/ModbusClientCommon.cin +++ b/Modbus-CAPL/include/CAPL/include/ModbusClientCommon.cin @@ -7,8 +7,6 @@ includes variables { - const word gMaxPacketLength = __size_of(struct ModbusReqWriteRegisters); - msTimer gtRobin; // Timer that sends the packets and watches for timeouts word gTxID = 0x0000; // Transaction Identifier for Modbus. Used as index for gQueue @@ -18,7 +16,7 @@ variables word TimeoutTicks; byte Timeouts; word Length; - byte Buffer[gMaxPacketLength]; + byte Buffer[gModbusMaxTelegramSize]; }; struct QueueElement gQueuePending[long, 2]; struct QueueElement gQueueSent[long, 2]; @@ -48,49 +46,58 @@ void ModbusInit() ModbusConnectTo(ip, @sysvar::Config::Modbus::Port); } -void ModbusMakeHeader(struct ModbusApHeader mbap, word length) +void ModbusMakeHeader(struct ModbusApHeader mbap, word length, byte funcCode) { mbap.TxID = gTxID++; // [2] Transaction ID mbap.Protocol = 0x0000; // [2] Protocol ID = 0 = Modbus mbap.Length = length - __offset_of(struct ModbusApHeader, UnitID); // [2] Length; Number of bytes following mbap.UnitID = 0xFF; // [1] Unit identifier; not relevant + mbap.FuncCode = funcCode; // [1] Function Code } // REGION: ModbusReadBits ------------------------------------------------------------- /// -void ModbusReadInBits(word address, word count) +void ModbusReadInBits(word address, long count) { ModbusReadBits(0x02, address, count); } /// -void ModbusReadBits(word address, word count) +void ModbusReadBits(word address, long count) { ModbusReadBits(0x02, address, count); } /// -void ModbusReadOutBits(word address, word count) +void ModbusReadOutBits(word address, long count) { ModbusReadBits(0x01, address, count); } /// -void ModbusReadBits(byte funcCode, word address, word count) +void ModbusReadBits(byte funcCode, word address, long count) { const byte length = __size_of(struct ModbusReqRead); byte buffer[length]; - struct ModbusReqRead mbr; + word curCount; + struct ModbusReqRead mbreq; - ModbusMakeHeader(mbr.Header, length); - // Payload - mbr.Header.FuncCode = funcCode; // [1] Function Code; 1: Read Coils (DI), 2: Read Discret Inputs (DIO), seems to be the same for WAGO 750-881 - mbr.Address = address; // [2] Start address - mbr.Count = count; // [2] Number of items; 1:max 2000=0x7D0 + // FC1: Read Coils (DO), FC2: Read Discret Inputs (DI) + while (count > 0) + { + curCount = count > gMaxBitsPerRead ? gMaxBitsPerRead : count; + ModbusMakeHeader(mbreq.Header, length, funcCode); - writeDbg(MbDebug, "Sending 'Read Bits' (0x01) command. Addr: 0x%04X, Count: %d", address, count); - - memcpy_h2n(buffer, mbr); - ModbusSend(buffer, length, mbr.Header.TxID); + mbreq.Address = address; // [2] Start address + mbreq.Count = curCount; // [2] Number of items; 1:max 2000=0x7D0 + + writeDbg(MbDebug, "Sending 'Read Bits' (0x01) command. TxID: 0x%04X, Addr: 0x%04X, Count: %d", mbreq.Header.TxID, address, curCount); + + memcpy_h2n(buffer, mbreq); + ModbusSend(buffer, length, mbreq.Header.TxID); + + count -= gMaxBitsPerRead; + address += gMaxBitsPerRead; + } } /// @@ -98,7 +105,7 @@ void OnModbusReceiveBits(byte buffer[]) { struct ModbusResReceiveBits mbres; struct ModbusReqRead mbreq; - byte bitStatus[1968]; + byte bitStatus[gMaxBitsPerRead]; word numBits; byte i, j; @@ -106,7 +113,6 @@ void OnModbusReceiveBits(byte buffer[]) memcpy_n2h(mbreq, gQueueAck[mbres.Header.TxID].Buffer); writeDbg(MbInfo, "Received %d bits from 0x%04X", mbreq.Count, mbreq.Address); - for (i = 0; i < mbres.ByteCount; i++) { for (j = 0; j < 8; j++) @@ -131,23 +137,45 @@ void OnModbusReceiveBitsException(struct ModbusApHeader mbap, enum ModbusExcepti // REGION: ModbusReadRegisters ------------------------------------------------------- /// -void ModbusReadRegisters(word address, word count) // 16 bit +void ModbusReadInRegisters(word address, long count) +{ + ModbusReadRegisters(0x04, address, count); +} +/// +void ModbusReadRegisters(word address, long count) +{ + ModbusReadRegisters(0x04, address, count); +} +/// +void ModbusReadOutRegisters(word address, long count) +{ + ModbusReadRegisters(0x03, address, count); +} +/// +void ModbusReadRegisters(byte funcCode, word address, long count) { const byte length = __size_of(struct ModbusReqRead); - const byte funcCode = 0x03; byte buffer[length]; - struct ModbusReqRead mbr; - - ModbusMakeHeader(mbr.Header, length); - // Payload - mbr.Header.FuncCode = funcCode; // [1] Function Code; 3: Read Holding Registers (AI), 4: Read Input Registers (AIO), seems to be the same for WAGO 750-881 - mbr.Address = address; // [2] Start address - mbr.Count = count; // [2] Number of items; 1:max 125=0x7D + word curCount; + struct ModbusReqRead mbreq; - writeDbg(MbDebug, "Sending 'Read Registers' (0x03) command. Addr: 0x%04X, Count: %d", address, count); + // FC3: Read Holding Registers (AO), FC4: Read Input Registers (AI) + while (count > 0) + { + curCount = count > gMaxRegsPerRead ? gMaxRegsPerRead : count; + ModbusMakeHeader(mbreq.Header, length, funcCode); - memcpy_h2n(buffer, mbr); - ModbusSend(buffer, length, mbr.Header.TxID); + mbreq.Address = address; // [2] Start address + mbreq.Count = curCount; // [2] Number of items; 1:max 125=0x7D + + writeDbg(MbDebug, "Sending 'Read Registers' (0x03) command. TxID: 0x%04X, Addr: 0x%04X, Count: %d", mbreq.Header.TxID, address, curCount); + + memcpy_h2n(buffer, mbreq); + ModbusSend(buffer, length, mbreq.Header.TxID); + + count -= gMaxRegsPerRead; + address += gMaxRegsPerRead; + } } /// @@ -182,21 +210,21 @@ void ModbusWriteBit(word address, byte value) const byte length = __size_of(struct ModbusReqWriteSingle); const byte funcCode = 0x05; // B&R does not support 0x06 byte buffer[length]; - struct ModbusReqWriteSingle mbw; + struct ModbusReqWriteSingle mbreq; if (value >= 1) value = 0xFF; - ModbusMakeHeader(mbw.Header, length); - // Payload - mbw.Header.FuncCode = funcCode; // [1] Function Code; 5: Write Single Coil (DO) - mbw.Address = address; // [2] Output address - mbw.Value = value << 8; // [2] Output value (0x0000: Off, 0xFF00: On) + // FC5: Write Single Coil (DO) + ModbusMakeHeader(mbreq.Header, length, funcCode); - writeDbg(Debug, "Sending 'Write Bit' (0x05) command. Addr: 0x%04X, Value: 0x%02X", address, value); + mbreq.Address = address; // [2] Output address + mbreq.Value = value << 8; // [2] Output value (0x0000: Off, 0xFF00: On) + + writeDbg(Debug, "Sending 'Write Bit' (0x05) command. TxID: 0x%04X, Addr: 0x%04X, Value: %d", mbreq.Header.TxID, address, value); - memcpy_h2n(buffer, mbw); - ModbusSend(buffer, length, mbw.Header.TxID); + memcpy_h2n(buffer, mbreq); + ModbusSend(buffer, length, mbreq.Header.TxID); } /// @@ -224,23 +252,23 @@ void OnModbusConfirmBitException(struct ModbusApHeader mbap, enum ModbusExceptio // REGION: ModbusWriteRegister ------------------------------------------------------ /// -void ModbusWriteRegister(word address, int value) +void ModbusWriteRegister(word address, word value) { const byte length = __size_of(struct ModbusReqWriteSingle); const byte funcCode = 0x06; byte buffer[length]; - struct ModbusReqWriteSingle mbw; + struct ModbusReqWriteSingle mbreq; - ModbusMakeHeader(mbw.Header, length); - // Payload - mbw.Header.FuncCode = funcCode; // [1] Function Code; 5: Write Single Register (AO) - mbw.Address = address; // [2] Output address - mbw.Value = value; // [2] Output value + // 5: Write Single Register (AO) + ModbusMakeHeader(mbreq.Header, length, funcCode); - writeDbg(MbDebug, "Sending 'Write Register' (0x06) command. Addr: 0x%04X, Value: 0x%02X", address, value); + mbreq.Address = address; // [2] Output address + mbreq.Value = value; // [2] Output value - memcpy_h2n(buffer, mbw); - ModbusSend(buffer, length, mbw.Header.TxID); + writeDbg(MbDebug, "Sending 'Write Register' (0x06) command. TxID: 0x%04X, Addr: 0x%04X, Value: %d", mbreq.Header.TxID, address, value); + + memcpy_h2n(buffer, mbreq); + ModbusSend(buffer, length, mbreq.Header.TxID); } /// @@ -270,34 +298,42 @@ void OnModbusConfirmRegisterException(struct ModbusApHeader mbap, enum ModbusExc // REGION: ModbusWriteBits ---------------------------------------------------------- /// -void ModbusWriteBits(word address, word count, byte values[]) +void ModbusWriteBits(word address, long count, byte values[]) { const word maxLength = __size_of(struct ModbusReqWriteBits); const byte funcCode = 0x0F; byte buffer[maxLength]; - struct ModbusReqWriteBits mbw; + struct ModbusReqWriteBits mbreq; + word curCount; byte dataLength; byte overallLength; word i; - dataLength = _ceil(count / 8.0); - overallLength = maxLength - 1968/8 + dataLength; - ModbusMakeHeader(mbw.Header, overallLength); - // Payload - mbw.Header.FuncCode = funcCode; // [1] Function Code; 15: Write Multiple Bits (DOs) - mbw.Address = address; // [2] Output address - mbw.Count = count; // [2] Number of items; 1:max 1968=0x7B0 - mbw.ByteCount = dataLength; // [1] Number of bytes; = ceil(count/8) - memcpy(mbw.Data, values, dataLength); // this is 1 memcpy too much -.- + // FC15: Write Multiple Bits (DOs) + while (count > 0) + { + curCount = count > gMaxBitsPerWrite ? gMaxBitsPerWrite : count; + dataLength = _ceil(curCount / 8.0); + overallLength = maxLength - gMaxBitsPerWrite/8 + dataLength; + ModbusMakeHeader(mbreq.Header, overallLength, funcCode); - writeDbg(MbDebug, "Sending 'Write Bits' (0x0F) command. Addr: 0x%04X, Count: %d", address, count); + mbreq.Address = address; // [2] Output address + mbreq.Count = curCount; // [2] Number of items; 1:max 1968=0x7B0 + mbreq.ByteCount = dataLength; // [1] Number of bytes; = ceil(count/8) + memcpy(mbreq.Data, values, dataLength); // [246] Data; this is 1 unneccessary memcpy :( Well, readability... - memcpy_h2n(buffer, mbw); - ModbusSend(buffer, overallLength, mbw.Header.TxID); + writeDbg(MbDebug, "Sending 'Write Bits' (0x0F) command. Addr: 0x%04X, Count: %d", address, curCount); + + memcpy_h2n(buffer, mbreq); + ModbusSend(buffer, overallLength, mbreq.Header.TxID); + + count -= gMaxBitsPerWrite; + address += gMaxBitsPerWrite; + } } /// -void ModbusWriteBitsB(word address, word count, byte values[]) +void ModbusWriteBitsB(word address, long count, byte values[]) { byte buffer[2]; // length word length; @@ -361,31 +397,38 @@ void OnModbusConfirmBitsException(struct ModbusApHeader mbap, enum ModbusExcepti // REGION: ModbusWriteRegisters ------------------------------------------------------- /// -void ModbusWriteRegisters(word address, word count, word values[]) +void ModbusWriteRegisters(word address, long count, word values[]) { const word maxLength = __size_of(struct ModbusReqWriteRegisters); const byte funcCode = 0x10; byte buffer[maxLength]; - struct ModbusReqWriteRegisters mbw; + struct ModbusReqWriteRegisters mbreq; + word curCount; byte dataLength; word overallLength; word i; - dataLength = 2 * count; - overallLength = maxLength - 2*123 + dataLength; + // FC16: Write Multiple Registers (AOs) + while (count > 0) + { + curCount = count > gMaxRegsPerWrite ? gMaxRegsPerWrite : count; + dataLength = 2 * curCount; + overallLength = maxLength - 2*gMaxRegsPerWrite + dataLength; - ModbusMakeHeader(mbw.Header, overallLength); - // Payload - mbw.Header.FuncCode = funcCode; // [1] Function Code; 16: Write Multiple Registers (AOs) - mbw.Address = address; // [2] Output address - mbw.Count = count; // [2] Number of items; 1:max 123=0x7B - mbw.ByteCount = dataLength; // [1] Number of bytes; = 2 * count + ModbusMakeHeader(mbreq.Header, overallLength, funcCode); - for (i = 0; i < dataLength; i++) - mbw.Data[i] = values[i]; + mbreq.Address = address; // [2] Output address + mbreq.Count = curCount; // [2] Number of items; 1:max 123=0x7B + mbreq.ByteCount = dataLength; // [1] Number of bytes; = 2 * count - memcpy_h2n(buffer, mbw); - ModbusSend(buffer, overallLength, mbw.Header.TxID); + for (i = 0; i < curCount; i++) + mbreq.Data[i] = values[i]; + for ( ; i < gMaxRegsPerWrite; i++) // do we need this? + mbreq.Data[i] = 0; + + memcpy_h2n(buffer, mbreq); + ModbusSend(buffer, overallLength, mbreq.Header.TxID); + } } /// @@ -419,17 +462,17 @@ void ModbusWriteMasks(word address, word and, word or) const word length = __size_of(struct ModbusReqWriteMasks); const byte funcCode = 0x16; byte buffer[length]; - struct ModbusReqWriteMasks mbw; + struct ModbusReqWriteMasks mbreq; - ModbusMakeHeader(mbw.Header, length); - // Payload - mbw.Header.FuncCode = funcCode; // [1] Function Code; 22: Mask Write Registers (AO) - mbw.Address = address; // [2] Output address - mbw.And = and; // [2] AND mask - mbw.Or = or; // [2] OR mask + // FC22: Mask Write Registers (AO) + ModbusMakeHeader(mbreq.Header, length, funcCode); - memcpy_h2n(buffer, mbw); - ModbusSend(buffer, length, mbw.Header.TxID); + mbreq.Address = address; // [2] Output address + mbreq.And = and; // [2] AND mask + mbreq.Or = or; // [2] OR mask + + memcpy_h2n(buffer, mbreq); + ModbusSend(buffer, length, mbreq.Header.TxID); } /// @@ -457,33 +500,48 @@ void OnModbusConfirmMasksException(struct ModbusApHeader mbap, enum ModbusExcept // REGION: ModbusReadWriteRegisters ------------------------------------------------------- /// -void ModbusReadWriteRegisters(word readAddress, word readCount, word writeAddress, word writeCount, int values[]) +void ModbusReadWriteRegisters(word readAddress, long readCount, word writeAddress, long writeCount, word values[]) { const word maxLength = __size_of(struct ModbusReqReadWriteRegisters); const byte funcCode = 0x17; byte buffer[maxLength]; - struct ModbusReqReadWriteRegisters mbw; + struct ModbusReqReadWriteRegisters mbreq; byte dataLength; word overallLength; - word i; + word i, offset; + + offset = 0; + if (readCount > gMaxRegsPerRead - 2) // if we have to split the read request. count = n*max + y + { + ModbusReadRegisters(readAddress, readCount - readCount % gMaxRegsPerRead); // let this function read the main part: n*max + readAddress += readCount - readCount % gMaxRegsPerRead; // increment address by n*max + readCount %= gMaxRegsPerRead; // only read y elements in this function + } + if (writeCount > gMaxRegsPerWrite - 2) // if we have to split the write request. count = n*max + y + { + ModbusWriteRegisters(writeAddress, writeCount - writeCount % gMaxRegsPerWrite, values); // let this function read the main part: n*max + offset = writeCount - writeCount % gMaxRegsPerWrite; // start reading values at n*max + writeAddress += offset; // increment address by n*max + writeCount %= gMaxRegsPerWrite; // only read y elements in this function + } dataLength = 2 * writeCount; - overallLength = maxLength - 2*121 + dataLength; + overallLength = maxLength - 2*(gMaxRegsPerWrite-2) + dataLength; - ModbusMakeHeader(mbw.Header, overallLength); - // Payload - mbw.Header.FuncCode = funcCode; // [1] Function Code; 16: Write Multiple Registers (AOs) - mbw.ReadAddress = readAddress; // [2] Input address - mbw.ReadCount = readCount; // [2] Number of items; 1:max 125=0x7D - mbw.WriteAddress = writeAddress;// [2] Output address - mbw.WriteCount = writeCount; // [2] Number of items; 1:max 121=0x79 - mbw.ByteCount = dataLength; // [1] Number of bytes; = 2 * count + // FC16: Write Multiple Registers (AOs) + ModbusMakeHeader(mbreq.Header, overallLength, funcCode); - for (i = 0; i < dataLength; i++) - mbw.Data[i] = values[i]; + mbreq.ReadAddress = readAddress; // [2] Input address + mbreq.ReadCount = readCount; // [2] Number of items; 1:max 125=0x7D + mbreq.WriteAddress = writeAddress; // [2] Output address + mbreq.WriteCount = writeCount; // [2] Number of items; 1:max 121=0x79 + mbreq.ByteCount = dataLength; // [1] Number of bytes; = 2 * count - memcpy_h2n(buffer, mbw); - ModbusSend(buffer, overallLength, mbw.Header.TxID); + for (i = 0; i < writeCount; i++) + mbreq.Data[i] = values[i + offset]; + + memcpy_h2n(buffer, mbreq); + ModbusSend(buffer, overallLength, mbreq.Header.TxID); } /// @@ -545,7 +603,7 @@ void OnModbusReceive(dword socket, long result, dword address, dword port, byte void OnModbusReceive2(byte buffer[], dword size) { struct ModbusApHeader mbap; - int offset; + long offset; char str[3*20]; if (size < 8) // No complete Modbus Application Header @@ -579,7 +637,7 @@ void OnModbusReceive2OnePacket(byte buffer[], int offset, struct ModbusApHeader // Test unit/device identifier? word i; // counter word length; // length of current packet - byte mbuffer[__size_of(struct ModbusResReceiveRegisters)]; // second buffer where we copy the message. This way the user won't overwrite other packages. + byte mbuffer[gModbusMaxTelegramSize]; // second buffer where we copy the message. This way the user won't overwrite other packages. length = __offset_of(struct ModbusApHeader, UnitID) + mbap.Length; // We cannot check this properly anymore. We have to trust the TCP/UDP stack and the sender... *sigh* @@ -597,7 +655,7 @@ void OnModbusReceive2OnePacket(byte buffer[], int offset, struct ModbusApHeader } // MBAP Header is OK :) Go on - if (!gQueueSent.ContainsKey(mbap.TxID)) // We don't wait for this message! + if (!gQueueSent.ContainsKey(mbap.TxID)) // We don't wait for this message!? return; //write("Received TxID: %d", mbap.TxID); @@ -613,7 +671,6 @@ void OnModbusReceive2OnePacket(byte buffer[], int offset, struct ModbusApHeader return; } - // Copy the message memcpy_off(mbuffer, 0, buffer, offset, length); @@ -780,8 +837,8 @@ on timer gtRobin // Second: send new packets for (long TxID : gQueuePending) { - if (gQueueSent.Size() > 4) // Wago 750-881 cannot handle more than 5 messages at a time :( - continue; + if (gQueueSent.Size() >= gDevReceiveWindow) // Device cannot handle many messages at a time + break; // if packet was sent or the socket is not currently being opened if (ModbusSnd(gQueuePending[TxID].Buffer, gQueuePending[TxID].Length) == 0 || gSocketState != NULL) diff --git a/Modbus-CAPL/include/CAPL/include/ModbusFunctions.cin b/Modbus-CAPL/include/CAPL/include/ModbusFunctions.cin deleted file mode 100644 index fc4a429..0000000 --- a/Modbus-CAPL/include/CAPL/include/ModbusFunctions.cin +++ /dev/null @@ -1,72 +0,0 @@ -/*@!Encoding:1252*/ -variables -{ - struct deviceIOs - { - byte InputRegisters; - word InputBits; - byte OutputRegisters; - word OutputBits; - char Modules[1024]; - }; -} - -void ParseDeviceCode(word dev, enum Vendor vendor, struct deviceIOs dios) -{ - byte input; - byte numChannels; - char module[10]; - - switch(vendor) - { - case Wago: // if this is a Wago device - - if (dev & 0x8000) // Digital Module - { - numChannels = (dev >> 8) & 0x007F; - - if (dev & 0x0001) // Input Module - { - input = 1; - strncpy(module, "DI%d,", elCount(module)); - } - else if (dev & 0x0002) // Output Module - { - input = 0; - strncpy(module, "DO%d,", elCount(module)); - } - else // blööd - { - writeDbg(AlgoError, "ParseDeviceCode: Device code 0x%X cannot be decoded", dev); - OnModbusClientPanics(DeviceCodeUnknown); - } - } - else - { - switch (dev) - { - case 881: // devices that have no inputs/outputs - return; - case 477: // devices that have 2 inputs - input = 1; - numChannels = 2; - break; - default: // unknown device. Ouch! - writeDbg(AlgoInfo, "Connected device: 750-%d", dev); - return; - } - if (input) - strncpy(module, "AI%d,", elCount(module)); - else - strncpy(module, "AO%d,", elCount(module)); - } - break; // switch(vendor) - default: - writeDbg(AlgoError, "ParseDeviceCode: Unknown vendor id: %d", vendor); - OnModbusClientPanics(VendorIdUnknown); - return; - } - - snprintf(module, elCount(module), module, numChannels); - strncat(dios.Modules, module, elCount(dios.Modules)); -} \ No newline at end of file diff --git a/Modbus-CAPL/include/CAPL/include/ModbusStructs.cin b/Modbus-CAPL/include/CAPL/include/ModbusStructs.cin index 5aaa6c1..ef17756 100644 --- a/Modbus-CAPL/include/CAPL/include/ModbusStructs.cin +++ b/Modbus-CAPL/include/CAPL/include/ModbusStructs.cin @@ -1,6 +1,12 @@ /*@!Encoding:1252*/ variables { + // according to Modbus Specification v1.1 + const word gMaxBitsPerRead = 2000; + const word gMaxRegsPerRead = 125; + const word gMaxBitsPerWrite = 1968; + const word gMaxRegsPerWrite = 123; + // A normal Modbus Application Header. Every Modbus Packet begins with these 7 (+FuncCode) Bytes _align(1) struct ModbusApHeader { @@ -31,7 +37,7 @@ variables word Address; word Count; byte ByteCount; - byte Data[246]; // Max length: 1968 bits + byte Data[gMaxBitsPerWrite/8]; // Max length: 1968 bits }; // Write several values to bits starting with Address _align(1) struct ModbusReqWriteRegisters @@ -40,7 +46,7 @@ variables word Address; word Count; byte ByteCount; - word Data[123]; // Max length: 123 registers + word Data[gMaxRegsPerWrite]; // Max length: 123 registers }; // Write AND and OR masks to a holding register _align(1) struct ModbusReqWriteMasks @@ -59,7 +65,7 @@ variables word WriteAddress; word WriteCount; byte ByteCount; - word Data[121]; // Max length: 123-2 registers + word Data[gMaxRegsPerWrite-2]; // Max length: 123-2 registers }; @@ -68,14 +74,14 @@ variables { struct ModbusApHeader Header; byte ByteCount; - byte Data[250]; // Max length: 2000 bits + byte Data[gMaxBitsPerRead/8]; // Max length: 2000 bits }; // Receive several register values _align(1) struct ModbusResReceiveRegisters { struct ModbusApHeader Header; byte ByteCount; - word Data[125]; // Max length: 125 registers + word Data[gMaxRegsPerRead]; // Max length: 125 registers }; // Confirm the write of a single bit/register _align(1) struct ModbusResConfirmSingle @@ -100,6 +106,7 @@ variables word Or; }; + const word gModbusMaxTelegramSize = __size_of(struct ModbusResReceiveRegisters); enum ModbusRequestError @@ -107,7 +114,7 @@ variables Exception, Timeout, FinalTimeout - }; + }; enum ModbusException { None = 0x00, @@ -143,9 +150,4 @@ variables VendorIdUnknown = 0x03, ConnectionError = 0x04 }; - enum Vendor - { - Wago = 23, - BuR = 2 - }; } \ No newline at end of file diff --git a/Modbus-CAPL/include/DBC/MakeConfig.ini b/Modbus-CAPL/include/DBC/MakeConfig.ini index e7255ca..5a90901 100644 --- a/Modbus-CAPL/include/DBC/MakeConfig.ini +++ b/Modbus-CAPL/include/DBC/MakeConfig.ini @@ -52,7 +52,7 @@ COLUMNWIDTHS=125,100,100,150,100,100, HIDDEN= ORDER=0,1,2,3,4, DEFINITIONS=1, -COLUMNWIDTHS=125,125,100,150,100, +COLUMNWIDTHS=125,125,100,150,123, [View_NetworkTxMessages] HIDDEN= ORDER=0,1,2,3,4,5,6,7,8, diff --git a/Modbus-CAPL/include/SysVars/generated.vsysvar b/Modbus-CAPL/include/SysVars/generated.vsysvar index 61a9c19..03feb1f 100644 --- a/Modbus-CAPL/include/SysVars/generated.vsysvar +++ b/Modbus-CAPL/include/SysVars/generated.vsysvar @@ -1,16 +1,6 @@ - - - - - - - - - - @@ -18,25 +8,25 @@ - + - - - - - - - + + + + + + + - - - - + + + + @@ -45,27 +35,37 @@ - + - - - - - - - + + + + + + + - - - - + + + + + + + + + + + + + + - + \ No newline at end of file