2014-05-12 10:46:53 +02:00
/*@!Encoding:1252*/
2014-06-20 12:45:41 +02:00
// 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.
2014-05-12 10:46:53 +02:00
includes
{
2014-06-20 12:45:41 +02:00
#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
2014-05-12 10:46:53 +02:00
}
variables
{
2014-06-20 12:45:41 +02:00
msTimer gtRead; // The timer that keeps on polling all the time
2014-05-12 10:46:53 +02:00
}
on preStart
{
2014-06-20 12:45:41 +02:00
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)
2014-05-12 10:46:53 +02:00
}
2014-07-08 13:38:32 +02:00
// Connect to Modbus server, read the status of output registers and bits and start the cyclic timer
2014-05-12 10:46:53 +02:00
on start
{
2014-06-20 12:45:41 +02:00
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)
2014-05-21 13:26:45 +02:00
2014-06-20 12:45:41 +02:00
writeDbg(MbInfo, "Connecting to %s:%d", ip, @sysvar::Config::Modbus::Port);
2014-07-04 13:29:18 +02:00
ModbusInit(ip, @sysvar::Config::Modbus::Port, @sysvar::Config::Modbus::RequestTimeout, @sysvar::Config::Modbus::MaxTransmissionCount); // Connect to device. Opens socket and connection or what ever
2014-05-26 12:07:04 +02:00
2014-07-10 09:56:55 +02:00
if (gSocketState < CONNECTING) // We are not connecting and not connected
return;
ModbusReadOutBits(thisDev.Addr.Read.OutputBits, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits); // Read the start status of the output bits
ModbusReadOutRegisters(thisDev.Addr.Read.OutputRegisters, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters); // Read the start status of the output registers
2014-06-20 12:45:41 +02:00
if (@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config::Interval > 0) // Start the polling timer
2014-06-17 18:41:05 +02:00
setTimerCyclic(gtRead, 1, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config::Interval);
2014-05-12 10:46:53 +02:00
}
2014-07-10 09:56:55 +02:00
// Stop all transactions and close connection
on preStop
{
ModbusEnd();
}
2014-05-12 10:46:53 +02:00
// Modbus events ----------------------------------------------------------------------
2014-06-20 12:45:41 +02:00
/// 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)
2014-07-08 13:38:32 +02:00
// This method gets called when an error occured while trying to read some bits (ModbusReadBits())
2014-05-12 10:46:53 +02:00
void OnModbusReadBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap)
{
2014-05-26 12:07:04 +02:00
word i;
2014-05-21 13:29:29 +02:00
switch (error)
{
case Exception:
case Timeout:
2014-07-14 11:37:21 +02:00
case NotSent:
2014-05-21 13:29:29 +02:00
break;
case FinalTimeout:
2014-05-26 12:07:04 +02:00
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputBits");
2014-05-21 13:29:29 +02:00
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputBits; i++)
2014-05-26 12:07:04 +02:00
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::InputBits[i] = -1;
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputBits");
2014-05-21 13:29:29 +02:00
break;
2014-07-14 11:37:21 +02:00
default:
writeDbg(MbError, "OnModbusReadBitsFailed: Unkown error: %d", error);
OnModbusClientPanics(SwitchArgumentInvalid);
return;
2014-05-21 13:29:29 +02:00
}
2014-05-15 14:43:52 +02:00
}
2014-05-21 13:29:29 +02:00
2014-07-08 13:38:32 +02:00
// This method gets called when an error occured while trying to read some registers (ModbusReadRegisters())
2014-05-26 12:07:04 +02:00
void OnModbusReadRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap)
2014-05-15 14:43:52 +02:00
{
2014-05-26 12:07:04 +02:00
byte i;
2014-05-21 13:29:29 +02:00
switch (error)
{
case Exception:
case Timeout:
2014-07-14 11:37:21 +02:00
case NotSent:
2014-05-21 13:29:29 +02:00
break;
case FinalTimeout:
2014-05-26 12:07:04 +02:00
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputRegisters");
2014-05-21 13:29:29 +02:00
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters; i++)
2014-05-26 12:07:04 +02:00
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::InputRegisters[i] = -1;
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputRegisters");
2014-05-21 13:29:29 +02:00
break;
2014-07-14 11:37:21 +02:00
default:
writeDbg(MbError, "OnModbusReadBitsFailed: Unkown error: %d", error);
OnModbusClientPanics(SwitchArgumentInvalid);
return;
2014-05-21 13:29:29 +02:00
}
2014-05-15 14:43:52 +02:00
}
2014-07-08 13:38:32 +02:00
// This method gets called when an error occured while trying to set a bit (ModbusWriteBit())
2014-06-20 12:45:41 +02:00
void OnModbusWriteBitFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
2014-07-08 13:38:32 +02:00
// This method gets called when an error occured while trying to set a register (ModbusWriteRegister())
2014-06-20 12:45:41 +02:00
void OnModbusWriteRegisterFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
2014-07-08 13:38:32 +02:00
// This method gets called when an error occured while trying to apply a mask on a register (ModbusWriteMask())
2014-06-20 12:45:41 +02:00
void OnModbusWriteMasksFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
2014-07-08 13:38:32 +02:00
// This method gets called when an error occured while trying to read and write registers (ModbusReadWriteRegisters())
2014-06-20 12:45:41 +02:00
void OnModbusReadWriteRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
2014-07-08 13:38:32 +02:00
// This method gets called when an error occured while trying to set multiple bits (ModbusWriteBits())
2014-06-20 12:45:41 +02:00
void OnModbusWriteBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
2014-07-08 13:38:32 +02:00
// This method gets called when an error occured while trying to set multiple registers (ModbusWriteRegisters())
2014-06-20 12:45:41 +02:00
void OnModbusWriteRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
2014-05-26 12:07:04 +02:00
2014-06-20 12:45:41 +02:00
// -- 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
2014-05-12 10:46:53 +02:00
2014-06-20 12:45:41 +02:00
// This method gets called when some bits were read successfully. See 'bitStatus' for their values and 'mbreq.Address' for their start address
2014-05-27 13:03:56 +02:00
void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[], struct ModbusReqRead mbreq)
2014-05-12 10:46:53 +02:00
{
2014-06-17 16:21:45 +02:00
word i, offset;
2014-05-21 13:29:29 +02:00
2014-06-17 16:21:45 +02:00
switch (mbres.Header.FuncCode) // We assume that we separate between 0x01 and 0x02 even though the address space may be the same
2014-05-21 13:29:29 +02:00
{
2014-07-08 13:38:32 +02:00
case ReadBitsOut: // Read output bits
2014-05-26 12:07:04 +02:00
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
2014-06-17 16:21:45 +02:00
2014-07-10 09:56:55 +02:00
offset = mbreq.Address - thisDev.Addr.Read.OutputBits; // Get the offset to the base output bit address
2014-06-17 16:21:45 +02:00
for (i = 0; i < mbreq.Count; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits[i + offset] = bitStatus[i];
2014-05-26 12:07:04 +02:00
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
break;
2014-06-17 16:21:45 +02:00
2014-07-08 13:38:32 +02:00
case ReadBitsIn: // Read input bits
2014-05-26 12:07:04 +02:00
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputBits");
2014-06-17 16:21:45 +02:00
2014-07-10 09:56:55 +02:00
offset = mbreq.Address - thisDev.Addr.Read.InputBits; // Get the offset to the base input bit address
2014-06-17 16:21:45 +02:00
for (i = 0; i < mbreq.Count; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::InputBits[i + offset] = bitStatus[i];
2014-05-26 12:07:04 +02:00
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputBits");
2014-05-21 13:29:29 +02:00
break;
2014-07-14 11:37:21 +02:00
default:
writeDbg(MbError, "OnModbusReadBitsSuccess: Unexpected function code: 0x%02X", mbreq.Header.FuncCode);
OnModbusClientPanics(FuncCodeIncorrect);
return;
2014-05-21 13:29:29 +02:00
}
2014-05-12 10:46:53 +02:00
}
2014-05-21 13:29:29 +02:00
2014-06-20 12:45:41 +02:00
// This method gets called when some bits were read successfully. See 'mbres.Data' for their values and 'mbreq.Address' for their start address
2014-05-27 13:03:56 +02:00
void OnModbusReadRegistersSuccess(struct ModbusResReceiveRegisters mbres, struct ModbusReqRead mbreq)
2014-05-12 10:46:53 +02:00
{
2014-06-17 16:21:45 +02:00
word i, offset;
2014-05-15 14:43:52 +02:00
2014-06-17 16:21:45 +02:00
switch (mbres.Header.FuncCode) // We assume that we separate between 0x03 and 0x04 even though the address space may be the same
2014-05-15 14:43:52 +02:00
{
2014-07-08 13:38:32 +02:00
case ReadRegistersOut: // Read output registers
2014-05-26 12:07:04 +02:00
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputRegisters");
2014-06-17 16:21:45 +02:00
2014-07-10 09:56:55 +02:00
offset = mbreq.Address - thisDev.Addr.Read.OutputRegisters; // Get the offset to the base output register address
2014-06-17 16:21:45 +02:00
for (i = 0; i < mbreq.Count; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputRegisters[i + offset] = mbres.Data[i];
2014-05-26 12:07:04 +02:00
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputRegisters");
2014-05-15 14:43:52 +02:00
break;
2014-06-17 16:21:45 +02:00
2014-07-08 13:38:32 +02:00
case ReadRegistersIn: // Read input registers
2014-05-26 12:07:04 +02:00
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputRegisters");
2014-06-17 16:21:45 +02:00
2014-07-10 09:56:55 +02:00
offset = mbreq.Address - thisDev.Addr.Read.InputRegisters; // Get the offset to the base input bit address
2014-06-17 16:21:45 +02:00
for (i = 0; i < mbreq.Count; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::InputRegisters[i + offset] = mbres.Data[i];
2014-05-26 12:07:04 +02:00
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputRegisters");
2014-05-21 13:29:29 +02:00
break;
2014-07-14 11:37:21 +02:00
default:
writeDbg(MbError, "OnModbusReadBitsSuccess: Unexpected function code: 0x%02X", mbreq.Header.FuncCode);
OnModbusClientPanics(FuncCodeIncorrect);
return;
2014-05-15 14:43:52 +02:00
}
2014-05-12 10:46:53 +02:00
}
2014-05-21 13:29:29 +02:00
2014-06-20 12:45:41 +02:00
// This method gets called when a bit was set successfully.
2014-07-07 12:24:11 +02:00
void OnModbusWriteBitSuccess(struct ModbusResConfirmSingle mbres){}
2014-06-20 12:45:41 +02:00
// This method gets called when a register was set successsfully.
2014-07-07 12:24:11 +02:00
void OnModbusWriteRegisterSuccess(struct ModbusResConfirmSingle mbres){}
2014-06-20 12:45:41 +02:00
// This method gets called when multiple bits were set successfully.
2014-07-07 12:24:11 +02:00
void OnModbusWriteBitsSuccess(struct ModbusResConfirmMultiple mbres){}
2014-06-20 12:45:41 +02:00
// This method gets called when multiple registers were set successfully.
2014-07-07 12:24:11 +02:00
void OnModbusWriteRegistersSuccess(struct ModbusResConfirmMultiple mbres){}
2014-06-20 12:45:41 +02:00
// This method gets called when a mask was applied successfully.
2014-07-07 12:24:11 +02:00
void OnModbusWriteMasksSuccess(struct ModbusResConfirmMasks mbres){}
2014-06-20 12:45:41 +02:00
2014-05-12 10:46:53 +02:00
2014-06-20 12:45:41 +02:00
// 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.
2014-05-27 13:03:56 +02:00
void OnModbusClientPanics(enum FatalErrors reason)
{
switch(reason)
{
case ParsingBuffer:
2014-07-14 11:37:21 +02:00
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error while parsing the received buffer");
2014-07-10 09:56:55 +02:00
runError(1001, reason);
break;
2014-05-27 13:03:56 +02:00
case ModbusPackageWasSplit:
2014-07-10 09:56:55 +02:00
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error while parsing the received buffer: The Modbus package was split", reason);
runError(1001, reason);
break;
2014-05-27 13:03:56 +02:00
case VendorIdUnknown:
2014-07-14 11:37:21 +02:00
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Vendor ID unknown");
2014-07-10 09:56:55 +02:00
runError(1001, reason);
break;
case FuncCodeIncorrect:
2014-07-14 11:37:21 +02:00
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Function code is incorrect");
2014-07-10 09:56:55 +02:00
runError(1001, reason);
break;
case AddressFailure:
2014-07-14 11:37:21 +02:00
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Start address is incorrect");
2014-05-27 13:03:56 +02:00
runError(1001, reason);
break;
case ConnectionError:
2014-07-14 11:37:21 +02:00
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Connection Error");
2014-05-27 13:03:56 +02:00
gtRead.Cancel();
break;
2014-07-14 11:37:21 +02:00
case SwitchArgumentInvalid:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: A argument of a switch statement is incorrect");
runError(1001, reason);
break;
2014-05-27 13:03:56 +02:00
}
2014-07-14 11:37:21 +02:00
stop();
2014-05-27 13:03:56 +02:00
}
2014-05-12 10:46:53 +02:00
2014-06-20 12:45:41 +02:00
// -------------------------------------------------------------------------
// The timer will continuously poll the input registers and intput bits
2014-05-21 13:26:45 +02:00
on timer gtRead
{
2014-07-10 09:56:55 +02:00
ModbusReadRegisters(thisDev.Addr.Read.InputRegisters, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters);
ModbusReadBits(thisDev.Addr.Read.InputBits, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputBits);
2014-05-21 13:26:45 +02:00
}
2014-06-20 12:45:41 +02:00
// If Data::OutputBits is changed we will send this update to the device
2014-05-21 13:29:29 +02:00
on sysvar %BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits
2014-05-15 14:43:52 +02:00
{
2014-06-16 09:23:21 +02:00
word count, i;
2014-05-21 13:29:29 +02:00
byte bitStatus[1968];
2014-05-26 12:07:04 +02:00
count = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits;
2014-06-20 12:45:41 +02:00
for (i = 0; i < count; i++) // Copy the data from SysVars to byte[]
2014-05-26 12:07:04 +02:00
bitStatus[i] = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits[i];
2014-06-20 12:45:41 +02:00
ModbusWriteBitsB(0, count, bitStatus); // Send update command
2014-05-15 14:43:52 +02:00
}
2014-06-20 12:45:41 +02:00
// If Data::OutputRergisters is changed we will send this update to the device
2014-05-21 13:29:29 +02:00
on sysvar %BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputRegisters
2014-05-12 10:46:53 +02:00
{
2014-05-26 12:07:04 +02:00
word count, i;
word regValues[123];
count = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters;
2014-06-20 12:45:41 +02:00
for (i = 0; i < count; i++) // Copy the data from SysVars to word[]
2014-05-26 12:07:04 +02:00
regValues[i] = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputRegisters[i];
2014-06-20 12:45:41 +02:00
ModbusWriteRegisters(0, count, regValues); // Send update command
2014-05-26 12:07:04 +02:00
}
2014-06-20 12:45:41 +02:00
// Config::Interval is changed we will update the timer gtRead accordingly
2014-05-26 12:07:04 +02:00
on sysvar %BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config::Interval
{
if (@this <= 0)
gtRead.Cancel();
else
setTimerCyclic(gtRead, @this);
2014-05-21 13:26:45 +02:00
}