diff --git a/Modbus-CAPL/ModbusNet.cfg b/Modbus-CAPL/ModbusNet.cfg index 4defbba..a266e26 100644 --- a/Modbus-CAPL/ModbusNet.cfg +++ b/Modbus-CAPL/ModbusNet.cfg @@ -1,17 +1,17 @@ -;CANoe Version |4|7|1|38833 ModbusNet +;CANoe Version |4|7|1|55221 ModbusNet Version: 8.2.40 Build 40 32 PRO 10 -APPDIR Vector.CANoe.SignalGenerators.DLL -Vector.CANoe.SignalGenerators, Version=8.2.40.0, Culture=neutral, PublicKeyToken=null -Vector.CANoe.SignalGenerators.ComponentWrapper -1 -1.0.1 APPDIR Vector.CANoe.Debugger.DLL Vector.CANoe.Debugger, Version=8.2.40.0, Culture=neutral, PublicKeyToken=null Vector.CANoe.Debugger.DebuggerComponent -2 +1 1.0.0 +APPDIR Vector.CANoe.SignalGenerators.DLL +Vector.CANoe.SignalGenerators, Version=8.2.40.0, Culture=neutral, PublicKeyToken=null +Vector.CANoe.SignalGenerators.ComponentWrapper +2 +1.0.1 VGlobalConfiguration 1 Begin_Of_Object 17 VGlobalParameters 2 Begin_Of_Object @@ -462,10 +462,8 @@ VSVConfigurationStreamer 3 Begin_Of_Object 2 -2 - 1 "include\SysVars\generated.vsysvar" 1 - 1 "include\SysVars\airbus.vsysvar" + 1 "include\SysVars\generated.vsysvar" 1 End_Of_Object VSVConfigurationStreamer 3 @@ -748,9 +746,9 @@ 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"> +ock="Bottom" DockOperationType="TopOuter" IsTopMost="True" /> End_Of_Serialized_Data 3 End_Of_Object VDesktop 3 VDesktop 3 Begin_Of_Object @@ -1520,7 +1518,7 @@ End_Of_Object VSysVarObject 15 Client_3::InputRegisters_[3] "" 223 228b22 8971. 19903. -100. 100. 1000 0 0 0 36000000 1 1 0 0 [GraphWindow:x_x_x_x_x_x_WindowBk_Grid_AxisBk_XAxisFr_YAxisFr_x_x_x_x_x_x] -0 315679.86736999999 315679.86736999999 200000 36000000 1 ffffff b2b2b2 ffffff 0 0 0 0 1 1 1 0 +721742.55151999998 1037422.41889 423751.50296999997 200000 36000000 1 ffffff b2b2b2 ffffff 0 0 0 0 1 1 1 0 0 30 5000 0 0 100 @@ -1799,7 +1797,7 @@ End_Of_Serialized_Data 14 6 1 14 -ver=2: FT TF TF FT FT FT;F T Airbus;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 @@ -4936,7 +4934,7 @@ End_Of_Object VGrMnBox 3 VDOLocalInfoStruct 3 Begin_Of_Object 3 1 -243 +238 VDAOBus 4 Begin_Of_Object 1 1 @@ -5417,7 +5415,7 @@ VSimulinkModelViewerConfiguration 7 Begin_Of_Object End_Of_Object VSimulinkModelViewerConfiguration 7 1 0 -3090661822 +169746433 0 NodeSignalPanelBustypeCount 0 End_Of_Object VSimulationNode 6 @@ -5552,7 +5550,7 @@ VSimulinkModelViewerConfiguration 7 Begin_Of_Object End_Of_Object VSimulinkModelViewerConfiguration 7 1 0 -3090661822 +169746433 0 NodeSignalPanelBustypeCount 0 End_Of_Object VSimulationNode 6 @@ -5585,7 +5583,7 @@ VBoxRoot 9 Begin_Of_Object 1 3 1 1 1 1 0 166 -8 -30 61 86 1093 577 -Ethernet Packet Builder + 1 MDI_DOCK_INFO_END @@ -5663,11 +5661,105 @@ EOF_MBSSDATA 1 0 1 - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -5678,104 +5770,10 @@ EOF_MBSSDATA - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + End_Of_Object VSSPlugInConfiguration 6 NULL @@ -5836,7 +5834,6 @@ Misc SS_END_COMMON_INFO - EOF_BUSDATA 1 _Start_VPRBSManager 1 @@ -6227,14 +6224,14 @@ SymbSelHeaderMgrBegin SymbSelHeaderMgrEnd End Begin -3 1 -1 +3 8 16 3 Modbus modbus Systemvariablen - ( 3 ( 1 ( 0 ) 0 ) 0 ) + ( 3 ( 1 ( 3 ( 0 ) 0 ) 0 ) 0 ) SymbSelHeaderMgrBegin 1 4 0 1 200 0 0 @@ -7245,319 +7242,7 @@ End_Of_Object VAutoRunPreLoggingCaplBox 3 End_Of_Object VStandaloneLoggingUserConfig 2 Mapping::VMappingManager 2 Begin_Of_Object 1 -6 -Mapping::VConfiguredMappingRelation 3 Begin_Of_Object -2 -3 -ValueObjectConfiguration::VConfiguredSysVar 4 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 Begin_Of_Object -1 -VConfigSysVar 7 Begin_Of_Object -1 -VConfigEvent 8 Begin_Of_Object -1 -End_Of_Object VConfigEvent 8 -Ethernet1::Client_2::Data -InputRegisters -End_Of_Object VConfigSysVar 7 -End_Of_Object ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 -End_Of_Object ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 0 -0 -End_Of_Object ValueObjectConfiguration::VConfiguredSysVar 4 - -End_Of_Serialized_Data 3 -3 -ValueObjectConfiguration::VConfiguredSysVar 4 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 Begin_Of_Object -1 -VConfigSysVar 7 Begin_Of_Object -1 -VConfigEvent 8 Begin_Of_Object -1 -End_Of_Object VConfigEvent 8 -Airbus::R14 -S5_1 -End_Of_Object VConfigSysVar 7 -End_Of_Object ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 -End_Of_Object ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 --1 -0 -End_Of_Object ValueObjectConfiguration::VConfiguredSysVar 4 - -End_Of_Serialized_Data 3 -1 -1 -0 -1 -0 -End_Of_Object Mapping::VConfiguredMappingRelation 3 -Mapping::VConfiguredMappingRelation 3 Begin_Of_Object -2 -3 -ValueObjectConfiguration::VConfiguredSysVar 4 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 Begin_Of_Object -1 -VConfigSysVar 7 Begin_Of_Object -1 -VConfigEvent 8 Begin_Of_Object -1 -End_Of_Object VConfigEvent 8 -Ethernet1::Client_3::Data -InputRegisters -End_Of_Object VConfigSysVar 7 -End_Of_Object ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 -End_Of_Object ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 -3 -0 -End_Of_Object ValueObjectConfiguration::VConfiguredSysVar 4 - -End_Of_Serialized_Data 3 -3 -ValueObjectConfiguration::VConfiguredSysVar 4 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 Begin_Of_Object -1 -VConfigSysVar 7 Begin_Of_Object -1 -VConfigEvent 8 Begin_Of_Object -1 -End_Of_Object VConfigEvent 8 -Airbus::R14 -S5_2 -End_Of_Object VConfigSysVar 7 -End_Of_Object ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 -End_Of_Object ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 --1 -0 -End_Of_Object ValueObjectConfiguration::VConfiguredSysVar 4 - -End_Of_Serialized_Data 3 -1 -0.30517578125 -0 -1 -0 -End_Of_Object Mapping::VConfiguredMappingRelation 3 -Mapping::VConfiguredMappingRelation 3 Begin_Of_Object -2 -3 -ValueObjectConfiguration::VConfiguredSysVar 4 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 Begin_Of_Object -1 -VConfigSysVar 7 Begin_Of_Object -1 -VConfigEvent 8 Begin_Of_Object -1 -End_Of_Object VConfigEvent 8 -Ethernet1::Client_2::Data -InputBits -End_Of_Object VConfigSysVar 7 -End_Of_Object ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 -End_Of_Object ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 -0 -0 -End_Of_Object ValueObjectConfiguration::VConfiguredSysVar 4 - -End_Of_Serialized_Data 3 -3 -ValueObjectConfiguration::VConfiguredSysVar 4 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 Begin_Of_Object -1 -VConfigSysVar 7 Begin_Of_Object -1 -VConfigEvent 8 Begin_Of_Object -1 -End_Of_Object VConfigEvent 8 -Airbus::R12 -L1 -End_Of_Object VConfigSysVar 7 -End_Of_Object ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 -End_Of_Object ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 --1 -0 -End_Of_Object ValueObjectConfiguration::VConfiguredSysVar 4 - -End_Of_Serialized_Data 3 -1 -1 -0 -1 -0 -End_Of_Object Mapping::VConfiguredMappingRelation 3 -Mapping::VConfiguredMappingRelation 3 Begin_Of_Object -2 -3 -ValueObjectConfiguration::VConfiguredSysVar 4 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 Begin_Of_Object -1 -VConfigSysVar 7 Begin_Of_Object -1 -VConfigEvent 8 Begin_Of_Object -1 -End_Of_Object VConfigEvent 8 -Ethernet1::Client_3::Data -InputBits -End_Of_Object VConfigSysVar 7 -End_Of_Object ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 -End_Of_Object ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 -2 -0 -End_Of_Object ValueObjectConfiguration::VConfiguredSysVar 4 - -End_Of_Serialized_Data 3 -3 -ValueObjectConfiguration::VConfiguredSysVar 4 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 Begin_Of_Object -1 -VConfigSysVar 7 Begin_Of_Object -1 -VConfigEvent 8 Begin_Of_Object -1 -End_Of_Object VConfigEvent 8 -Airbus::R14 -L1 -End_Of_Object VConfigSysVar 7 -End_Of_Object ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 -End_Of_Object ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 --1 -0 -End_Of_Object ValueObjectConfiguration::VConfiguredSysVar 4 - -End_Of_Serialized_Data 3 -1 -1 -0 -1 -0 -End_Of_Object Mapping::VConfiguredMappingRelation 3 -Mapping::VConfiguredMappingRelation 3 Begin_Of_Object -2 -3 -ValueObjectConfiguration::VConfiguredSysVar 4 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 Begin_Of_Object -1 -VConfigSysVar 7 Begin_Of_Object -1 -VConfigEvent 8 Begin_Of_Object -1 -End_Of_Object VConfigEvent 8 -Ethernet1::Client_2::Data -OutputBits -End_Of_Object VConfigSysVar 7 -End_Of_Object ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 -End_Of_Object ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 -0 -0 -End_Of_Object ValueObjectConfiguration::VConfiguredSysVar 4 - -End_Of_Serialized_Data 3 -3 -ValueObjectConfiguration::VConfiguredSysVar 4 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 Begin_Of_Object -1 -VConfigSysVar 7 Begin_Of_Object -1 -VConfigEvent 8 Begin_Of_Object -1 -End_Of_Object VConfigEvent 8 -Airbus::R14 -X -End_Of_Object VConfigSysVar 7 -End_Of_Object ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 -End_Of_Object ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 --1 -0 -End_Of_Object ValueObjectConfiguration::VConfiguredSysVar 4 - -End_Of_Serialized_Data 3 -0 -1 -0 -1 -0 -End_Of_Object Mapping::VConfiguredMappingRelation 3 -Mapping::VConfiguredMappingRelation 3 Begin_Of_Object -2 -3 -ValueObjectConfiguration::VConfiguredSysVar 4 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 Begin_Of_Object -1 -VConfigSysVar 7 Begin_Of_Object -1 -VConfigEvent 8 Begin_Of_Object -1 -End_Of_Object VConfigEvent 8 -Ethernet1::Client_2::Data -OutputBits -End_Of_Object VConfigSysVar 7 -End_Of_Object ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 -End_Of_Object ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 -9 -0 -End_Of_Object ValueObjectConfiguration::VConfiguredSysVar 4 - -End_Of_Serialized_Data 3 -3 -ValueObjectConfiguration::VConfiguredSysVar 4 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 Begin_Of_Object -1 -ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 Begin_Of_Object -1 -VConfigSysVar 7 Begin_Of_Object -1 -VConfigEvent 8 Begin_Of_Object -1 -End_Of_Object VConfigEvent 8 -Airbus::R12 -X -End_Of_Object VConfigSysVar 7 -End_Of_Object ValueObjectConfiguration::Detail::AbstractConfiguredValueObject 6 -End_Of_Object ValueObjectConfiguration::Detail::VConfiguredValueObjectBase 5 --1 -0 -End_Of_Object ValueObjectConfiguration::VConfiguredSysVar 4 - -End_Of_Serialized_Data 3 -0 -1 -0 -1 -0 -End_Of_Object Mapping::VConfiguredMappingRelation 3 End_Of_Object Mapping::VMappingManager 2 VTSystemControl 0 TestConfigurationSetup diff --git a/Modbus-CAPL/include/CAPL/Doo.can b/Modbus-CAPL/include/CAPL/Doo.can deleted file mode 100644 index 691ce36..0000000 --- a/Modbus-CAPL/include/CAPL/Doo.can +++ /dev/null @@ -1,21 +0,0 @@ -/*@!Encoding:1252*/ -includes -{ - -} - -variables -{ - -} - - - -on key '2' -{ - @sysvar::Airbus::R12::X = !@sysvar::Airbus::R12::X; -} -on key '4' -{ - @sysvar::Airbus::R14::X = !@sysvar::Airbus::R14::X; -} \ No newline at end of file diff --git a/Modbus-CAPL/include/CAPL/MakeConfig.can b/Modbus-CAPL/include/CAPL/MakeConfig.can index de2422b..557dbf0 100644 --- a/Modbus-CAPL/include/CAPL/MakeConfig.can +++ b/Modbus-CAPL/include/CAPL/MakeConfig.can @@ -29,11 +29,11 @@ variables on preStart { // List of IPs of devices go here - /* - strncpy(gIps[0], "192.168.1.3", 16); - strncpy(gIps[2], "192.168.1.4", 16); - strncpy(gIps[3], "192.168.1.8", 16); - */ + + strncpy(gIps[0], "192.168.1.100", 16); + strncpy(gIps[2], "192.168.1.101", 16); + strncpy(gIps[1], "192.168.100.2", 16); + //strncpy(gIps[3], "192.168.1.8", 16); // 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 diff --git a/Modbus-CAPL/include/CAPL/ModbusClient.can b/Modbus-CAPL/include/CAPL/PollingModbusClient.can similarity index 50% rename from Modbus-CAPL/include/CAPL/ModbusClient.can rename to Modbus-CAPL/include/CAPL/PollingModbusClient.can index da53baf..a863799 100644 --- a/Modbus-CAPL/include/CAPL/ModbusClient.can +++ b/Modbus-CAPL/include/CAPL/PollingModbusClient.can @@ -1,37 +1,56 @@ /*@!Encoding:1252*/ +// This file is the Modbus Client for Airbus CIDS +// It automatically and periodically reads all input bits and registers and writes them to SysVars %BUSTYPE%%CHANNEL%::%NODE_NAME%::Data +// It also reacts on changes in the output SysVars and write those to the Modbus device. + includes { - #include "include\ModbusUdpClientCommon.cin" - #include "include\DeviceInformation.cin" + #include "include\ModbusUdp.cin" // Use UDP as Layer 4 + #include "include\ModbusClient.cin" // Use Modbus as Application Layer + #include "include\DeviceInformation.cin" // Handle several vendors differently } variables { - msTimer gtRead; + msTimer gtRead; // The timer that keeps on polling all the time } on preStart { - writeClear(0); - setStartdelay(10); - OutputDebugLevel = Warning; + writeClear(0); // Clear write window in CANoe + setStartdelay(10); // Wait for Ethernet device to be ready + OutputDebugLevel = Warning; // The debug level (messages in write window) } on start { - DeviceInit(@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::Vendor); - ModbusInit(); + char ip[16]; + sysGetVariableString("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config", "IP", ip, elCount(ip)); // Get IP address of device from sysvars config - ModbusReadOutBits(gDevOutputBitAddr, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits); - ModbusReadOutRegisters(gDevOutputRegAddr, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters); + DeviceInit(@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::Vendor); // Set all device specific parameters (Wago / B&R) - if (@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config::Interval > 0) + writeDbg(MbInfo, "Connecting to %s:%d", ip, @sysvar::Config::Modbus::Port); + ModbusConnectTo(ip, @sysvar::Config::Modbus::Port); // Connect to device. Opens socket and connection or what ever + + ModbusReadOutBits(gDevOutputBitAddrR, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits); // Read the start status of the output bits + ModbusReadOutRegisters(gDevOutputRegAddrR, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters); // Read the start status of the output registers + + if (@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config::Interval > 0) // Start the polling timer setTimerCyclic(gtRead, 1, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config::Interval); } // Modbus events ---------------------------------------------------------------------- +/// All these events will be called by functions out of ModbusClientCommon.cin + +// -- Modbus Failures ----------------------------------------------------------------- +/// Several reasons are possible: +/// error == Timeout: The packet will be resent +/// error == FinalTimeout: The packet will not be resent +/// error == Exception: The client responded with an exception (which again can have several reasons, see enum ModbusException) + +/// This method gets called when an error occured while trying to read some bits (ModbusReadBits()) void OnModbusReadBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) { word i; @@ -51,6 +70,7 @@ void OnModbusReadBitsFailed(enum ModbusRequestError error, enum ModbusException } } +/// This method gets called when an error occured while trying to read some registers (ModbusReadRegisters()) void OnModbusReadRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) { byte i; @@ -70,26 +90,25 @@ void OnModbusReadRegistersFailed(enum ModbusRequestError error, enum ModbusExcep } } -void OnModbusWriteBitFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) -{ -} -void OnModbusWriteRegisterFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) -{ -} -void OnModbusWriteMasksFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) -{ -} -void OnModbusReadWriteRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) -{ -} -void OnModbusWriteBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) -{ -} -void OnModbusWriteRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) -{ -} +/// This method gets called when an error occured while trying to set a bit (ModbusWriteBit()) +void OnModbusWriteBitFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){} +/// This method gets called when an error occured while trying to set a register (ModbusWriteRegister()) +void OnModbusWriteRegisterFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){} +/// This method gets called when an error occured while trying to apply a mask on a register (ModbusWriteMask()) +void OnModbusWriteMasksFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){} +/// This method gets called when an error occured while trying to read and write registers (ModbusReadWriteRegisters()) +void OnModbusReadWriteRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){} +/// This method gets called when an error occured while trying to set multiple bits (ModbusWriteBits()) +void OnModbusWriteBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){} +/// This method gets called when an error occured while trying to set multiple registers (ModbusWriteRegisters()) +void OnModbusWriteRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){} + +// -- Modbus Success ------------------------------------------------------------------ +/// These functions get called when a device responds that a request was fulfilled successfully +/// Normally the Reponse as well the Request will be handed over +// This method gets called when some bits were read successfully. See 'bitStatus' for their values and 'mbreq.Address' for their start address void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[], struct ModbusReqRead mbreq) { word i, offset; @@ -99,7 +118,7 @@ void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[] case 0x01: // Read output bits sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits"); - offset = mbreq.Address - gDevOutputBitAddr; // Get the offset to the base output bit address + offset = mbreq.Address - gDevOutputBitAddrR; // 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]; @@ -119,6 +138,7 @@ void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[] } } +// This method gets called when some bits were read successfully. See 'mbres.Data' for their values and 'mbreq.Address' for their start address void OnModbusReadRegistersSuccess(struct ModbusResReceiveRegisters mbres, struct ModbusReqRead mbreq) { word i, offset; @@ -128,7 +148,7 @@ void OnModbusReadRegistersSuccess(struct ModbusResReceiveRegisters mbres, struct case 0x03: // Read output registers sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputRegisters"); - offset = mbreq.Address - gDevOutputRegAddr; // Get the offset to the base output register address + offset = mbreq.Address - gDevOutputRegAddrR; // 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]; @@ -148,22 +168,20 @@ void OnModbusReadRegistersSuccess(struct ModbusResReceiveRegisters mbres, struct } } -void OnModbusWriteBitSuccess(struct ModbusResConfirmSingle mbc) -{ -} -void OnModbusWriteRegisterSuccess(struct ModbusResConfirmSingle mbc) -{ -} -void OnModbusWriteBitsSuccess(struct ModbusResConfirmMultiple mbc) -{ -} -void OnModbusWriteRegistersSuccess(struct ModbusResConfirmMultiple mbc) -{ -} -void OnModbusWriteMasksSuccess(struct ModbusResConfirmMasks mbc) -{ -} +// This method gets called when a bit was set successfully. +void OnModbusWriteBitSuccess(struct ModbusResConfirmSingle mbc){} +// This method gets called when a register was set successsfully. +void OnModbusWriteRegisterSuccess(struct ModbusResConfirmSingle mbc){} +// This method gets called when multiple bits were set successfully. +void OnModbusWriteBitsSuccess(struct ModbusResConfirmMultiple mbc){} +// This method gets called when multiple registers were set successfully. +void OnModbusWriteRegistersSuccess(struct ModbusResConfirmMultiple mbc){} +// This method gets called when a mask was applied successfully. +void OnModbusWriteMasksSuccess(struct ModbusResConfirmMasks mbc){} + +// This method gets called when the Modbus Client panics (saying a fatal error occured). +// It will pass as argument what happened. Please see the log (increase debug level in preStart) for more details. void OnModbusClientPanics(enum FatalErrors reason) { writeLineEx(0, 4, "<%NODE_NAME%> FATAL! %d", reason); @@ -171,7 +189,6 @@ void OnModbusClientPanics(enum FatalErrors reason) { case ParsingBuffer: case ModbusPackageWasSplit: - case DeviceCodeUnknown: case VendorIdUnknown: runError(1001, reason); break; @@ -181,14 +198,15 @@ void OnModbusClientPanics(enum FatalErrors reason) } } -// Key events ------------------------------------------------------------------------- +// ------------------------------------------------------------------------- +// The timer will continuously poll the input registers and intput bits on timer gtRead { ModbusReadRegisters(0x0000, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters); ModbusReadBits(0x0000, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputBits); - //this.Cancel(); } +// If Data::OutputBits is changed we will send this update to the device on sysvar %BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits { word count, i; @@ -196,11 +214,12 @@ on sysvar %BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits count = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits; - for (i = 0; i < count; i++) + for (i = 0; i < count; i++) // Copy the data from SysVars to byte[] bitStatus[i] = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits[i]; - ModbusWriteBitsB(0, count, bitStatus); + ModbusWriteBitsB(0, count, bitStatus); // Send update command } +// If Data::OutputRergisters is changed we will send this update to the device on sysvar %BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputRegisters { word count, i; @@ -208,12 +227,12 @@ on sysvar %BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputRegisters count = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters; - for (i = 0; i < count; i++) + for (i = 0; i < count; i++) // Copy the data from SysVars to word[] regValues[i] = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputRegisters[i]; - ModbusWriteRegisters(0x000, count, regValues); + ModbusWriteRegisters(0, count, regValues); // Send update command } - +// Config::Interval is changed we will update the timer gtRead accordingly on sysvar %BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config::Interval { if (@this <= 0) diff --git a/Modbus-CAPL/include/CAPL/include/Common.cin b/Modbus-CAPL/include/CAPL/include/Common.cin index e816623..86b08e7 100644 --- a/Modbus-CAPL/include/CAPL/include/Common.cin +++ b/Modbus-CAPL/include/CAPL/include/Common.cin @@ -1,5 +1,11 @@ /*@!Encoding:1252*/ +// This file contains very common functions: +// - writeDbg() which writes messages only if the global debug level 'OutputDebugLevel' is high enough +// - bin_to_strhex() which converts byte[]s to a hex string +// - hbin_to_strhex() which converts only the lower nibble of the byte[]s to a hex string +// - dbin_to_strhex() which converts word[]s to a hex string + variables { enum DebugLvl { @@ -29,6 +35,8 @@ variables enum DebugLvl OutputDebugLevel = Debug; } +// This method creates a format string in 'msg' which can then be passed to writeLineEx(). +// This format string contains the node name and the debug level /// void writeDbgFormat(byte lVl, char msg[], char format[]) { @@ -37,6 +45,8 @@ void writeDbgFormat(byte lVl, char msg[], char format[]) strncat(msg, ": ", elCount(msg)); strncat(msg, format, elCount(msg)-strlen(msg)); } + +// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough /// void writeDbg(enum DebugLvl lvl, char format[]) { @@ -50,6 +60,7 @@ void writeDbg(enum DebugLvl lvl, char format[]) writeDbgFormat(lVl, msg, format); writeLineEx(1, lVl, msg); } +// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough /// void writeDbg(enum DebugLvl lvl, char format[], char string[]) { @@ -63,6 +74,7 @@ void writeDbg(enum DebugLvl lvl, char format[], char string[]) writeDbgFormat(lVl, msg, format); writeLineEx(1, lVl, msg, string); } +// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough /// void writeDbg(enum DebugLvl lvl, char format[], char string1[], char string2[]) { @@ -76,6 +88,7 @@ void writeDbg(enum DebugLvl lvl, char format[], char string1[], char string2[]) writeDbgFormat(lVl, msg, format); writeLineEx(1, lVl, msg, string1, string2); } +// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough /// void writeDbg(enum DebugLvl lvl, char format[], long num, char string[]) { @@ -89,6 +102,7 @@ void writeDbg(enum DebugLvl lvl, char format[], long num, char string[]) writeDbgFormat(lVl, msg, format); writeLineEx(1, lVl, msg, num, string); } +// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough /// void writeDbg(enum DebugLvl lvl, char format[], char string[], long num) { @@ -102,6 +116,7 @@ void writeDbg(enum DebugLvl lvl, char format[], char string[], long num) writeDbgFormat(lVl, msg, format); writeLineEx(1, lVl, msg, string, num); } +// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough /// void writeDbg(enum DebugLvl lvl, char format[], long num1) { @@ -115,6 +130,7 @@ void writeDbg(enum DebugLvl lvl, char format[], long num1) writeDbgFormat(lVl, msg, format); writeLineEx(1, lVl, msg, num1); } +// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough /// void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, char string[]) { @@ -128,6 +144,7 @@ void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, char strin writeDbgFormat(lVl, msg, format); writeLineEx(1, lVl, msg, num1, num2, string); } +// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough /// void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2) { @@ -141,6 +158,7 @@ void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2) writeDbgFormat(lVl, msg, format); writeLineEx(1, lVl, msg, num1, num2); } +// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough /// void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3) { @@ -154,6 +172,7 @@ void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3) writeDbgFormat(lVl, msg, format); writeLineEx(1, lVl, msg, num1, num2, num3); } +// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough /// void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3, long num4) { @@ -167,6 +186,7 @@ void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3, writeDbgFormat(lVl, msg, format); writeLineEx(1, lVl, msg, num1, num2, num3, num4); } +// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough /// void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3, long num4, long num5, long num6) { @@ -183,7 +203,8 @@ void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3, - +// This method converts the bytes in 'bin' to a readable string in hex format ("XX XX XX XX") +// You have to allocate 60 bytes for 'result' void bin_to_strhex(byte bin[], char result[]) { char hex_str[17] = "0123456789ABCDEF"; @@ -210,6 +231,8 @@ void bin_to_strhex(byte bin[], char result[]) result[binsz * 3 - 1] = 0; } +// This method converts the lower nibbles of the bytes in 'bin' to a readable string in hex format ("X X X X") +// You have to allocate 40 bytes for 'result' void hbin_to_strhex(byte bin[], char result[]) { char hex_str[17] = "0123456789ABCDEF"; @@ -235,6 +258,8 @@ void hbin_to_strhex(byte bin[], char result[]) result[binsz * 2 - 1] = 0; } +// This method converts the words in 'bin' to a readable string in hex format ("XXXX XXXX XXXX XXXX") +// You have to allocate 100 bytes for 'result' void dbin_to_strhex(word bin[], char result[]) { char hex_str[17] = "0123456789ABCDEF"; diff --git a/Modbus-CAPL/include/CAPL/include/DeviceInformation.cin b/Modbus-CAPL/include/CAPL/include/DeviceInformation.cin index 8768144..be920c7 100644 --- a/Modbus-CAPL/include/CAPL/include/DeviceInformation.cin +++ b/Modbus-CAPL/include/CAPL/include/DeviceInformation.cin @@ -1,25 +1,38 @@ /*@!Encoding:1252*/ + +// This file shall contain all information about device specific parameters. +/// The aim of this file is to centralize this device specific information so that new Vendors can be added easily +/// Currently supported are Wago 750-881 and B&R X20BC0087. +/// The Modbus specification leaves room for several parameters than can vary from vendor to vendor (and perhaps also from device to device). These parameters are: +/// - Start addresses for reading input bits/registers +/// - Start addresses for reading output bits/registers +/// - Start addresses for writing input bits/registers +/// - Start addresses for writing output bits/registers +/// - Maximum number of connected IO (bits and registers) +/// - Size of the receive window (how many telegrams can be processed at the same time) +/// This file is used at two position: The majority of the functions are used to analyze devices when making the config. The minority has to be called when initiating a Modbus client so it knows about the addresses and window size. variables { - word gDevOutputBitAddr, gDevOutputRegAddr; - word gDevInputBitAddr, gDevInputRegAddr; - word gDevBitMaxCount, gDevRegMaxCount; - word gDevReceiveWindow = 1; + word gDevOutputBitAddrR, gDevOutputRegAddrR; // Output start addresses Read + word gDevOutputBitAddrW, gDevOutputRegAddrW; // Output start addresses Write + word gDevInputBitAddr, gDevInputRegAddr; // Input start addresses + word gDevBitMaxCount, gDevRegMaxCount; // Max number of bits/registers + word gDevReceiveWindow = 1; // Registers size - enum Vendor + enum Vendor // The Vendor enum. All Vendors have to listed here and all listed vendors have to be implemented in this file { - Wago = 23, - BuR = 2 + Wago = 23, // Wago + BuR = 2 // B&R }; - struct deviceIOs + struct deviceIOs // A structure which contains quantity information about connected IO. Used in MakeConfig. These info will be written into the SysVars { - byte InputRegisters; - word InputBits; - byte OutputRegisters; - word OutputBits; - char Modules[1024]; + byte InputRegisters; // Count of AI + word InputBits; // Count of DI + byte OutputRegisters; // Count of AO + word OutputBits; // Count of DO + char Modules[1024]; // A string representing the connected modules }; - struct device // A structure that contains information about an Modbus device + struct device // A structure that contains information about an Modbus device. Used in MakeConfig. { char Ip[16]; // String: The IP address char IpLsb[4]; // String: The last byte of the IP address. Used as index of node name @@ -31,7 +44,8 @@ variables }; } -// This is for the normal client and for making the sysvars +// This is for both the normal client and for making the sysvars. +// It will set the above mentioned parameters so the client can work properly /// void DeviceInit(byte vendor) { @@ -40,8 +54,10 @@ void DeviceInit(byte vendor) case Wago: gDevInputBitAddr = 0x0000; // Wago inputs start at 0x000 gDevInputRegAddr = 0x0000; - gDevOutputBitAddr = 0x0200; // Wago outputs start at 0x200 - gDevOutputRegAddr = 0x0200; + gDevOutputBitAddrR = 0x0200; // Wago reading outputs start at 0x200 + gDevOutputRegAddrR = 0x0200; + gDevOutputBitAddrW = 0x0000; // Wago writing outputs start at 0x000 + gDevOutputRegAddrW = 0x0000; gDevBitMaxCount = 0x0100; // Wago allows up to 256 inputs gDevRegMaxCount = 0x0100; gDevReceiveWindow = 5; // Wago can handle 5 requests simultaneously @@ -49,52 +65,55 @@ void DeviceInit(byte vendor) 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 + gDevOutputBitAddrR = 0x0000; // B&R reading digital outputs start at 0x000 + gDevOutputRegAddrR = 0x0800; // B&R reading analog outputs start at 0x800 + gDevOutputBitAddrW = 0x0000; // B&R writing outputs start at 0x000 + gDevOutputRegAddrW = 0x0000; 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; + default: + OnModbusClientPanics(VendorIdUnknown); + break; } } // This is for making the sysvars (MakeConfig) + +// This function parses the received device code/module code received from the device and writes it to struct deviceIO.Modules. +// The quantity of IO does not have to be parsed here because it is extracted from extra registers /// void DeviceParseCode(word dev, enum Vendor vendor, struct deviceIOs dios) { byte input; byte numChannels; char module[10]; + strncpy(module, "..%d,", elCount(module)); switch(vendor) { - case Wago: // if this is a Wago device + case Wago: // if this is a Wago device if (dev & 0x8000) // Digital Module { + module[0] = 'D'; 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 // mhm... What is it? Input and Output? + writeDbg(AlgoError, "DeviceParseCode: Device code 0x%X cannot be decoded", dev); } - else + else // Analog (=Complex) module { - // http://www.wago.com/wagoweb/documentation/navigate/nm0dx__d.htm - // http://www.wago.com/wagoweb/documentation/navigate/nm0dy__d.htm + module[0] = 'A'; + // List of available 750 modules: + // AI: http://www.wago.com/wagoweb/documentation/navigate/nm0dx__d.htm + // AO: http://www.wago.com/wagoweb/documentation/navigate/nm0dy__d.htm switch (dev) { case 881: // devices that have no inputs/outputs @@ -149,7 +168,7 @@ void DeviceParseCode(word dev, enum Vendor vendor, struct deviceIOs dios) input = 1; numChannels = 4; break; - case 552: // devices that have 2 inputs + case 552: // devices that have 2 outputs case 585: case 563: case 554: @@ -159,20 +178,17 @@ void DeviceParseCode(word dev, enum Vendor vendor, struct deviceIOs dios) case 556: input = 0; numChannels = 2; - case 555: // devices that have 4 inputs + case 555: // devices that have 4 outputs case 553: case 557: case 559: input = 0; numChannels = 4; - default: // unknown device. Ouch! + default: // unknown device writeDbg(AlgoInfo, "Connected device: 750-%d", dev); return; } - if (input) - strncpy(module, "AI%d,", elCount(module)); - else - strncpy(module, "AO%d,", elCount(module)); + // Prepare the format string } break; // switch(vendor) default: @@ -180,12 +196,16 @@ void DeviceParseCode(word dev, enum Vendor vendor, struct deviceIOs dios) OnModbusClientPanics(VendorIdUnknown); return; } + + module[1] = input ? 'I' : 'O'; // Set input/output char - snprintf(module, elCount(module), module, numChannels); - strncat(dios.Modules, module, elCount(dios.Modules)); + snprintf(module, elCount(module), module, numChannels); // --> DO%d, AO%d, DI%d, AI%d + strncat(dios.Modules, module, elCount(dios.Modules)); // Append new module to existing module string } -// This function requests more information from the device and return the number of expected results +// This function requests more information from the device and returns the count of expected responses (Modbus telegrams). It is called by AnalyzeDevices() in MakeConfig +// Requested information has to be: Count of AOs, AIs, DOs, DIs +// Additionaly information can be: Serial Code, Device/Product Code, Codes of connected modules /// byte DeviceGetInformation(enum Vendor vendor) { @@ -210,11 +230,15 @@ byte DeviceGetInformation(enum Vendor vendor) ModbusReadRegisters(0x1105, 1); // Number of DIs ModbusReadRegisters(0x1107, 1); // Number of DOs return 5; + default: + writeDbg(AlgoError, "DeviceGetInformation: Unknown vendor id: %d", vendor); + OnModbusClientPanics(VendorIdUnknown); } return 0; } -// This function parses the received registers +// This function parses the received registers (requested by DeviceGetInformation()) +// It is called in the callback OnModbusReadRegistersSuccess() to fill the 'device' structure /// void DeviceParseRegister(struct device device, word address, word data[], word count) { @@ -226,25 +250,25 @@ void DeviceParseRegister(struct device device, word address, word data[], word c // Parse the received data switch (address) { - case 0x2011: + case 0x2011: // Serial Code device.serialCode = data[0]; break; - case 0x2012: + case 0x2012: // Device Code device.deviceCode = data[0]; break; - case 0x1022: - device.DeviceIOs.OutputRegisters = data[0] / 16; // Wago returns the size in bits allocated by the module + case 0x1022: // Number of output registers (AO) + device.DeviceIOs.OutputRegisters = data[0] / 16; // Wago returns the allocated bits. break; - case 0x1023: + case 0x1023: // Number of input registers (AI) device.DeviceIOs.InputRegisters = data[0] / 16; break; - case 0x1024: + case 0x1024: // Number of output bits (DO) device.DeviceIOs.OutputBits = data[0]; break; - case 0x1025: + case 0x1025: // Number of input bits (DI) device.DeviceIOs.InputBits = data[0]; break; - case 0x2030: + case 0x2030: // Codes of connected modules. Pass to DeviceParseCode() case 0x2031: case 0x2032: case 0x2033: @@ -261,22 +285,21 @@ void DeviceParseRegister(struct device device, word address, word data[], word c // Parse the received data switch (address) { - case 0x1083: + case 0x1083: // Product Code device.serialCode = data[0]; break; - case 0x1101: + case 0x1101: // Number of input registers (AI) device.DeviceIOs.InputRegisters = data[0]; break; - case 0x1103: + case 0x1103: // Number of output registers (AO) 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 + case 0x1105: // Number of input bits (DI) + 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 2 DI4 modules not allocate not 2*4 bit = 1 byte, but 2*8 bit = 16 bit = 2 byte + // See Modbus X20BC0087 documentation v1.11 p. 22 break; - case 0x1107: + case 0x1107: // Number of output bits (DO) device.DeviceIOs.OutputBits = data[0] * 8; break; } diff --git a/Modbus-CAPL/include/CAPL/include/EilCommon.cin b/Modbus-CAPL/include/CAPL/include/EilCommon.cin deleted file mode 100644 index 02f970b..0000000 --- a/Modbus-CAPL/include/CAPL/include/EilCommon.cin +++ /dev/null @@ -1,167 +0,0 @@ -/*@!Encoding:1252*/ -includes -{ - #include "Common.cin" - #include "TcpUdpCommon.cin" -} - -variables -{ - long gPacket; - msTimer gtArp; - - byte gLocalMac[6]; - byte gRemoteMac[6]; - dword gLocalIP = 0xC0A80101; -} - - -word EilConnectTo(char Remote_IP[], word remotePort) -{ - dword remoteIp; - - // Convert IP string to Number - remoteIp = IpGetAddressAsNumber(Remote_IP); - if (remoteIp == INVALID_IP) - { - writeDbg(ConnError, "EilConnectTo: invalid server Ip address!"); - OnModbusClientPanics(ConnectionError); - return ipGetLastError(); - } - - return EilConnectTo(remoteIp, remotePort); -} - -word EilConnectTo(dword remoteIp, word remotePort) -{ - long error; - byte broadcastMac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - - if (EthGetMacId(gLocalMac) != 0) - { - gSocketState = ERROR; - error = EthGetLastError(); - writeDbg(ConnError, "EthGetMacId: Could not get local MAC! %d", error); - OnModbusClientPanics(ConnectionError); - return error; - } - - // TCP/IP API gives IP in little endian but EIL uses big endian - gRemotePort = remotePort; - gRemoteIP = (remoteIp >> 24) | (remoteIp >> 8) & 0x0000FF00 | (remoteIp << 8) & 0x00FF0000 | (remoteIp << 24); - - if (gPacket != 0) - ModbusDisconnect(); - // Try to create an ARP packet that gets the MAC from remote server - gPacket = EthInitPacket("arp"); - if (gPacket == 0) - { - gSocketState = ERROR; - error = EthGetLastError(); - writeDbg(ConnError, "EthInitPacket: Could not create ARP package! %d", error); - OnModbusClientPanics(ConnectionError); - return error; - } - - EthSetTokenData(gPacket, "eth", "source" , elCount(gLocalMac), gLocalMac); - EthSetTokenData(gPacket, "eth", "destination" , elCount(broadcastMac), broadcastMac); - EthSetTokenInt(gPacket, "arp", "hwType" , 1); // Ethernet - EthSetTokenInt(gPacket, "arp", "protType" , 0x0800); // IPv4 - EthSetTokenInt(gPacket, "arp", "hwSize" , 6); // Ethernet addr size - EthSetTokenInt(gPacket, "arp", "protSize" , 4); // IP addr size - EthSetTokenInt(gPacket, "arp", "operation" , 1); - EthSetTokenData(gPacket, "arp", "hwSourceAddr" , elCount(gLocalMac), gLocalMac); - EthSetTokenInt(gPacket, "arp", "protSourceAddr" , gLocalIP); - //EthSetTokenData(gPacket, "arp", "hwDestinationAddr" , elCount(gLocalMac), gLocalMac); - EthSetTokenInt(gPacket, "arp", "protDestinationAddr" , gRemoteIP); - - EthReceivePacket("OnEthReceivePacket"); - - EthCompletePacket(gPacket); - EthOutputPacket(gPacket); - EthReleasePacket(gPacket); - gSocketState = NULL; - gtArp.set(@sysvar::Config::Modbus::RequestTimeout); - return 0; -} - -void EilConnectTo2() -{ - gPacket = EthInitPacket("udp"); - if (gPacket == 0) - { - gSocketState = ERROR; - writeDbg(ConnError, "EthInitPacket: Could not create UDP packet: %d", EthGetLastError()); - OnModbusClientPanics(ConnectionError); - return; - } - - EthSetTokenData(gPacket, "eth", "source" , elCount(gLocalMac), gLocalMac); - EthSetTokenData(gPacket, "eth", "destination" , elCount(gRemoteMac), gRemoteMac); - EthSetTokenInt(gPacket, "ipv4", "source" , gLocalIP); - EthSetTokenInt(gPacket, "ipv4", "destination" , gRemoteIP); - EthSetTokenInt(gPacket, "udp", "source" , 23456); - EthSetTokenInt(gPacket, "udp", "destination" , 502); - - gSocketState = OK; -} - -void EilDisconnect() -{ - if (gPacket != 0) - { - EthReleasePacket(gPacket); - gPacket = 0; - } - gSocketState = CLOSED; -} - - -void EilRecv() -{ -} - -byte EilSnd(byte buffer[], word length) -{ - char str[20*3]; - - switch (gSocketState) - { - case CLOSED: - EilConnectTo(gRemoteIP, gRemotePort); - if (gSocketState != OK) - { - writeDbg(ConnWarning, "EilSnd: Reconnecting failed! Doing nothing."); - return 1; - } - case OK: - break; - default: - writeDbg(ConnWarning, "EilSnd: Socket status is not OK! Doing nothing."); - return 1; - } - - bin_to_strhex(buffer, str); - writeDbg(ConnDebug, "EilSnd: %s (Länge: %d)", str, length); - - EthResizeToken(gPacket, "udp", "data" , length*8); - EthSetTokenData(gPacket, "udp", "data" , length, buffer); - - EthCompletePacket(gPacket); - EthOutputPacket(gPacket); - - return 0; -} - -long EilGetLastConnectionError(char string[]) -{ - EthGetLastErrorText(elCount(string), string); - return EthGetLastError(); -} - -on timer gtArp -{ - gSocketState = ERROR; - writeDbg(ConnError, "No (valid) ARP response detected. The host seems to be offline!"); - OnModbusClientPanics(ConnectionError); -} \ No newline at end of file diff --git a/Modbus-CAPL/include/CAPL/include/ModbusClientCommon.cin b/Modbus-CAPL/include/CAPL/include/ModbusClient.cin similarity index 90% rename from Modbus-CAPL/include/CAPL/include/ModbusClientCommon.cin rename to Modbus-CAPL/include/CAPL/include/ModbusClient.cin index af2d93d..8f07411 100644 --- a/Modbus-CAPL/include/CAPL/include/ModbusClientCommon.cin +++ b/Modbus-CAPL/include/CAPL/include/ModbusClient.cin @@ -1,5 +1,12 @@ /*@!Encoding:1252*/ -// Additionally include ModbusTcpCommon.cin or ModbusUdpCommon.cin + +// This is the main file providing functions for talking to Modbus devices. +// Don't include this file directly! Choose one transportation protocol (TCP, UDP, EthernetIL) and include the corresponding file (Modbus###ClientCommon.cin) + +/// This layer provides functions to send Modbus telegrams, enqueues this messages and watch for timeouts. When responses are received, the corresponding callback method will be called +/// The Modbus telegrams are distinguished in the queues by their TxID +/// There are three queues available: Pending, Sent & Ack. Only the latter shall be used by the higher layer. Pending telegrams are waiting to be sent, sent telegrams are waiting to be responded and acknowledged telegrams were already responded and can now be processed further. + includes { #include "ModbusStructs.cin" @@ -7,14 +14,14 @@ includes variables { - msTimer gtRobin; // Timer that sends the packets and watches for timeouts + msTimer gtModbusRobin; // Timer that sends the packets and watches for timeouts word gTxID = 0x0000; // Transaction Identifier for Modbus. Used as index for gQueue // Global storage for pending and sent requests, associated by TxID struct QueueElement { - word TimeoutTicks; - byte Timeouts; + word TimeoutTicks; // Time counter [ms]. Used to watch for timeouts (see @sysvar::Config::Modubs::RequestTimeout) + byte Timeouts; word Length; byte Buffer[gModbusMaxTelegramSize]; }; @@ -37,15 +44,6 @@ variables }; } -void ModbusInit() -{ - char ip[16]; - sysGetVariableString("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config", "IP", ip, elCount(ip)); - - writeDbg(MbInfo, "Connecting to %s:%d", ip, @sysvar::Config::Modbus::Port); - ModbusConnectTo(ip, @sysvar::Config::Modbus::Port); -} - void ModbusMakeHeader(struct ModbusApHeader mbap, word length, byte funcCode) { mbap.TxID = gTxID++; // [2] Transaction ID @@ -312,9 +310,18 @@ void ModbusWriteBits(word address, long count, byte values[]) // FC15: Write Multiple Bits (DOs) while (count > 0) { - curCount = count > gMaxBitsPerWrite ? gMaxBitsPerWrite : count; - dataLength = _ceil(curCount / 8.0); - overallLength = maxLength - gMaxBitsPerWrite/8 + dataLength; + if (count > gMaxBitsPerWrite) + { + curCount = gMaxBitsPerWrite; + dataLength = gMaxBitsPerWrite/8; + overallLength = maxLength; + } + else + { + curCount = count; + dataLength = _ceil(curCount / 8.0); + overallLength = maxLength - gMaxBitsPerWrite/8 + dataLength; + } ModbusMakeHeader(mbreq.Header, overallLength, funcCode); mbreq.Address = address; // [2] Output address @@ -335,7 +342,7 @@ void ModbusWriteBits(word address, long count, byte values[]) /// void ModbusWriteBitsB(word address, long count, byte values[]) { - byte buffer[2]; // length + byte buffer[gMaxBitsPerWrite/8]; // length word length; dword ellCount; dword i; @@ -378,7 +385,7 @@ void OnModbusConfirmBits(byte buffer[]) memcpy_n2h(mbc, buffer); - writeDbg(MbInfo, "Updated &d bits at 0x%04X", mbc.Count, mbc.Address); + writeDbg(MbInfo, "Updated %d bits at 0x%04X", mbc.Count, mbc.Address); OnModbusWriteBitsSuccess(mbc); } @@ -438,7 +445,7 @@ void OnModbusConfirmRegisters(byte buffer[]) memcpy_n2h(mbc, buffer); - writeDbg(MbInfo, "Updated &d registers at 0x%04X", mbc.Count, mbc.Address); + writeDbg(MbInfo, "Updated %d registers at 0x%04X", mbc.Count, mbc.Address); OnModbusWriteRegistersSuccess(mbc); } @@ -767,17 +774,17 @@ void ModbusSend(byte buffer[], word length, word TxID) void ModbusStartQueue() { - writeDbg(ConnDebug, "Starting Timer gtRobin"); - setTimerCyclic(gtRobin, 1); + writeDbg(ConnDebug, "Starting Timer gtModbusRobin"); + setTimerCyclic(gtModbusRobin, 1); } /// <-ModbusSend> -on timer gtRobin +on timer gtModbusRobin { struct ModbusApHeader mbap; enum ModbusRequestError reqError; - writeDbg(ConnDebug, "gtRobin: Queue Sent: %d, Queue Pending: %d, Queue Ack: %d", gQueueSent.Size(), gQueuePending.Size(), gQueueAck.Size()); + writeDbg(ConnDebug, "gtModbusRobin: Queue Sent: %d, Queue Pending: %d, Queue Ack: %d", gQueueSent.Size(), gQueuePending.Size(), gQueueAck.Size()); // First: check timeouts = packets that were sent in previous run and not removed by response for (long TxID : gQueueSent) @@ -851,7 +858,7 @@ on timer gtRobin if (gSocketState == ERROR || gQueueSent.Size() == 0 && gQueuePending.Size() == 0) // Stop timer to reduce latency of first packet { - writeDbg(ConnDebug, "Stopping Timer gtRobin"); + writeDbg(ConnDebug, "Stopping Timer gtModbusRobin"); this.Cancel(); } -} \ No newline at end of file +} diff --git a/Modbus-CAPL/include/CAPL/include/ModbusEil.cin b/Modbus-CAPL/include/CAPL/include/ModbusEil.cin new file mode 100644 index 0000000..8da6b0a --- /dev/null +++ b/Modbus-CAPL/include/CAPL/include/ModbusEil.cin @@ -0,0 +1,214 @@ +/*@!Encoding:1252*/ + +// This file contains functions that abstract the Ethernet Interaction Layer +/// It provides following methods +/// - ModbusConnectTo() Prepare anything that sending works. Here: Create a new packet +/// - ModbusDisconnect() Gracefully disconnect from the device. Here: Invalidate the existing packet +/// - ModbusRecv() Receive data from the device. Here: Does nothing, receiving takes place automatically +/// - ModbusSnd() Send data to the device. Here: Fills the packet with payload data and sends it +/// - Some function that received packets + +includes +{ + #include "Common.cin" + #include "TcpUdpEilCommon.cin" +} + +variables +{ + long gPacket; // The packet that will be send + msTimer gtModbusArp; // A timer waiting for the ARP response + + byte gLocalMac[6]; // Storage of the local MAC address + byte gRemoteMac[6]; // Storage of the remote MAC address (the one of the device) + // TODO: The local IP should not have to be specified here. Where can we get it from? + dword gLocalIP = 0xC0A80101; // The local IP address. +} + +// This method prepares anything in a way that sending with ModbusSnd() is possible. +// Here: The method will create an ARP telegram to get the MAC address of the device with the specified Remote_IP +word ModbusConnectTo(char Remote_IP[], word remotePort) +{ + dword remoteIp; + + // Convert IP string to Number + remoteIp = IpGetAddressAsNumber(Remote_IP); + if (remoteIp == INVALID_IP) + { + writeDbg(ConnError, "EilConnectTo: invalid server Ip address!"); + OnModbusClientPanics(ConnectionError); + return ipGetLastError(); + } + + return ModbusConnectTo(remoteIp, remotePort); +} +// This method prepares anything in a way that sending with ModbusSnd() is possible. +// Here: The method will create an ARP telegram to get the MAC address of the device with the specified remoteIp +word ModbusConnectTo(dword remoteIp, word remotePort) +{ + long error; + byte broadcastMac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + if (EthGetMacId(gLocalMac) != 0) + { + gSocketState = ERROR; + error = EthGetLastError(); + writeDbg(ConnError, "EthGetMacId: Could not get local MAC! %d", error); + OnModbusClientPanics(ConnectionError); + return error; + } + + // TCP/IP API gives IP in little endian but EIL uses big endian + gRemotePort = remotePort; + gRemoteIP = (remoteIp >> 24) | (remoteIp >> 8) & 0x0000FF00 | (remoteIp << 8) & 0x00FF0000 | (remoteIp << 24); + + if (gPacket != 0) + ModbusDisconnect(); + // Try to create an ARP packet that gets the MAC from remote server + gPacket = EthInitPacket("arp"); + if (gPacket == 0) + { + gSocketState = ERROR; + error = EthGetLastError(); + writeDbg(ConnError, "EthInitPacket: Could not create ARP package! %d", error); + OnModbusClientPanics(ConnectionError); + return error; + } + + EthSetTokenData(gPacket, "eth", "source" , elCount(gLocalMac), gLocalMac); + EthSetTokenData(gPacket, "eth", "destination" , elCount(broadcastMac), broadcastMac); + EthSetTokenInt(gPacket, "arp", "hwType" , 1); // Ethernet + EthSetTokenInt(gPacket, "arp", "protType" , 0x0800); // IPv4 + EthSetTokenInt(gPacket, "arp", "hwSize" , 6); // Ethernet addr size + EthSetTokenInt(gPacket, "arp", "protSize" , 4); // IP addr size + EthSetTokenInt(gPacket, "arp", "operation" , 1); + EthSetTokenData(gPacket, "arp", "hwSourceAddr" , elCount(gLocalMac), gLocalMac); + EthSetTokenInt(gPacket, "arp", "protSourceAddr" , gLocalIP); + EthSetTokenInt(gPacket, "arp", "protDestinationAddr" , gRemoteIP); + + EthReceivePacket("OnEthReceivePacket"); + + EthCompletePacket(gPacket); + EthOutputPacket(gPacket); + EthReleasePacket(gPacket); + gSocketState = NULL; + gtModbusArp.set(@sysvar::Config::Modbus::RequestTimeout); + return 0; +} +// This method prepares anything in a way that sending with ModbusSnd() is possible. +// Here: The method will be called after an ARP telegram was received and set up the EthernetIL packet. +void ModbusConnectTo2() +{ + gPacket = EthInitPacket("udp"); + if (gPacket == 0) + { + gSocketState = ERROR; + writeDbg(ConnError, "EthInitPacket: Could not create UDP packet: %d", EthGetLastError()); + OnModbusClientPanics(ConnectionError); + return; + } + + EthSetTokenData(gPacket, "eth", "source" , elCount(gLocalMac), gLocalMac); + EthSetTokenData(gPacket, "eth", "destination" , elCount(gRemoteMac), gRemoteMac); + EthSetTokenInt(gPacket, "ipv4", "source" , gLocalIP); + EthSetTokenInt(gPacket, "ipv4", "destination" , gRemoteIP); + EthSetTokenInt(gPacket, "udp", "source" , 23456); + EthSetTokenInt(gPacket, "udp", "destination" , 502); + + gSocketState = OK; +} + +// This method will gracefully disconnect from the remote device. +// Here: The method will invalidate the packet 'gPacket' +void ModbusDisconnect() +{ + if (gPacket != 0) + { + EthReleasePacket(gPacket); + gPacket = 0; + } + gSocketState = CLOSED; +} + +// This method will wait for data from the remote device. +// Here: Nothing has to be done, EthernetIL is waiting for packets all the time +void ModbusRecv() +{ +} + +// This method will send the payload 'buffer' to the device. +// Here: It fills the packet 'gPacket' and sends it +byte ModbusSnd(byte buffer[], word length) +{ + char str[20*3]; + + switch (gSocketState) + { + case CLOSED: + ModbusConnectTo(gRemoteIP, gRemotePort); + if (gSocketState != OK) + { + writeDbg(ConnWarning, "EilSnd: Reconnecting failed! Doing nothing."); + return 1; + } + case OK: + break; + default: + writeDbg(ConnWarning, "EilSnd: Socket status is not OK! Doing nothing."); + return 1; + } + + bin_to_strhex(buffer, str); + writeDbg(ConnDebug, "EilSnd: %s (Länge: %d)", str, length); + + EthResizeToken(gPacket, "udp", "data" , length*8); + EthSetTokenData(gPacket, "udp", "data" , length, buffer); + + EthCompletePacket(gPacket); + EthOutputPacket(gPacket); + + return 0; +} + +// This Method simply combines the two EthGetLastError functions +long ModbusGetLastConnectionError(char string[]) +{ + EthGetLastErrorText(elCount(string), string); + return EthGetLastError(); +} + +// When the ARP times out the "connection" could not be opened and we have to throw an error +on timer gtModbusArp +{ + gSocketState = ERROR; + writeDbg(ConnError, "No (valid) ARP response detected. The host seems to be offline!"); + OnModbusClientPanics(ConnectionError); +} + +// This function will handle incoming packets and give them to the Modbus layer +void OnEthReceivePacket(long channel, long dir, long packet) +{ + byte buffer[gModbusMaxTelegramSize]; + long size; + + if (dir == TX) + return; + + if (EthGetTokenInt(packet, "arp", "protSourceAddr") == gRemoteIP) // this was our ARP package + { + if (EthGetTokenData(packet, "arp", "hwSourceAddr", elCount(gRemoteMac), gRemoteMac) == 6) // save it + { + gtModbusArp.Cancel(); + writeDbg(ConnDebug, "Remote Mac: %02X:%02X:%02X:%02X:%02X:%02X", gRemoteMac[0], gRemoteMac[1], gRemoteMac[2], gRemoteMac[3], gRemoteMac[4], gRemoteMac[5]); + ModbusConnectTo2(); // create the UDP package + ModbusStartQueue(); + } + return; + } + + if (EthGetTokenInt(packet, "ipv4", "protocol") == 0x11 && EthGetTokenInt(packet, "ipv4", "source") == gRemoteIP) // if this is a UDP package from our server + { + size = EthGetThisData(0, gModbusMaxTelegramSize, buffer); + OnModbusReceive(0, 0, EthGetTokenInt(packet, "ipv4", "source"), gRemoteIP, buffer, size); // Hand to Modbus Layer + } +} \ No newline at end of file diff --git a/Modbus-CAPL/include/CAPL/include/ModbusEilClientCommon.cin b/Modbus-CAPL/include/CAPL/include/ModbusEilClientCommon.cin deleted file mode 100644 index 06c6ae3..0000000 --- a/Modbus-CAPL/include/CAPL/include/ModbusEilClientCommon.cin +++ /dev/null @@ -1,64 +0,0 @@ -/*@!Encoding:1252*/ - -includes -{ - #include "EilCommon.cin" - #include "ModbusClientCommon.cin" -} - -void ModbusConnectTo(char Remote_IP[], word Remote_Port) -{ - EilConnectTo(Remote_IP, Remote_Port); -} - -void ModbusConnectTo(dword Remote_IP, word Remote_Port) -{ - EilConnectTo(Remote_IP, Remote_Port); -} - -byte ModbusSnd(byte buffer[], word length) -{ - return EilSnd(buffer, length); -} - -void ModbusRecv() -{ - EilRecv(); -} - -void ModbusDisconnect() -{ - EilDisconnect(); -} - -long ModbusGetLastConnectionError(char string[]) -{ - return EilGetLastConnectionError(string); -} - -void OnEthReceivePacket(long channel, long dir, long packet) -{ - byte buffer[gMaxPacketLength]; - long size; - - if (dir == TX) - return; - - if (EthGetTokenInt(packet, "arp", "protSourceAddr") == gRemoteIP) // this was our ARP package - { - if (EthGetTokenData(packet, "arp", "hwSourceAddr", elCount(gRemoteMac), gRemoteMac) == 6) - { - gtArp.Cancel(); - writeDbg(ConnDebug, "Remote Mac: %02X:%02X:%02X:%02X:%02X:%02X", gRemoteMac[0], gRemoteMac[1], gRemoteMac[2], gRemoteMac[3], gRemoteMac[4], gRemoteMac[5]); - EilConnectTo2(); // create the UDP package - ModbusStartQueue(); - } - return; - } - - if (EthGetTokenInt(packet, "ipv4", "protocol") == 0x11 && EthGetTokenInt(packet, "ipv4", "source") == gRemoteIP) // if this is a UDP package from our server - { - size = EthGetThisData(0, gMaxPacketLength, buffer); - OnModbusReceive(0, 0, EthGetTokenInt(packet, "ipv4", "source"), gRemoteIP, buffer, size); - } -} \ 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 ef17756..18c0209 100644 --- a/Modbus-CAPL/include/CAPL/include/ModbusStructs.cin +++ b/Modbus-CAPL/include/CAPL/include/ModbusStructs.cin @@ -4,7 +4,7 @@ variables // according to Modbus Specification v1.1 const word gMaxBitsPerRead = 2000; const word gMaxRegsPerRead = 125; - const word gMaxBitsPerWrite = 1968; + const word gMaxBitsPerWrite = 1968; // Multiple of 8! const word gMaxRegsPerWrite = 123; // A normal Modbus Application Header. Every Modbus Packet begins with these 7 (+FuncCode) Bytes diff --git a/Modbus-CAPL/include/CAPL/include/ModbusTcp.cin b/Modbus-CAPL/include/CAPL/include/ModbusTcp.cin new file mode 100644 index 0000000..06859f3 --- /dev/null +++ b/Modbus-CAPL/include/CAPL/include/ModbusTcp.cin @@ -0,0 +1,222 @@ +/*@!Encoding:1252*/ + +// This file connected functions that abstract the UDP/IP API +/// It provides following methods +/// - ModbusConnectTo() Prepare anything that sending works. Here: Open a TCP socket and connection +/// - ModbusDisconnect() Gracefully disconnect from the device. Here: Close the TCP connection and socket +/// - ModbusRecv() Receive data from the device. Here: Wait for a TCP packet on the connection +/// - ModbusSnd() Send data to the device. Here: Send a packet on the TCP connection + +includes +{ + #include "Common.cin" + #include "TcpUdpEilCommon.cin" +} + +variables +{ + TcpSocket gSocket; +} + +// This method opens an TCP socket. It has to check for several errors. +word TcpOpenSocket() +{ + byte i; + char errorText[200]; + long error; + + if (EthGetAdapterStatus() != 2) // Not connected + { + writeDbg(ConnError, "TcpOpenSocket: Adapter status not ok: %d!", EthGetAdapterStatus()); + OnModbusClientPanics(ConnectionError); + return INVALID_IP; + } + + // Try to open socket + gSocket = TcpSocket::Open(0, 0); + error = gSocket.GetLastSocketError(); + if (error != 0) + { + gSocket.GetLastSocketErrorAsString(errorText, elcount(errorText)); + writeDbg(ConnInfo, "TcpOpenSocket: could not open socket: (%d) %s", error, errorText); + OnModbusClientPanics(ConnectionError); + return error; + } + else + { + writeDbg(ConnInfo, "Tcp socket opened."); + } + return 0; +} + +// This method prepares anything in a way that sending with ModbusSnd() is possible. +// Here: The method will open a TCP socket and connect to the remote device +word ModbusConnectTo(char Remote_IP[], word remotePort) +{ + dword remoteIp; + + // Convert IP string to Number + remoteIp = IpGetAddressAsNumber(Remote_IP); + if (remoteIp == INVALID_IP) + { + writeDbg(ConnError, "TcpConnectTo: invalid server Ip address: %s", Remote_IP); + OnModbusClientPanics(ConnectionError); + return 1; + } + + return ModbusConnectTo(remoteIp, remotePort); +} + +// This method prepares anything in a way that sending with ModbusSnd() is possible. +// Here: The method will open a TCP socket and connect to the remote device +word ModbusConnectTo(dword remoteIp, word remotePort) +{ + long fehler; + + // Try to open a socket + fehler = TcpOpenSocket(); + if (fehler != 0) + { + gSocketState = ERROR; + return fehler; + } + + gRemoteIP = remoteIp; + gRemotePort = remotePort; + + + // Connect to Server + if (gSocket.Connect(remoteIp, remotePort) != 0) + { + fehler = gSocket.GetLastSocketError(); + + if (fehler != WSAEWOULDBLOCK) // OnTcpConnect will be called otherwise + { + writeDbg(ConnError, "TcpConnectTo: No connection established: %d", fehler); + gSocketState = ERROR; + OnModbusClientPanics(ConnectionError); + return fehler; + } + return 0; + } + else + { + writeDbg(ConnInfo, "TcpConnectTo: Successfully connected to server"); + gSocketState = OK; + return 0; + } +} + +// This method will be called when TcpSocket.Connect() had to defer the result (and returned WSAEWOULDBLOCK). +// It checks whether the connection was opened successfully and sets the socket state accordingly +void OnTcpConnect(dword socket, long result) +{ + if (result != 0) + { + gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr)); + writeDbg(ConnError, "OnTcpConnect: (%d) %s", gSocket.GetLastSocketError(), gIpLastErrStr); + gSocketState = ERROR; + OnModbusClientPanics(ConnectionError); + return; + } + else + { + writeDbg(ConnInfo, "OnTcpConnect: Successfully connected to server"); + gSocketState = OK; + ModbusStartQueue(); + } +} + +// This method will gracefully disconnect from the remote device. +// Here: Simply close the socket. This will also disconnect the remote device +void ModbusDisconnect() +{ + gSocket.Close(); + gSocketState = CLOSED; +} + +// This method will check for data from the remote device. +// Here: Tell the API to check for new TCP telegrams. When data arrived OnTcpReceive() will be called by the API +void ModbusRecv() +{ + int result; + + if (gSocketState != OK) + { + writeDbg(ConnWarning, "ModbusRecv: Socket status is not OK! Doing nothing."); + OnModbusClientPanics(ConnectionError); + return; + } + + result = gSocket.Receive(gRxBuffer, elcount(gRxBuffer)); + + if (result != 0) // Calling OnTcpReceive otherwise + { + gIpLastErr = gSocket.GetLastSocketError(); + + if (gIpLastErr != WSA_IO_PENDING) // Calling OnTcpReceive otherwise + { + gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr)); + writeDbg(ConnError, "ModbusRecv: (%d) %s", gIpLastErr, gIpLastErrStr); + ModbusDisconnect(); + } + } + + return; +} + +// This method will send the payload 'buffer' to the device. +// Here: Call the appropriate API function +word ModbusSnd(byte buffer[], word length) +{ + char str[20*3]; + + switch (gSocketState) + { + case CLOSED: // If the connection is closed + ModbusConnectTo(gRemoteIP, gRemotePort); // Try to (re)connect + if (gSocketState != OK) // If this didn't work + { + writeDbg(ConnError, "ModbusSnd: Reconnecting failed!"); + OnModbusClientPanics(ConnectionError); + return 1; + } + case OK: + break; + default: + writeDbg(ConnError, "ModbusSnd: Socket status is not OK! Doing nothing."); + OnModbusClientPanics(ConnectionError); + return 1; + } + + bin_to_strhex(buffer, str); + writeDbg(ConnDebug, "ModbusSnd: %s (Länge: %d)", str, length); + + if (gSocket.Send(buffer, length) != 0) + { + gIpLastErr = gSocket.GetLastSocketError(); + + if (gIpLastErr != WSA_IO_PENDING) + { + gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr)); + writeDbg(ConnError, "ModbusSnd: (%d) %s", gIpLastErr, gIpLastErrStr); + ModbusDisconnect(); + return 1; + } + // else: tough luck! + } + return 0; +} + +// This method simply combines the two EthGetLastError functions +long ModbusGetLastConnectionError(char string[]) +{ + gSocket.GetLastSocketErrorAsString(string, elCount(string)); + return gSocket.GetLastSocketError(); +} + +// This method receives telegrams (from the TCP/IP API). ModbusRecv() has to be called first +void OnTcpReceive(dword socket, long result, dword address, dword port, byte buffer[], dword size) +{ + OnModbusReceive(socket, result, address, port, buffer, size); // Hand to Modbus layer +} \ No newline at end of file diff --git a/Modbus-CAPL/include/CAPL/include/ModbusTcpClientCommon.cin b/Modbus-CAPL/include/CAPL/include/ModbusTcpClientCommon.cin deleted file mode 100644 index cb7ceae..0000000 --- a/Modbus-CAPL/include/CAPL/include/ModbusTcpClientCommon.cin +++ /dev/null @@ -1,44 +0,0 @@ -/*@!Encoding:1252*/ - -includes -{ - #include "TcpCommon.cin" - #include "ModbusClientCommon.cin" -} - -void ModbusConnectTo(char Remote_IP[], word Remote_Port) -{ - TcpConnectTo(Remote_IP, Remote_Port); -} - -void ModbusConnectTo(dword Remote_IP, word Remote_Port) -{ - TcpConnectTo(Remote_IP, Remote_Port); -} - -void ModbusDisconnect() -{ - TcpDisconnect(); -} - -byte ModbusSnd(byte buffer[], word length) -{ - return TcpSnd(buffer, length); -} - -void ModbusRecv() -{ - TcpRecv(); -} - -long ModbusGetLastConnectionError(char string[]) -{ - return TcpGetLastConnectionError(string); -} - -void OnTcpReceive(dword socket, long result, dword address, dword port, byte buffer[], dword size) -{ - OnModbusReceive(socket, result, address, port, buffer, size); - if (result == 0 && size != 0) - TcpRecv(); -} \ No newline at end of file diff --git a/Modbus-CAPL/include/CAPL/include/ModbusUdp.cin b/Modbus-CAPL/include/CAPL/include/ModbusUdp.cin new file mode 100644 index 0000000..9b81dd2 --- /dev/null +++ b/Modbus-CAPL/include/CAPL/include/ModbusUdp.cin @@ -0,0 +1,182 @@ +/*@!Encoding:1252*/ + +// This file connected functions that abstract the TCP/IP API +/// It provides following methods +/// - ModbusConnectTo() Prepare anything that sending works. Here: Open a UDP socket +/// - ModbusDisconnect() Gracefully disconnect from the device. Here: Close the UDP socket +/// - ModbusRecv() Receive data from the device. Here: Wait for a UDP packet +/// - ModbusSnd() Send data to the device. Here: Send a packet on the UDP connection to the remote device 'gIpRemote' + +includes +{ + #include "Common.cin" + #include "TcpUdpEilCommon.cin" +} + +variables +{ + UdpSocket gSocket; +} + +// This method opens an UDP socket. It has to check for several errors. +word UdpOpenSocket() +{ + byte i; + char errorText[200]; + long error; + + if (EthGetAdapterStatus() != 2) // EthernetIL says: Not connected + { + writeDbg(ConnError, "UdpOpenSocket: Adapter status not ok: %d!", EthGetAdapterStatus()); + OnModbusClientPanics(ConnectionError); + return INVALID_IP; + } + + // Try to open socket + gSocket = UdpSocket::Open(0, 0); + error = gSocket.GetLastSocketError(); + if (error != 0) + { + gSocket.GetLastSocketErrorAsString(errorText, elcount(errorText)); + writeDbg(ConnInfo, "UdpOpenSocket: could not open socket: (%d) %s", error, errorText); + OnModbusClientPanics(ConnectionError); + return error; + } + else + { + writeDbg(ConnInfo, "Udp socket opened."); + } + return 0; +} + +// This method prepares anything in a way that sending with ModbusSnd() is possible. +// Here: The method will open a UDP socket and save the IP and Port in global variables so they can be used when sending +word ModbusConnectTo(char Remote_IP[], word remotePort) +{ + dword remoteIp; + + // Convert IP string to Number + remoteIp = IpGetAddressAsNumber(Remote_IP); + if (remoteIp == INVALID_IP) + { + writeDbg(ConnError, "ModbusConnectTo: invalid server Ip address: %s", Remote_IP); + OnModbusClientPanics(ConnectionError); + return 1; + } + + return ModbusConnectTo(remoteIp, remotePort); +} + +// This method prepares anything in a way that sending with ModbusSnd() is possible. +// Here: The method will open a UDP socket and save the IP and Port in global variables so they can be used when sending +word ModbusConnectTo(dword remoteIp, word remotePort) +{ + long fehler; + + // Try to open a socket + fehler = UdpOpenSocket(); + if (fehler != 0) + { + gSocketState = ERROR; + return fehler; + } + + gRemoteIP = remoteIp; + gRemotePort = remotePort; + gSocketState = OK; + return 0; +} + +// This method will gracefully disconnect from the remote device. +// Here: Simply close the socket +void ModbusDisconnect() +{ + gSocket.Close(); + gSocketState = CLOSED; +} + +// This method will check for data from the remote device. +// Here: Tell the API to check for new UDP datagrams. When data arrived OnUdpReceiveFrom() will be called by the API +void ModbusRecv() +{ + int result; + + if (gSocketState != OK) + { + writeDbg(ConnError, "ModbusRecv: Socket status is not OK! Doing nothing."); + OnModbusClientPanics(ConnectionError); + return; + } + + result = gSocket.ReceiveFrom(gRxBuffer, elCount(gRxBuffer)); + + if (result != 0) // Calling OnUdpReceiveFrom otherwise + { + gIpLastErr = gSocket.GetLastSocketError(); + + if (gIpLastErr != WSA_IO_PENDING) // Calling OnUdpReceive otherwise + { + gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elCount(gIpLastErrStr)); + writeDbg(ConnError, "UdpReceiveFrom: (%d) %s", gIpLastErr, gIpLastErrStr); + ModbusDisconnect(); + } + } + + return; +} + +// This method will send the payload 'buffer' to the device. +// Here: Call the appropriate API function +byte ModbusSnd(byte buffer[], word length) +{ + char str[20*3]; + + switch (gSocketState) + { + case CLOSED: // If the connection is closed + ModbusConnectTo(gRemoteIP, gRemotePort); // Try to (re)connect + if (gSocketState != OK) // If this didn't work + { + writeDbg(ConnError, "ModbusSnd: Reconnecting failed!"); + OnModbusClientPanics(ConnectionError); + return 1; + } + case OK: + break; + default: + writeDbg(ConnError, "ModbusSnd: Socket status is not OK! Doing nothing."); + OnModbusClientPanics(ConnectionError); + return 1; + } + + bin_to_strhex(buffer, str); + writeDbg(ConnDebug, "ModbusSnd: %s (Länge: %d)", str, length); + + if (gSocket.SendTo(gRemoteIP, gRemotePort, buffer, length) != 0) + { + gIpLastErr = gSocket.GetLastSocketError(); + + if (gIpLastErr != WSA_IO_PENDING) + { + gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr)); + writeDbg(ConnError, "ModbusSnd error (%d): %s", gIpLastErr, gIpLastErrStr); + ModbusDisconnect(); + return 1; + } + // else: tough luck! + } + return 0; +} + +// This method simply combines the two EthGetLastError functions +long ModbusGetLastConnectionError(char string[]) +{ + gSocket.GetLastSocketErrorAsString(string, elCount(string)); + return gSocket.GetLastSocketError(); +} + +// This method receives datagrams (from the UDP/IP API). ModbusRecv() has to be called first +void OnUdpReceiveFrom(dword socket, long result, dword address, dword port, byte buffer[], dword size) +{ + OnModbusReceive(socket, result, address, port, buffer, size); // Hand to Modbus layer +} \ No newline at end of file diff --git a/Modbus-CAPL/include/CAPL/include/ModbusUdpClientCommon.cin b/Modbus-CAPL/include/CAPL/include/ModbusUdpClientCommon.cin deleted file mode 100644 index 3c0fd9a..0000000 --- a/Modbus-CAPL/include/CAPL/include/ModbusUdpClientCommon.cin +++ /dev/null @@ -1,44 +0,0 @@ -/*@!Encoding:1252*/ - -includes -{ - #include "UdpCommon.cin" - #include "ModbusClientCommon.cin" -} - -void ModbusConnectTo(char Remote_IP[], word Remote_Port) -{ - UdpConnectTo(Remote_IP, Remote_Port); -} - -void ModbusConnectTo(dword Remote_IP, word Remote_Port) -{ - UdpConnectTo(Remote_IP, Remote_Port); -} - -void ModbusDisconnect() -{ - UdpDisconnect(); -} - -byte ModbusSnd(byte buffer[], word length) -{ - return UdpSnd(buffer, length); -} - -void ModbusRecv() -{ - UdpRecv(); -} - -long ModbusGetLastConnectionError(char string[]) -{ - return UdpGetLastConnectionError(string); -} - -void OnUdpReceiveFrom(dword socket, long result, dword address, dword port, byte buffer[], dword size) -{ - OnModbusReceive(socket, result, address, port, buffer, size); - if (result == 0 && size != 0) - UdpRecv(); -} \ No newline at end of file diff --git a/Modbus-CAPL/include/CAPL/include/TcpCommon.cin b/Modbus-CAPL/include/CAPL/include/TcpCommon.cin deleted file mode 100644 index 857d42b..0000000 --- a/Modbus-CAPL/include/CAPL/include/TcpCommon.cin +++ /dev/null @@ -1,204 +0,0 @@ -/*@!Encoding:1252*/ -includes -{ - #include "Common.cin" - #include "TcpUdpCommon.cin" -} - -variables -{ - TcpSocket gSocket; -} - - -word TcpOpenSocket() -{ - byte i; - CHAR errorText[200]; - long error; - - if (EthGetAdapterStatus() != 2) // Not connected - { - writeDbg(ConnError, "TcpOpenSocket: Adapter status not ok: %d!", EthGetAdapterStatus()); - OnModbusClientPanics(ConnectionError); - return INVALID_IP; - } - - // Try to open socket - i = 0; - do - { - gSocket = TcpSocket::Open(0, 0); - error = gSocket.GetLastSocketError(); - if (error != 0) - { - gSocket.GetLastSocketErrorAsString(errorText, elcount(errorText)); - writeDbg(ConnInfo, "TcpOpenSocket: could not open socket: (%d) %s", error, errorText); - } - } - while (error != 0 && i++ < 9); - - if (error != 0) - { - writeDbg(ConnError, "TcpOpenSocket: could not open socket: (%d) %s", error, errorText); - OnModbusClientPanics(ConnectionError); - return error; - } - else - { - writeDbg(ConnInfo, "Tcp socket opened."); - } - return 0; -} - -word TcpConnectTo(char Remote_IP[], word remotePort) -{ - dword remoteIp; - - // Convert IP string to Number - remoteIp = IpGetAddressAsNumber(Remote_IP); - if (remoteIp == INVALID_IP) - { - writeDbg(ConnError, "TcpConnectTo: invalid server Ip address: %s", Remote_IP); - OnModbusClientPanics(ConnectionError); - return 1; - } - - return TcpConnectTo(remoteIp, remotePort); -} - -word TcpConnectTo(dword remoteIp, word remotePort) -{ - long fehler; - - // Try to open a socket - fehler = TcpOpenSocket(); - if (fehler != 0) - { - gSocketState = ERROR; - return fehler; - } - - gRemoteIP = remoteIp; - gRemotePort = remotePort; - - - // Connect to Server - if (gSocket.Connect(remoteIp, remotePort) != 0) - { - fehler = gSocket.GetLastSocketError(); - - if (fehler != WSAEWOULDBLOCK) // OnTcpConnect will be called otherwise - { - writeDbg(ConnError, "TcpConnectTo: No connection established: %d", fehler); - gSocketState = ERROR; - OnModbusClientPanics(ConnectionError); - return fehler; - } - return 0; - } - else - { - writeDbg(ConnInfo, "TcpConnectTo: Successfully connected to server"); - gSocketState = OK; - return 0; - } -} - -void OnTcpConnect(dword socket, long result) -{ - if (result != 0) - { - gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr)); - writeDbg(ConnError, "OnTcpConnect: (%d) %s", gSocket.GetLastSocketError(), gIpLastErrStr); - gSocketState = ERROR; - OnModbusClientPanics(ConnectionError); - return; - } - else - { - writeDbg(ConnInfo, "OnTcpConnect: Successfully connected to server"); - gSocketState = OK; - ModbusStartQueue(); - } -} - -void TcpDisconnect() -{ - gSocket.Close(); - gSocketState = CLOSED; -} - -void TcpRecv() -{ - int result; - - if (gSocketState != OK) - { - writeDbg(ConnWarning, "TcpRecv: Socket status is not OK! Doing nothing."); - OnModbusClientPanics(ConnectionError); - return; - } - - result = gSocket.Receive(gRxBuffer, elcount(gRxBuffer)); - - if (result != 0) // Calling OnTcpReceive otherwise - { - gIpLastErr = gSocket.GetLastSocketError(); - - if (gIpLastErr != WSA_IO_PENDING) // Calling OnTcpReceive otherwise - { - gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr)); - writeDbg(ConnError, "TcpReceive: (%d) %s", gIpLastErr, gIpLastErrStr); - TcpDisconnect(); - } - } - - return; -} - -word TcpSnd(byte buffer[], word length) -{ - char str[20*3]; - - switch (gSocketState) - { - case CLOSED: - TcpConnectTo(gRemoteIP, gRemotePort); - if (gSocketState != OK) - { - writeDbg(ConnError, "TcpSnd: Reconnecting failed!"); - OnModbusClientPanics(ConnectionError); - return 1; - } - case OK: - break; - default: - writeDbg(ConnError, "TcpSnd: Socket status is not OK! Doing nothing."); - OnModbusClientPanics(ConnectionError); - return 1; - } - - bin_to_strhex(buffer, str); - writeDbg(ConnDebug, "TcpSnd: %s (Länge: %d)", str, length); - - if (gSocket.Send(buffer, length) != 0) - { - gIpLastErr = gSocket.GetLastSocketError(); - - if (gIpLastErr != WSA_IO_PENDING) - { - gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr)); - writeDbg(ConnError, "TcpSnd: (%d) %s", gIpLastErr, gIpLastErrStr); - TcpDisconnect(); - return 1; - } - } - return 0; -} - -long TcpGetLastConnectionError(char string[]) -{ - gSocket.GetLastSocketErrorAsString(string, elCount(string)); - return gSocket.GetLastSocketError(); -} \ No newline at end of file diff --git a/Modbus-CAPL/include/CAPL/include/TcpUdpCommon.cin b/Modbus-CAPL/include/CAPL/include/TcpUdpEilCommon.cin similarity index 65% rename from Modbus-CAPL/include/CAPL/include/TcpUdpCommon.cin rename to Modbus-CAPL/include/CAPL/include/TcpUdpEilCommon.cin index be3029a..75aabb1 100644 --- a/Modbus-CAPL/include/CAPL/include/TcpUdpCommon.cin +++ b/Modbus-CAPL/include/CAPL/include/TcpUdpEilCommon.cin @@ -2,19 +2,22 @@ variables { + // Some constants const long WSA_IO_PENDING = 997; const long WSAEWOULDBLOCK = 10035; const dword INVALID_IP = 0xffffffff; - + long gIpLastErr = 0; char gIpLastErrStr[512] = ""; + // The state of the socket will be safed here. This way we can check if we can send/receive packets enum SocketState { NULL, OK, ERROR, CLOSED }; enum SocketState gSocketState = NULL; dword gRemoteIP = INVALID_IP; word gRemotePort = 0; + // The buffer where to received telegrams are put byte gRxBuffer[8192]; } diff --git a/Modbus-CAPL/include/CAPL/include/UdpCommon.cin b/Modbus-CAPL/include/CAPL/include/UdpCommon.cin deleted file mode 100644 index 712f545..0000000 --- a/Modbus-CAPL/include/CAPL/include/UdpCommon.cin +++ /dev/null @@ -1,166 +0,0 @@ -/*@!Encoding:1252*/ -includes -{ - #include "Common.cin" - #include "TcpUdpCommon.cin" -} - -variables -{ - UdpSocket gSocket; -} - - -word UdpOpenSocket() -{ - byte i; - char errorText[200]; - long error; - - if (EthGetAdapterStatus() != 2) // Not connected - { - writeDbg(ConnError, "UdpOpenSocket: Adapter status not ok: %d!", EthGetAdapterStatus()); - OnModbusClientPanics(ConnectionError); - return INVALID_IP; - } - - // Try to open socket - i = 0; - do - { - gSocket = UdpSocket::Open(0, 0); - error = gSocket.GetLastSocketError(); - if (error != 0) - { - gSocket.GetLastSocketErrorAsString(errorText, elcount(errorText)); - writeDbg(ConnInfo, "UdpOpenSocket: could not open socket: (%d) %s", error, errorText); - } - } - while (error != 0 && i++ < 9); - - if (error != 0) - { - writeDbg(ConnError, "UdpOpenSocket: could not open socket: (%d) %s", error, errorText); - OnModbusClientPanics(ConnectionError); - return error; - } - else - { - writeDbg(ConnInfo, "Udp socket opened."); - } - return 0; -} - -word UdpConnectTo(char Remote_IP[], word remotePort) -{ - dword remoteIp; - - // Convert IP string to Number - remoteIp = IpGetAddressAsNumber(Remote_IP); - if (remoteIp == INVALID_IP) - { - writeDbg(ConnError, "UdpConnectTo: invalid server Ip address: %s", Remote_IP); - OnModbusClientPanics(ConnectionError); - return 1; - } - - return UdpConnectTo(remoteIp, remotePort); -} - -word UdpConnectTo(dword remoteIp, word remotePort) -{ - long fehler; - - // Try to open a socket - fehler = UdpOpenSocket(); - if (fehler != 0) - { - gSocketState = ERROR; - return fehler; - } - - gRemoteIP = remoteIp; - gRemotePort = remotePort; - gSocketState = OK; - return 0; -} - -void UdpDisconnect() -{ - gSocket.Close(); - gSocketState = CLOSED; -} - -void UdpRecv() -{ - int result; - - if (gSocketState != OK) - { - writeDbg(ConnError, "UdpRecv: Socket status is not OK! Doing nothing."); - OnModbusClientPanics(ConnectionError); - return; - } - - result = gSocket.ReceiveFrom(gRxBuffer, elCount(gRxBuffer)); - - if (result != 0) // Calling OnUdpReceive otherwise - { - gIpLastErr = gSocket.GetLastSocketError(); - - if (gIpLastErr != WSA_IO_PENDING) // Calling OnUdpReceive otherwise - { - gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elCount(gIpLastErrStr)); - writeDbg(ConnError, "UdpReceiveFrom: (%d) %s", gIpLastErr, gIpLastErrStr); - UdpDisconnect(); - } - } - - return; -} - -byte UdpSnd(byte buffer[], word length) -{ - char str[20*3]; - - switch (gSocketState) - { - case CLOSED: - UdpConnectTo(gRemoteIP, gRemotePort); - if (gSocketState != OK) - { - writeDbg(ConnError, "UdpSnd: Reconnecting failed!"); - OnModbusClientPanics(ConnectionError); - return 1; - } - case OK: - break; - default: - writeDbg(ConnError, "UdpSnd: Socket status is not OK! Doing nothing."); - OnModbusClientPanics(ConnectionError); - return 1; - } - - bin_to_strhex(buffer, str); - writeDbg(ConnDebug, "UdpSnd: %s (Länge: %d)", str, length); - - if (gSocket.SendTo(gRemoteIP, gRemotePort, buffer, length) != 0) - { - gIpLastErr = gSocket.GetLastSocketError(); - - if (gIpLastErr != WSA_IO_PENDING) - { - gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr)); - writeDbg(ConnError, "UdpSnd error (%d): %s", gIpLastErr, gIpLastErrStr); - UdpDisconnect(); - return 1; - } - } - return 0; -} - -long UdpGetLastConnectionError(char string[]) -{ - gSocket.GetLastSocketErrorAsString(string, elCount(string)); - return gSocket.GetLastSocketError(); -} \ No newline at end of file diff --git a/Modbus-CAPL/include/DBC/generated.dbc b/Modbus-CAPL/include/DBC/generated.dbc index e790666..bb9596d 100644 --- a/Modbus-CAPL/include/DBC/generated.dbc +++ b/Modbus-CAPL/include/DBC/generated.dbc @@ -33,7 +33,7 @@ NS_ : BS_: -BU_: Client_2 Client_3 +BU_: Client_ Client_100 Client_101 diff --git a/Modbus-CAPL/include/SysVars/airbus.vmap b/Modbus-CAPL/include/SysVars/airbus.vmap index a877357..0d7d85a 100644 --- a/Modbus-CAPL/include/SysVars/airbus.vmap +++ b/Modbus-CAPL/include/SysVars/airbus.vmap @@ -1,195 +1,291 @@  - + - 0 - Ethernet1::Client_2::Data::InputRegisters - - - Ethernet1::Client_2::Data::InputRegisters - 5 - - Ethernet1::Client_2::Data::InputRegisters - False - - - - 5 - - -1 - Airbus::R14::S5_1 + Airbus::DEU223RH06::DC1_E - Airbus::R14::S5_1 + Airbus::DEU223RH06::DC1_E 2 - Airbus::R14::S5_1 + Airbus::DEU223RH06::DC1_E False 1 + + + 1 + Ethernet1::Client_100::Data::OutputBits + + + Ethernet1::Client_100::Data::OutputBits + 5 + + Ethernet1::Client_100::Data::OutputBits + False + + + + 5 - + + -1 + Airbus::DEU223RH06::DC1_N + + + Airbus::DEU223RH06::DC1_N + 2 + + Airbus::DEU223RH06::DC1_N + False + + + + 1 + + 3 - Ethernet1::Client_3::Data::InputRegisters + Ethernet1::Client_100::Data::OutputBits - Ethernet1::Client_3::Data::InputRegisters + Ethernet1::Client_100::Data::OutputBits 5 - Ethernet1::Client_3::Data::InputRegisters + Ethernet1::Client_100::Data::OutputBits False 5 - - + + + + -1 - Airbus::R14::S5_2 + Airbus::DEU223RH06::DC2_E - Airbus::R14::S5_2 + Airbus::DEU223RH06::DC2_E 2 - Airbus::R14::S5_2 + Airbus::DEU223RH06::DC2_E False 1 - - - - + + 0 - Ethernet1::Client_2::Data::InputBits + Ethernet1::Client_100::Data::OutputBits - Ethernet1::Client_2::Data::InputBits + Ethernet1::Client_100::Data::OutputBits 5 - Ethernet1::Client_2::Data::InputBits + Ethernet1::Client_100::Data::OutputBits False 5 - - + + + + -1 - Airbus::R12::L1 + Airbus::DEU223RH06::DC2_N - Airbus::R12::L1 + Airbus::DEU223RH06::DC2_N 2 - Airbus::R12::L1 + Airbus::DEU223RH06::DC2_N False 1 - - - - + + 2 - Ethernet1::Client_3::Data::InputBits + Ethernet1::Client_100::Data::OutputBits - Ethernet1::Client_3::Data::InputBits + Ethernet1::Client_100::Data::OutputBits 5 - Ethernet1::Client_3::Data::InputBits + Ethernet1::Client_100::Data::OutputBits False 5 - - + + + + -1 - Airbus::R14::L1 + Airbus::TL_CUT_OFF - Airbus::R14::L1 + Airbus::TL_CUT_OFF 2 - Airbus::R14::L1 + Airbus::TL_CUT_OFF False 1 - - - - + + 0 - Ethernet1::Client_2::Data::OutputBits + Ethernet1::Client_100::Data::InputBits - Ethernet1::Client_2::Data::OutputBits + Ethernet1::Client_100::Data::InputBits 5 - Ethernet1::Client_2::Data::OutputBits + Ethernet1::Client_100::Data::InputBits False 5 - - - -1 - Airbus::R14::X - - - Airbus::R14::X - 2 - - Airbus::R14::X - False - - - - 1 - + - 9 - Ethernet1::Client_2::Data::OutputBits - - - Ethernet1::Client_2::Data::OutputBits - 5 - - Ethernet1::Client_2::Data::OutputBits - False - - - - 5 - - -1 - Airbus::R12::X + Airbus::DEU226RH06::DC1_N - Airbus::R12::X + Airbus::DEU226RH06::DC1_N 2 - Airbus::R12::X + Airbus::DEU226RH06::DC1_N False 1 + + + 7 + Ethernet1::Client_100::Data::OutputBits + + + Ethernet1::Client_100::Data::OutputBits + 5 + + Ethernet1::Client_100::Data::OutputBits + False + + + + 5 + + + + + -1 + Airbus::DEU226RH06::DC2_E + + + Airbus::DEU226RH06::DC2_E + 2 + + Airbus::DEU226RH06::DC2_E + False + + + + 1 + + + 4 + Ethernet1::Client_100::Data::OutputBits + + + Ethernet1::Client_100::Data::OutputBits + 5 + + Ethernet1::Client_100::Data::OutputBits + False + + + + 5 + + + + + -1 + Airbus::DEU226RH06::DC2_N + + + Airbus::DEU226RH06::DC2_N + 2 + + Airbus::DEU226RH06::DC2_N + False + + + + 1 + + + 6 + Ethernet1::Client_100::Data::OutputBits + + + Ethernet1::Client_100::Data::OutputBits + 5 + + Ethernet1::Client_100::Data::OutputBits + False + + + + 5 + + + + + -1 + Airbus::DEU226RH06::DC1_E + + + Airbus::DEU226RH06::DC1_E + 2 + + Airbus::DEU226RH06::DC1_E + False + + + + 1 + + + 5 + Ethernet1::Client_100::Data::OutputBits + + + Ethernet1::Client_100::Data::OutputBits + 5 + + Ethernet1::Client_100::Data::OutputBits + False + + + + 5 \ No newline at end of file diff --git a/Modbus-CAPL/include/SysVars/generated.vsysvar b/Modbus-CAPL/include/SysVars/generated.vsysvar index 8759068..50c88ce 100644 --- a/Modbus-CAPL/include/SysVars/generated.vsysvar +++ b/Modbus-CAPL/include/SysVars/generated.vsysvar @@ -5,16 +5,43 @@ - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -39,29 +66,31 @@ - + + + - + - + - + - - - - + + + + - - + +