/*@!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\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; // The timer that keeps on polling all the time } on preStart { 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 { char ip[16]; sysGetVariableString("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config", "IP", ip, elCount(ip)); // Get IP address of device from sysvars config DeviceInit(@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::Vendor); // Set all device specific parameters (Wago / B&R) writeDbg(MbInfo, "Connecting to %s:%d", ip, @sysvar::Config::Modbus::Port); ModbusInit(ip, @sysvar::Config::Modbus::Port, @sysvar::Config::Modbus::RequestTimeout, @sysvar::Config::Modbus::MaxTransmissionCount); // 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; switch (error) { case Exception: break; case Timeout: break; case FinalTimeout: 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] = -1; sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputBits"); break; } } /// 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; switch (error) { case Exception: break; case Timeout: break; case FinalTimeout: 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] = -1; sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputRegisters"); break; } } /// 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; switch (mbres.Header.FuncCode) // We assume that we separate between 0x01 and 0x02 even though the address space may be the same { case 0x01: // Read output bits sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits"); 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]; sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits"); break; case 0x02: // Read input bits sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputBits"); 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; } } // 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; switch (mbres.Header.FuncCode) // We assume that we separate between 0x03 and 0x04 even though the address space may be the same { case 0x03: // Read output registers sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputRegisters"); 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]; sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputRegisters"); break; case 0x04: // Read input registers sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputRegisters"); 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; } } // This method gets called when a bit was set successfully. void OnModbusWriteBitSuccess(struct ModbusResConfirmSingle mbres){} // This method gets called when a register was set successsfully. void OnModbusWriteRegisterSuccess(struct ModbusResConfirmSingle mbres){} // This method gets called when multiple bits were set successfully. void OnModbusWriteBitsSuccess(struct ModbusResConfirmMultiple mbres){} // This method gets called when multiple registers were set successfully. void OnModbusWriteRegistersSuccess(struct ModbusResConfirmMultiple mbres){} // This method gets called when a mask was applied successfully. void OnModbusWriteMasksSuccess(struct ModbusResConfirmMasks mbres){} // 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); switch(reason) { case ParsingBuffer: case ModbusPackageWasSplit: case VendorIdUnknown: runError(1001, reason); break; case ConnectionError: gtRead.Cancel(); break; } } // ------------------------------------------------------------------------- // 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); } // 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; byte bitStatus[1968]; count = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits; 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); // 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; word regValues[123]; count = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters; 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(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) gtRead.Cancel(); else setTimerCyclic(gtRead, @this); }