Converted tabs to spaces.

I still think tabs are better :P
This commit is contained in:
Jonny007-MKD 2014-09-23 09:48:47 +00:00
parent e534955938
commit 6125015cc5
24 changed files with 4466 additions and 4470 deletions

View file

@ -4,9 +4,9 @@
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
#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
@ -16,43 +16,43 @@ variables
on preStart
{
writeClear(0); // Clear write window in CANoe
setStartdelay(10); // Wait for Ethernet device to be ready
OutputDebugLevel = Error; // The debug level (messages in write window)
writeClear(0); // Clear write window in CANoe
setStartdelay(10); // Wait for Ethernet device to be ready
OutputDebugLevel = Error; // The debug level (messages in write window)
}
// Connect to Modbus server, read the status of output registers and bits and start the cyclic timer
on start
{
char ip[16];
sysGetVariableString("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config", "IP", ip, elCount(ip)); // Get IP address of device from sysvars config
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)
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
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
if (gSocketState < CONNECTING) // We are not connecting and not connected
return;
if (gSocketState < CONNECTING) // We are not connecting and not connected
return;
//ModbusReadRegisters(0x1083, 1);
//ModbusReadRegisters(0x1083, 1);
}
// Stop all transactions and close connection
on preStop
{
ModbusEnd();
ModbusEnd();
}
// Modbus events ----------------------------------------------------------------------
/// All these events will be called by functions out of ModbusClientCommon.cin
/// 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)
/// 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){}
@ -72,8 +72,8 @@ void OnModbusWriteBitsFailed(enum ModbusRequestError error, enum ModbusException
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
/// 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
@ -81,7 +81,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)
{
writeLineEx(0, 1, "<%NODE_NAME%> 0x%04X = 0x%04X (%d)", mbreq.Address, mbres.Data[0], mbres.Data[0]);
writeLineEx(0, 1, "<%NODE_NAME%> 0x%04X = 0x%04X (%d)", mbreq.Address, mbres.Data[0], mbres.Data[0]);
}
// This method gets called when a bit was set successfully.
void OnModbusWriteBitSuccess(struct ModbusResConfirmSingle mbres){}
@ -99,30 +99,30 @@ void OnModbusWriteMasksSuccess(struct ModbusResConfirmMasks mbres){}
// It will pass as argument what happened. Please see the log (increase debug level in preStart) for more details.
void OnModbusClientPanics(enum FatalErrors reason)
{
switch(reason)
{
case ParsingBuffer:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error while parsing the received buffer");
break;
case ModbusPackageWasSplit:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error while parsing the received buffer: The Modbus package was split", reason);
break;
case VendorIdUnknown:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Vendor ID unknown");
break;
case FuncCodeIncorrect:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Function code is incorrect");
break;
case AddressFailure:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Start address is incorrect");
break;
case ConnectionError:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Connection Error");
break;
case SwitchArgumentInvalid:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: A argument of a switch statement is incorrect");
break;
}
stop();
runError(1001, reason);
switch(reason)
{
case ParsingBuffer:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error while parsing the received buffer");
break;
case ModbusPackageWasSplit:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error while parsing the received buffer: The Modbus package was split", reason);
break;
case VendorIdUnknown:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Vendor ID unknown");
break;
case FuncCodeIncorrect:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Function code is incorrect");
break;
case AddressFailure:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Start address is incorrect");
break;
case ConnectionError:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Connection Error");
break;
case SwitchArgumentInvalid:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: A argument of a switch statement is incorrect");
break;
}
stop();
runError(1001, reason);
}

View file

@ -1,90 +1,90 @@
/*@!Encoding:1252*/
includes
{
#include "include/DeviceInformation.cin"
#include "include/ModbusUdp.cin"
#include "include/ModbusClient.cin"
#include "include/DeviceInformation.cin"
#include "include/ModbusUdp.cin"
#include "include/ModbusClient.cin"
}
variables
{
char[16] gIps[long]; // List of IP addresses. These will be analysed
char gScanFirstIp[16]; // The first IP address that will be scanned
char gScanLastIp[16]; // The first IP address that will be scanned
byte skip255; // Whether the IP address .255 (broadcast in /24 nets) shall be skipped
char[16] gIps[long]; // List of IP addresses. These will be analysed
char gScanFirstIp[16]; // The first IP address that will be scanned
char gScanLastIp[16]; // The first IP address that will be scanned
byte skip255; // Whether the IP address .255 (broadcast in /24 nets) shall be skipped
char fnSysvar[100]; // Filename of Sysvars
char fnDbc[100]; // Filename of DBC
dword ips[50]; // detected IPs. We need this array for enumeration with integers (assoc. arrays cannot be enumerated with integers :( )
char fnSysvar[100]; // Filename of Sysvars
char fnDbc[100]; // Filename of DBC
dword ips[50]; // detected IPs. We need this array for enumeration with integers (assoc. arrays cannot be enumerated with integers :( )
file f; // The file we are writing to
struct device gIpsSorted[long]; // The final array with the devices
dword gScanCurr, gScanLast; // The first and last IP address as dword
word ADi, ADn, ADl; // Some variables for AnalyzeDevices() (event driven, so global)
file f; // The file we are writing to
struct device gIpsSorted[long]; // The final array with the devices
dword gScanCurr, gScanLast; // The first and last IP address as dword
word ADi, ADn, ADl; // Some variables for AnalyzeDevices() (event driven, so global)
byte ggMaxTransmissionCount; // temp var for gMaxTransmissionCount
byte ggMaxTransmissionCount; // temp var for gMaxTransmissionCount
enum eNodeName {WholeIp, LastByte, TwoLastBytes, ThreeLastBytes};
enum eNodeName NodeNameStyle; // The style of the node name
enum eNodeName {WholeIp, LastByte, TwoLastBytes, ThreeLastBytes};
enum eNodeName NodeNameStyle; // The style of the node name
}
on preStart
{
byte i = 0;
// List of IPs of devices goes here
///strncpy(gIps[i++], "192.168.1.100", elCount(gIps));
///strncpy(gIps[i++], "192.168.1.101", elCount(gIps));
byte i = 0;
// List of IPs of devices goes here
///strncpy(gIps[i++], "192.168.1.100", elCount(gIps));
///strncpy(gIps[i++], "192.168.1.101", elCount(gIps));
// Scan a range of IPs for devices (if nothing was set above). Start and Stop go here
strncpy(gScanFirstIp, "192.168.1.2", elCount(gScanFirstIp));
strncpy(gScanLastIp, "192.168.1.255", elCount(gScanLastIp));
// Scan a range of IPs for devices (if nothing was set above). Start and Stop go here
strncpy(gScanFirstIp, "192.168.1.2", elCount(gScanFirstIp));
strncpy(gScanLastIp, "192.168.1.255", elCount(gScanLastIp));
// How the Node name shall be formatted
// LastByte: 192.168.12.34 --> Client_34
// TwoLastBytes: 192.168.12.34 --> Client_12_34
// ThreeLastBytes: 192.168.12.34 --> Client_168_12_34
// WholeIp: 192.168.12.34 --> Client_192_168_12_34
NodeNameStyle = LastByte;
// Whether the IP address .255 (broadcast in /24) shall be skipped
skip255 = 1;
// How the Node name shall be formatted
// LastByte: 192.168.12.34 --> Client_34
// TwoLastBytes: 192.168.12.34 --> Client_12_34
// ThreeLastBytes: 192.168.12.34 --> Client_168_12_34
// WholeIp: 192.168.12.34 --> Client_192_168_12_34
NodeNameStyle = LastByte;
// Whether the IP address .255 (broadcast in /24) shall be skipped
skip255 = 1;
// Paths to the generated files relative to MakeConfig.cfg
strncpy(fnSysvar, "include/SysVars/Modbus.vsysvar", elCount(fnSysvar));
strncpy(fnDbc, "include/DBC/Modbus.dbc", elCount(fnDbc));
// Paths to the generated files relative to MakeConfig.cfg
strncpy(fnSysvar, "include/SysVars/Modbus.vsysvar", elCount(fnSysvar));
strncpy(fnDbc, "include/DBC/Modbus.dbc", elCount(fnDbc));
OutputDebugLevel = Error;
OutputDebugLevel = Error;
}
on start
{
ggMaxTransmissionCount = @sysvar::Config::Modbus::MaxTransmissionCount; // save the value
ggMaxTransmissionCount = @sysvar::Config::Modbus::MaxTransmissionCount; // save the value
DeviceInit(All);
if (gIps.Size() == 0) // if no IP address were specified
DetectDevices(); // scan network for devices (Step1)
else
MakeIpNets(); // else continue with Step2
DeviceInit(All);
if (gIps.Size() == 0) // if no IP address were specified
DetectDevices(); // scan network for devices (Step1)
else
MakeIpNets(); // else continue with Step2
}
/// <PutString>
void PutString(char str[])
{
f.PutString(str, strlen(str));
f.PutString(str, strlen(str));
}
/// <PutString>
void PutString(word d)
{
char str[6];
ltoa(d, str, 10);
f.PutString(str, strlen(str));
char str[6];
ltoa(d, str, 10);
f.PutString(str, strlen(str));
}
/// <PutString>
void PutString(byte d)
{
char str[4];
ltoa(d, str, 10);
f.PutString(str, strlen(str));
char str[4];
ltoa(d, str, 10);
f.PutString(str, strlen(str));
}
// Step 1: Detect active devices and collect IP addresses
@ -92,51 +92,51 @@ void PutString(byte d)
/// <Step1>
void DetectDevices()
{
writeLineEx(0, 1, "Scanning from %s to %s with timeout of %d ms", gScanFirstIp, gScanLastIp, @sysvar::Config::Modbus::RequestTimeout);
writeLineEx(0, 1, "Scanning from %s to %s with timeout of %d ms", gScanFirstIp, gScanLastIp, @sysvar::Config::Modbus::RequestTimeout);
gScanCurr = ipGetAddressAsNumber(gScanFirstIp); // We have to use big endian here
gScanLast = swapDWord(ipGetAddressAsNumber(gScanLastIp)); // But not here :)
gScanCurr = ipGetAddressAsNumber(gScanFirstIp); // We have to use big endian here
gScanLast = swapDWord(ipGetAddressAsNumber(gScanLastIp)); // But not here :)
writeLineEx(0, 0, "%d.%d.%d.%d ", gScanCurr & 0xFF, (gScanCurr >> 8) & 0xFF, (gScanCurr >> 16) & 0xFF, gScanCurr >> 24);
ModbusInit(gScanFirstIp, @sysvar::Config::Modbus::Port, @sysvar::Config::Modbus::RequestTimeout, 1); // Open socket and set variables
ModbusReadBits(0, 1); // Start device detection
writeLineEx(0, 0, "%d.%d.%d.%d ", gScanCurr & 0xFF, (gScanCurr >> 8) & 0xFF, (gScanCurr >> 16) & 0xFF, gScanCurr >> 24);
ModbusInit(gScanFirstIp, @sysvar::Config::Modbus::Port, @sysvar::Config::Modbus::RequestTimeout, 1); // Open socket and set variables
ModbusReadBits(0, 1); // Start device detection
}
// This function will increment the IP address and continue the detection
/// <Step1>
void DetectDevicesNext()
{
gScanCurr = swapDWord(gScanCurr); // Swap to increment
gScanCurr++; // Increment
if ((gScanCurr & 0xFF) == 0xFF) // If .255
{
if (skip255) // If we shall skip .255
gScanCurr++;
writeLineEx(0, 0, "%d.%d.%d.%d ", gScanCurr >> 24, (gScanCurr >> 16) & 0xFF, (gScanCurr >> 8) & 0xFF, gScanCurr & 0xFF);
}
gScanCurr = swapDWord(gScanCurr); // Swap to increment
gScanCurr++; // Increment
if ((gScanCurr & 0xFF) == 0xFF) // If .255
{
if (skip255) // If we shall skip .255
gScanCurr++;
writeLineEx(0, 0, "%d.%d.%d.%d ", gScanCurr >> 24, (gScanCurr >> 16) & 0xFF, (gScanCurr >> 8) & 0xFF, gScanCurr & 0xFF);
}
if (gScanCurr > gScanLast) // If we are beyond the last ip address
{
@sysvar::Config::Modbus::MaxTransmissionCount = ggMaxTransmissionCount; // reset
MakeIpNets(); // Continue with Step 2
return;
}
if (gScanCurr > gScanLast) // If we are beyond the last ip address
{
@sysvar::Config::Modbus::MaxTransmissionCount = ggMaxTransmissionCount; // reset
MakeIpNets(); // Continue with Step 2
return;
}
gScanCurr = swapDWord(gScanCurr); // Swap back
gScanCurr = swapDWord(gScanCurr); // Swap back
writeEx(0, 0, "."); // Write something so the user knows something is happening
gRemoteIP = gScanCurr; // Don't open new socket, it takes too much time. This means we should use UDP or EIL here!
ModbusReadBits(0, 1); // Scan the next device
writeEx(0, 0, "."); // Write something so the user knows something is happening
gRemoteIP = gScanCurr; // Don't open new socket, it takes too much time. This means we should use UDP or EIL here!
ModbusReadBits(0, 1); // Scan the next device
}
/// <Step1>
void OnModbusReadBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap)
{
DetectDevicesNext(); // Timeout, NotSent or Exception! We will go to the next device
DetectDevicesNext(); // Timeout, NotSent or Exception! We will go to the next device
}
/// <Step1>
void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[], struct ModbusReqRead mbreq)
{
ipGetAddressAsString(gScanCurr, gIps[gScanCurr], 16); // store the detected device's IP address
DetectDevicesNext(); // and continue
ipGetAddressAsString(gScanCurr, gIps[gScanCurr], 16); // store the detected device's IP address
DetectDevicesNext(); // and continue
}
@ -145,31 +145,31 @@ void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[]
/// <Step2>
void MakeIpNets()
{
long ipNum;
long ipNum;
if (gIps.Size() == 0) // If no devices were specified and detected
{
writeDbg(MbError, "No devices found!");
stop(); // Don't do anything
return;
}
if (gIps.Size() == 0) // If no devices were specified and detected
{
writeDbg(MbError, "No devices found!");
stop(); // Don't do anything
return;
}
for (long i : gIps) // Iterate all devices
{
ipNum = ipGetAddressAsNumber(gIps[i]); // convert IP to dword
for (long i : gIps) // Iterate all devices
{
ipNum = ipGetAddressAsNumber(gIps[i]); // convert IP to dword
ips[gIpsSorted.size()] = ipNum; // add ip address to normal array
// fill the device structure array:
strncpy(gIpsSorted[ipNum].IP, gIps[i], 16); // set .IP
ltoa((ipNum ) & 0xFF, gIpsSorted[ipNum].Ip1, 10); // set .Ip1
ltoa((ipNum >> 8) & 0xFF, gIpsSorted[ipNum].Ip2, 10); // set .Ip2
ltoa((ipNum >> 16) & 0xFF, gIpsSorted[ipNum].Ip3, 10); // set .Ip3
ltoa((ipNum >> 24) & 0xFF, gIpsSorted[ipNum].Ip4, 10); // set .Ip4
ips[gIpsSorted.size()] = ipNum; // add ip address to normal array
// fill the device structure array:
strncpy(gIpsSorted[ipNum].IP, gIps[i], 16); // set .IP
ltoa((ipNum ) & 0xFF, gIpsSorted[ipNum].Ip1, 10); // set .Ip1
ltoa((ipNum >> 8) & 0xFF, gIpsSorted[ipNum].Ip2, 10); // set .Ip2
ltoa((ipNum >> 16) & 0xFF, gIpsSorted[ipNum].Ip3, 10); // set .Ip3
ltoa((ipNum >> 24) & 0xFF, gIpsSorted[ipNum].Ip4, 10); // set .Ip4
gIps.Remove(i);
}
gIps.Remove(i);
}
AnalyzeDevices(); // Continue with step 3
AnalyzeDevices(); // Continue with step 3
}
@ -177,341 +177,341 @@ void MakeIpNets()
/// <Step3>
void AnalyzeDevices()
{
// Init counters
ADn = 1; // expect 1 response
ADi = 0; // First IP address
ADl = gIpsSorted.Size();
// Init counters
ADn = 1; // expect 1 response
ADi = 0; // First IP address
ADl = gIpsSorted.Size();
writeLineEx(0, 1, "Analyzing %s", gIpsSorted[ips[ADi]].Ip);
writeLineEx(0, 1, "Analyzing %s", gIpsSorted[ips[ADi]].Ip);
if (gRemoteIP != INVALID_IP) // If we already do have a socket
gRemoteIP = ips[ADi]; // use it
else // else create a new one
_ModbusConnectTo(ips[ADi], @sysvar::Config::Modbus::Port);
if (gRemoteIP != INVALID_IP) // If we already do have a socket
gRemoteIP = ips[ADi]; // use it
else // else create a new one
_ModbusConnectTo(ips[ADi], @sysvar::Config::Modbus::Port);
// request something special to get the vendor
// since there is no common register that holds the vendor
// we have to send a request that only one device responds correctly.
// At 0x1000-0x1002 B&R devices return the MAC address, whereas Wago holds the WatchdogTime.
// As the watchdog time only consists of 1 word Wago will return a IllegalDataAddress exception (0x02)
ModbusReadRegisters(0x1000, 3); // Request B&R MAC address
// request something special to get the vendor
// since there is no common register that holds the vendor
// we have to send a request that only one device responds correctly.
// At 0x1000-0x1002 B&R devices return the MAC address, whereas Wago holds the WatchdogTime.
// As the watchdog time only consists of 1 word Wago will return a IllegalDataAddress exception (0x02)
ModbusReadRegisters(0x1000, 3); // Request B&R MAC address
}
/// <Step3>
void AnalyzeDevicesNext()
{
// clean up the string of the previous devices
if (strlen(gIpsSorted[ips[ADi]].DeviceIOs.Modules) > 0) // If we do have some Modules in this string
gIpsSorted[ips[ADi]].DeviceIOs.Modules[strlen(gIpsSorted[ips[ADi]].DeviceIOs.Modules)-1] = 0; // Remove the last comma (set the char to NUL)
// print the result
writeEx(0, 1, ": AOs: %d, AIs: %d, DOs: %d, DIs: %d --> %s", gIpsSorted[ips[ADi]].DeviceIOs.OutputRegisters, gIpsSorted[ips[ADi]].DeviceIOs.InputRegisters, gIpsSorted[ips[ADi]].DeviceIOs.OutputBits, gIpsSorted[ips[ADi]].DeviceIOs.InputBits, gIpsSorted[ips[ADi]].DeviceIOs.Modules);
// clean up the string of the previous devices
if (strlen(gIpsSorted[ips[ADi]].DeviceIOs.Modules) > 0) // If we do have some Modules in this string
gIpsSorted[ips[ADi]].DeviceIOs.Modules[strlen(gIpsSorted[ips[ADi]].DeviceIOs.Modules)-1] = 0; // Remove the last comma (set the char to NUL)
// print the result
writeEx(0, 1, ": AOs: %d, AIs: %d, DOs: %d, DIs: %d --> %s", gIpsSorted[ips[ADi]].DeviceIOs.OutputRegisters, gIpsSorted[ips[ADi]].DeviceIOs.InputRegisters, gIpsSorted[ips[ADi]].DeviceIOs.OutputBits, gIpsSorted[ips[ADi]].DeviceIOs.InputBits, gIpsSorted[ips[ADi]].DeviceIOs.Modules);
if (++ADi >= ADl) // continue with the next device. If we have analyzed all devices
{
MakeFiles(); // go to Step4
return;
}
if (++ADi >= ADl) // continue with the next device. If we have analyzed all devices
{
MakeFiles(); // go to Step4
return;
}
ADn = 1; // expect 1 response again
gRemoteIP = ips[ADi]; // Next IP address
writeLineEx(0, 1, "Analyzing %s", gIpsSorted[ips[ADi]].Ip);
// request something special to get the vendor
ModbusReadRegisters(0x1000, 3); // Request B&R MAC address
ADn = 1; // expect 1 response again
gRemoteIP = ips[ADi]; // Next IP address
writeLineEx(0, 1, "Analyzing %s", gIpsSorted[ips[ADi]].Ip);
// request something special to get the vendor
ModbusReadRegisters(0x1000, 3); // Request B&R MAC address
}
/// <Step3>
void OnModbusReadRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap)
{
struct ModbusReqRead mbreq;
struct ModbusReqRead mbreq;
switch (error)
{
case Exception:
memcpy_n2h(mbreq, gQueueAck[mbap.TxID].Buffer);
switch (error)
{
case Exception:
memcpy_n2h(mbreq, gQueueAck[mbap.TxID].Buffer);
if (mbreq.Address == 0x1000 && ex == IllegalDataAddress) // We requested B&R MAC and it didn't work --> Not B&R --> Wago. Not future proof :(
{
gIpsSorted[ips[ADi]].Vendor = Wago;
// request information
ADn = _DeviceGetInformation(Wago);
return;
}
if (mbreq.Address == 0x1000 && ex == IllegalDataAddress) // We requested B&R MAC and it didn't work --> Not B&R --> Wago. Not future proof :(
{
gIpsSorted[ips[ADi]].Vendor = Wago;
// request information
ADn = _DeviceGetInformation(Wago);
return;
}
writeLineEx(0, 3, "Error while analyzing %s! The device respond with exception code %d! Ignoring...", gIpsSorted[ips[ADi]].IP, ex);
break;
case Timeout:
return; // Timeout is unimportant, it means the request will be resent
case FinalTimeout:
writeLineEx(0, 3, "Error while analyzing %s! The device did not respond! Ignoring...", gIpsSorted[ips[ADi]].IP);
break;
case NotSent:
writeLineEx(0, 3, "Error while analyzing %s! The device was not available! Ignoring...", gIpsSorted[ips[ADi]].IP);
break;
default:
writeLineEx(0, 3, "OnModbusReadRegistersFailed: Unknown error: %d", error);
OnModbusClientPanics(SwitchArgumentInvalid);
return;
}
gQueueAck.Clear(); // Clear all queues
gQueuePending.Clear();
gQueueSent.Clear();
gIpsSorted.Remove(ips[ADi]); // Remove the IP
AnalyzeDevicesNext(); // And go to the next device
writeLineEx(0, 3, "Error while analyzing %s! The device respond with exception code %d! Ignoring...", gIpsSorted[ips[ADi]].IP, ex);
break;
case Timeout:
return; // Timeout is unimportant, it means the request will be resent
case FinalTimeout:
writeLineEx(0, 3, "Error while analyzing %s! The device did not respond! Ignoring...", gIpsSorted[ips[ADi]].IP);
break;
case NotSent:
writeLineEx(0, 3, "Error while analyzing %s! The device was not available! Ignoring...", gIpsSorted[ips[ADi]].IP);
break;
default:
writeLineEx(0, 3, "OnModbusReadRegistersFailed: Unknown error: %d", error);
OnModbusClientPanics(SwitchArgumentInvalid);
return;
}
gQueueAck.Clear(); // Clear all queues
gQueuePending.Clear();
gQueueSent.Clear();
gIpsSorted.Remove(ips[ADi]); // Remove the IP
AnalyzeDevicesNext(); // And go to the next device
}
/// <Step3>
void OnModbusReadRegistersSuccess(struct ModbusResReceiveRegisters mbres, struct ModbusReqRead mbreq)
{
if (mbreq.Address == 0x1000) // We detected a B&R device (from MAC address)
{
gIpsSorted[ips[ADi]].Vendor = BuR;
if (mbreq.Address == 0x1000) // We detected a B&R device (from MAC address)
{
gIpsSorted[ips[ADi]].Vendor = BuR;
// request further information
ADn = _DeviceGetInformation(BuR);
return;
}
// request further information
ADn = _DeviceGetInformation(BuR);
return;
}
// else parse the received data
_DeviceParseRegister(gIpsSorted[ips[ADi]], mbreq.Address, mbres.Data, mbreq.Count);
// else parse the received data
_DeviceParseRegister(gIpsSorted[ips[ADi]], mbreq.Address, mbres.Data, mbreq.Count);
if (--ADn == 0) // If we received all registers
AnalyzeDevicesNext(); // Continue with the next device
if (--ADn == 0) // If we received all registers
AnalyzeDevicesNext(); // Continue with the next device
}
// Step 4: Create the files with the queried data
/// <Step4>
void MakeFiles()
{
GenSysvars();
GenDbc();
stop();
GenSysvars();
GenDbc();
stop();
}
// Generate the SysVars XML
/// <Step4>
void GenSysvars()
{
writeLineEx(0, 1, "GenSysvars() -> %s", fnSysvar);
f.Open(fnSysvar, 0, 0); // rewrite file in ASCII
writeLineEx(0, 1, "GenSysvars() -> %s", fnSysvar);
f.Open(fnSysvar, 0, 0); // rewrite file in ASCII
PutString("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
PutString("<systemvariables version=\"4\">\n");
PutString("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
PutString("<systemvariables version=\"4\">\n");
PutString(" <namespace name=\"Config\" comment=\"\">\n");
PutString(" <namespace name=\"Modbus\" comment=\"\">\n");
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"ms\" name=\"RequestTimeout\" comment=\"The maximum duration for a Modbus-UDP/-TCP request in milliseconds. After timeout a retransmission may be started (see MaxRetransmissionCount). Use `ping` to get the maximum latency to a device, double it and add 2-3 ms for processing.\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString((word)@sysvar::Config::Modbus::RequestTimeout);
PutString("\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"1000\" maxValuePhys=\"1000\" />\n");
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"Port\" comment=\"\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString((word)@sysvar::Config::Modbus::Port);
PutString("\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"65535\" maxValuePhys=\"65535\" />\n");
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"times\" name=\"MaxTransmissionCount\" comment=\"How often a retransmission of a request will be sent until it gets discarded and an error is thrown.\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString((byte)@sysvar::Config::Modbus::MaxTransmissionCount);
PutString("\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"10\" maxValuePhys=\"10\" />\n");
PutString(" </namespace>\n");
PutString(" <namespace name=\"TcpIp\" comment=\"\">\n");
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"AdapterIndex\" comment=\"Index of network interface to use\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"2\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"20\" maxValuePhys=\"20\" />\n");
PutString(" </namespace>\n");
PutString(" </namespace>\n");
PutString(" <namespace name=\"Config\" comment=\"\">\n");
PutString(" <namespace name=\"Modbus\" comment=\"\">\n");
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"ms\" name=\"RequestTimeout\" comment=\"The maximum duration for a Modbus-UDP/-TCP request in milliseconds. After timeout a retransmission may be started (see MaxRetransmissionCount). Use `ping` to get the maximum latency to a device, double it and add 2-3 ms for processing.\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString((word)@sysvar::Config::Modbus::RequestTimeout);
PutString("\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"1000\" maxValuePhys=\"1000\" />\n");
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"Port\" comment=\"\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString((word)@sysvar::Config::Modbus::Port);
PutString("\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"65535\" maxValuePhys=\"65535\" />\n");
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"times\" name=\"MaxTransmissionCount\" comment=\"How often a retransmission of a request will be sent until it gets discarded and an error is thrown.\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString((byte)@sysvar::Config::Modbus::MaxTransmissionCount);
PutString("\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"10\" maxValuePhys=\"10\" />\n");
PutString(" </namespace>\n");
PutString(" <namespace name=\"TcpIp\" comment=\"\">\n");
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"AdapterIndex\" comment=\"Index of network interface to use\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"2\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"20\" maxValuePhys=\"20\" />\n");
PutString(" </namespace>\n");
PutString(" </namespace>\n");
PutString(" <namespace name=\"Modbus\" comment=\"\">\n");
PutString(" <namespace name=\"Modbus\" comment=\"\">\n");
for (long ipN : gIpsSorted)
{
DeviceInit(gIpsSorted[ipN].Vendor);
for (long ipN : gIpsSorted)
{
DeviceInit(gIpsSorted[ipN].Vendor);
PutString(" <namespace name=\"Client_");
switch (NodeNameStyle) // Add the IP bytes depending on the style. Don't break anywhere.
{
case WholeIp:
PutString(gIpsSorted[ipN].Ip1);
PutString("_");
case ThreeLastBytes:
PutString(gIpsSorted[ipN].Ip2);
PutString("_");
case TwoLastBytes:
PutString(gIpsSorted[ipN].Ip3);
PutString("_");
case LastByte:
PutString(gIpsSorted[ipN].Ip4);
break;
default:
writeDbg(MbError, "The NodeNameStyle %d is unknown, please use a value of the enum!", NodeNameStyle);
runError(1001, 0);
return;
}
PutString("\" comment=\"Server with ip address '");
PutString(gIpsSorted[ipN].Ip);
PutString("'\">\n");
PutString(" <namespace name=\"Client_");
switch (NodeNameStyle) // Add the IP bytes depending on the style. Don't break anywhere.
{
case WholeIp:
PutString(gIpsSorted[ipN].Ip1);
PutString("_");
case ThreeLastBytes:
PutString(gIpsSorted[ipN].Ip2);
PutString("_");
case TwoLastBytes:
PutString(gIpsSorted[ipN].Ip3);
PutString("_");
case LastByte:
PutString(gIpsSorted[ipN].Ip4);
break;
default:
writeDbg(MbError, "The NodeNameStyle %d is unknown, please use a value of the enum!", NodeNameStyle);
runError(1001, 0);
return;
}
PutString("\" comment=\"Server with ip address '");
PutString(gIpsSorted[ipN].Ip);
PutString("'\">\n");
// Namespace Config
PutString(" <namespace name=\"Config\" comment=\"Configuration section for this server\">\n");
// IP
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"IP\" comment=\"The IP address of this server\" bitcount=\"8\" isSigned=\"true\" encoding=\"65001\" type=\"string\" startValue=\"");
PutString(gIpsSorted[ipN].Ip);
PutString("\" />\n");
// Intveral
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"ms\" name=\"Interval\" comment=\"The interval with which the device will be queried\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"100\" minValue=\"10\" minValuePhys=\"10\" maxValue=\"10000\" maxValuePhys=\"10000\" />\n");
PutString(" </namespace>\n");
// Namespace Config
PutString(" <namespace name=\"Config\" comment=\"Configuration section for this server\">\n");
// IP
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"IP\" comment=\"The IP address of this server\" bitcount=\"8\" isSigned=\"true\" encoding=\"65001\" type=\"string\" startValue=\"");
PutString(gIpsSorted[ipN].Ip);
PutString("\" />\n");
// Intveral
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"ms\" name=\"Interval\" comment=\"The interval with which the device will be queried\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"100\" minValue=\"10\" minValuePhys=\"10\" maxValue=\"10000\" maxValuePhys=\"10000\" />\n");
PutString(" </namespace>\n");
//Namespace Info
PutString(" <namespace name=\"Info\" comment=\"Some information about the device\">\n");
// Vendor
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"Vendor\" comment=\"The vendor of the device\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString((byte)gIpsSorted[ipN].Vendor);
PutString("\">\n");
PutString(" <valuetable definesMinMax=\"true\">\n");
PutString(" <valuetableentry value=\"2\" description=\"BuR\" />\n");
PutString(" <valuetableentry value=\"23\" description=\"Wago\" />\n");
PutString(" </valuetable>\n");
PutString(" </variable>\n");
// SerialCode
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"SerialCode\" comment=\"The serial code of the server\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(gIpsSorted[ipN].SerialCode);
PutString("\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"10000\" maxValuePhys=\"10000\" />\n");
// DeviceCode
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"DeviceCode\" comment=\"The device code of the server\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(gIpsSorted[ipN].DeviceCode);
PutString("\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"10000\" maxValuePhys=\"10000\" />\n");
// Modules
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"Modules\" comment=\"The type and number of inputs of modules that are connected to the server\" bitcount=\"8\" isSigned=\"true\" encoding=\"65001\" type=\"string\" startValue=\"");
PutString(gIpsSorted[ipN].DeviceIOs.Modules);
PutString("\" />\n");
// InputRegisters
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"InputRegisters\" comment=\"Number of input registers\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(gIpsSorted[ipN].DeviceIOs.InputRegisters);
PutString("\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"");
PutString((word)thisDev.MaxRegisterCount);
PutString("\" maxValuePhys=\"");
PutString((word)thisDev.MaxRegisterCount);
PutString("\" />\n");
// InputBits
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"InputBits\" comment=\"Number of input bits\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(gIpsSorted[ipN].DeviceIOs.InputBits);
PutString("\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"");
PutString((word)thisDev.MaxBitCount);
PutString("\" maxValuePhys=\"");
PutString((word)thisDev.MaxBitCount);
PutString("\" />\n");
// OutputRegisters
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"OutputRegisters\" comment=\"Number of output registers\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(gIpsSorted[ipN].DeviceIOs.OutputRegisters);
PutString("\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"");
PutString((word)thisDev.MaxRegisterCount);
PutString("\" maxValuePhys=\"");
PutString((word)thisDev.MaxRegisterCount);
PutString("\" />\n");
// OutputBits
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"OutputBits\" comment=\"Number of output bits\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(gIpsSorted[ipN].DeviceIOs.OutputBits);
PutString("\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"");
PutString((word)thisDev.MaxBitCount);
PutString("\" maxValuePhys=\"");
PutString((word)thisDev.MaxBitCount);
PutString("\" />\n");
PutString(" </namespace>\n");
//Namespace Info
PutString(" <namespace name=\"Info\" comment=\"Some information about the device\">\n");
// Vendor
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"Vendor\" comment=\"The vendor of the device\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString((byte)gIpsSorted[ipN].Vendor);
PutString("\">\n");
PutString(" <valuetable definesMinMax=\"true\">\n");
PutString(" <valuetableentry value=\"2\" description=\"BuR\" />\n");
PutString(" <valuetableentry value=\"23\" description=\"Wago\" />\n");
PutString(" </valuetable>\n");
PutString(" </variable>\n");
// SerialCode
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"SerialCode\" comment=\"The serial code of the server\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(gIpsSorted[ipN].SerialCode);
PutString("\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"10000\" maxValuePhys=\"10000\" />\n");
// DeviceCode
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"DeviceCode\" comment=\"The device code of the server\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(gIpsSorted[ipN].DeviceCode);
PutString("\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"10000\" maxValuePhys=\"10000\" />\n");
// Modules
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"Modules\" comment=\"The type and number of inputs of modules that are connected to the server\" bitcount=\"8\" isSigned=\"true\" encoding=\"65001\" type=\"string\" startValue=\"");
PutString(gIpsSorted[ipN].DeviceIOs.Modules);
PutString("\" />\n");
// InputRegisters
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"InputRegisters\" comment=\"Number of input registers\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(gIpsSorted[ipN].DeviceIOs.InputRegisters);
PutString("\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"");
PutString((word)thisDev.MaxRegisterCount);
PutString("\" maxValuePhys=\"");
PutString((word)thisDev.MaxRegisterCount);
PutString("\" />\n");
// InputBits
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"InputBits\" comment=\"Number of input bits\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(gIpsSorted[ipN].DeviceIOs.InputBits);
PutString("\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"");
PutString((word)thisDev.MaxBitCount);
PutString("\" maxValuePhys=\"");
PutString((word)thisDev.MaxBitCount);
PutString("\" />\n");
// OutputRegisters
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"OutputRegisters\" comment=\"Number of output registers\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(gIpsSorted[ipN].DeviceIOs.OutputRegisters);
PutString("\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"");
PutString((word)thisDev.MaxRegisterCount);
PutString("\" maxValuePhys=\"");
PutString((word)thisDev.MaxRegisterCount);
PutString("\" />\n");
// OutputBits
PutString(" <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"OutputBits\" comment=\"Number of output bits\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(gIpsSorted[ipN].DeviceIOs.OutputBits);
PutString("\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"");
PutString((word)thisDev.MaxBitCount);
PutString("\" maxValuePhys=\"");
PutString((word)thisDev.MaxBitCount);
PutString("\" />\n");
PutString(" </namespace>\n");
// Namespace Data
PutString(" <namespace name=\"Data\" comment=\"The actual process image\">\n");
// InputRegisters
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"InputRegisters\" comment=\"The values of the input registers\" bitcount=\"17\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(gIpsSorted[ipN].DeviceIOs.InputRegisters);
PutString("\" />\n");
// InputBits
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"InputBits\" comment=\"The state of the input bits\" bitcount=\"2\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(gIpsSorted[ipN].DeviceIOs.InputBits);
PutString("\" />\n");
// OutputRegisters
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"OutputRegisters\" comment=\"The values of the output registers. Write here and the values will be sent to the device\" bitcount=\"17\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(gIpsSorted[ipN].DeviceIOs.OutputRegisters);
PutString("\" />\n");
// OutputBits
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"OutputBits\" comment=\"The state of the output bits. Write here and the values will be sent to the device\" bitcount=\"2\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(gIpsSorted[ipN].DeviceIOs.OutputBits);
PutString("\" />\n");
// Namespace Data
PutString(" <namespace name=\"Data\" comment=\"The actual process image\">\n");
// InputRegisters
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"InputRegisters\" comment=\"The values of the input registers\" bitcount=\"17\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(gIpsSorted[ipN].DeviceIOs.InputRegisters);
PutString("\" />\n");
// InputBits
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"InputBits\" comment=\"The state of the input bits\" bitcount=\"2\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(gIpsSorted[ipN].DeviceIOs.InputBits);
PutString("\" />\n");
// OutputRegisters
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"OutputRegisters\" comment=\"The values of the output registers. Write here and the values will be sent to the device\" bitcount=\"17\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(gIpsSorted[ipN].DeviceIOs.OutputRegisters);
PutString("\" />\n");
// OutputBits
PutString(" <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"OutputBits\" comment=\"The state of the output bits. Write here and the values will be sent to the device\" bitcount=\"2\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(gIpsSorted[ipN].DeviceIOs.OutputBits);
PutString("\" />\n");
PutString(" </namespace>\n");
PutString(" </namespace>\n");
}
PutString(" </namespace>\n");
PutString(" </namespace>\n");
}
PutString(" </namespace>\n");
PutString("</systemvariables>\n");
f.Close();
PutString(" </namespace>\n");
PutString("</systemvariables>\n");
f.Close();
}
// Generate the Database
/// <Step4>
void GenDbc()
{
writeLineEx(0, 1, "GenDbc() -> %s", fnDbc);
f.Open(fnDbc, 0, 0); // rewrite file in ASCII
writeLineEx(0, 1, "GenDbc() -> %s", fnDbc);
f.Open(fnDbc, 0, 0); // rewrite file in ASCII
PutString("VERSION \"\"\n\n\n");
PutString("NS_ :\n");
PutString(" NS_DESC_\n");
PutString(" CM_\n");
PutString(" BA_DEF_\n");
PutString(" BA_\n");
PutString(" VAL_\n");
PutString(" CAT_DEF_\n");
PutString(" CAT_\n");
PutString(" FILTER\n");
PutString(" BA_DEF_DEF_\n");
PutString(" EV_DATA_\n");
PutString(" ENVVAR_DATA_\n");
PutString(" SGTYPE_\n");
PutString(" SGTYPE_VAL_\n");
PutString(" BA_DEF_SGTYPE_\n");
PutString(" BA_SGTYPE_\n");
PutString(" SIG_TYPE_REF_\n");
PutString(" VAL_TABLE_\n");
PutString(" SIG_GROUP_\n");
PutString(" SIG_VALTYPE_\n");
PutString(" SIGTYPE_VALTYPE_\n");
PutString(" BO_TX_BU_\n");
PutString(" BA_DEF_REL_\n");
PutString(" BA_REL_\n");
PutString(" BA_DEF_DEF_REL_\n");
PutString(" BU_SG_REL_\n");
PutString(" BU_EV_REL_\n");
PutString(" BU_BO_REL_\n");
PutString(" SG_MUL_VAL_\n");
PutString("\n");
PutString("BS_:\n");
PutString("\nBU_:");
PutString("VERSION \"\"\n\n\n");
PutString("NS_ :\n");
PutString(" NS_DESC_\n");
PutString(" CM_\n");
PutString(" BA_DEF_\n");
PutString(" BA_\n");
PutString(" VAL_\n");
PutString(" CAT_DEF_\n");
PutString(" CAT_\n");
PutString(" FILTER\n");
PutString(" BA_DEF_DEF_\n");
PutString(" EV_DATA_\n");
PutString(" ENVVAR_DATA_\n");
PutString(" SGTYPE_\n");
PutString(" SGTYPE_VAL_\n");
PutString(" BA_DEF_SGTYPE_\n");
PutString(" BA_SGTYPE_\n");
PutString(" SIG_TYPE_REF_\n");
PutString(" VAL_TABLE_\n");
PutString(" SIG_GROUP_\n");
PutString(" SIG_VALTYPE_\n");
PutString(" SIGTYPE_VALTYPE_\n");
PutString(" BO_TX_BU_\n");
PutString(" BA_DEF_REL_\n");
PutString(" BA_REL_\n");
PutString(" BA_DEF_DEF_REL_\n");
PutString(" BU_SG_REL_\n");
PutString(" BU_EV_REL_\n");
PutString(" BU_BO_REL_\n");
PutString(" SG_MUL_VAL_\n");
PutString("\n");
PutString("BS_:\n");
PutString("\nBU_:");
for (long ipN : gIpsSorted)
{
PutString(" Client_");
switch (NodeNameStyle) // Add the IP bytes depending on the style. Don't break anywhere.
{
case WholeIp:
PutString(gIpsSorted[ipN].Ip1);
PutString("_");
case ThreeLastBytes:
PutString(gIpsSorted[ipN].Ip2);
PutString("_");
case TwoLastBytes:
PutString(gIpsSorted[ipN].Ip3);
PutString("_");
case LastByte:
PutString(gIpsSorted[ipN].Ip4);
break;
default:
writeDbg(MbError, "The NodeNameStyle %d is unknown, please use a value of the enum!", NodeNameStyle);
runError(1001, 0);
return;
}
}
PutString("\n\n\n\n");
PutString("BA_DEF_ BU_ \"NodeLayerModules\" STRING ;\n");
PutString("BA_DEF_ \"DBName\" STRING ;\n");
PutString("BA_DEF_ \"BusType\" STRING ;\n");
PutString("BA_DEF_DEF_ \"NodeLayerModules\" \"Ethernet_IL.DLL\";\n");
PutString("BA_DEF_DEF_ \"DBName\" \"\";\n");
PutString("BA_DEF_DEF_ \"BusType\" \"Ethernet\";\n");
PutString("BA_ \"BusType\" \"Ethernet\";\n");
PutString("BA_ \"DBName\" \"Modbus\";\n");
for (long ipN : gIpsSorted)
{
PutString(" Client_");
switch (NodeNameStyle) // Add the IP bytes depending on the style. Don't break anywhere.
{
case WholeIp:
PutString(gIpsSorted[ipN].Ip1);
PutString("_");
case ThreeLastBytes:
PutString(gIpsSorted[ipN].Ip2);
PutString("_");
case TwoLastBytes:
PutString(gIpsSorted[ipN].Ip3);
PutString("_");
case LastByte:
PutString(gIpsSorted[ipN].Ip4);
break;
default:
writeDbg(MbError, "The NodeNameStyle %d is unknown, please use a value of the enum!", NodeNameStyle);
runError(1001, 0);
return;
}
}
PutString("\n\n\n\n");
PutString("BA_DEF_ BU_ \"NodeLayerModules\" STRING ;\n");
PutString("BA_DEF_ \"DBName\" STRING ;\n");
PutString("BA_DEF_ \"BusType\" STRING ;\n");
PutString("BA_DEF_DEF_ \"NodeLayerModules\" \"Ethernet_IL.DLL\";\n");
PutString("BA_DEF_DEF_ \"DBName\" \"\";\n");
PutString("BA_DEF_DEF_ \"BusType\" \"Ethernet\";\n");
PutString("BA_ \"BusType\" \"Ethernet\";\n");
PutString("BA_ \"DBName\" \"Modbus\";\n");
f.Close();
f.Close();
}
@ -525,16 +525,16 @@ void GenDbc()
/// <zzzModbus>
void OnModbusClientPanics(enum FatalErrors reason)
{
writeLineEx(0, 4, "<%NODE_NAME%> FATAL! %d", reason);
/* switch(reason)
{
case ParsingBuffer:
case ModbusPackageWasSplit:
case DeviceCodeUnknown:
case VendorIdUnknown:
case ConnectionError:
break;
}
writeLineEx(0, 4, "<%NODE_NAME%> FATAL! %d", reason);
/* switch(reason)
{
case ParsingBuffer:
case ModbusPackageWasSplit:
case DeviceCodeUnknown:
case VendorIdUnknown:
case ConnectionError:
break;
}
*/
}
/// <zzzModbus>

View file

@ -6,106 +6,106 @@
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
#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
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)
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)
}
// Connect to Modbus server, read the status of output registers and bits and start the cyclic timer
on start
{
char ip[16];
sysGetVariableString("Modbus::%NODE_NAME%::Config", "IP", ip, elCount(ip)); // Get IP address of device from sysvars config
char ip[16];
sysGetVariableString("Modbus::%NODE_NAME%::Config", "IP", ip, elCount(ip)); // Get IP address of device from sysvars config
DeviceInit(@sysvar::Modbus::%NODE_NAME%::Info::Vendor); // Set all device specific parameters (Wago / B&R)
DeviceInit(@sysvar::Modbus::%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
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
if (gSocketState < CONNECTING) // We are not connecting and not connected
return;
if (gSocketState < CONNECTING) // We are not connecting and not connected
return;
ModbusReadOutBits(thisDev.Addr.Read.OutputBits, @sysvar::Modbus::%NODE_NAME%::Info::OutputBits); // Read the start status of the output bits
ModbusReadOutRegisters(thisDev.Addr.Read.OutputRegisters, @sysvar::Modbus::%NODE_NAME%::Info::OutputRegisters); // Read the start status of the output registers
ModbusReadOutBits(thisDev.Addr.Read.OutputBits, @sysvar::Modbus::%NODE_NAME%::Info::OutputBits); // Read the start status of the output bits
ModbusReadOutRegisters(thisDev.Addr.Read.OutputRegisters, @sysvar::Modbus::%NODE_NAME%::Info::OutputRegisters); // Read the start status of the output registers
if (@sysvar::Modbus::%NODE_NAME%::Config::Interval > 0) // Start the polling timer
setTimerCyclic(gtRead, 1, @sysvar::Modbus::%NODE_NAME%::Config::Interval);
if (@sysvar::Modbus::%NODE_NAME%::Config::Interval > 0) // Start the polling timer
setTimerCyclic(gtRead, 1, @sysvar::Modbus::%NODE_NAME%::Config::Interval);
}
// Stop all transactions and close connection
on preStop
{
ModbusEnd();
ModbusEnd();
}
// Modbus events ----------------------------------------------------------------------
/// All these events will be called by functions out of ModbusClientCommon.cin
/// 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)
/// 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;
word i;
switch (error)
{
case Exception:
case Timeout:
case NotSent:
break;
case FinalTimeout:
sysBeginVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputBits");
for (i = 0; i < @sysvar::Modbus::%NODE_NAME%::Info::InputBits; i++)
@sysvar::Modbus::%NODE_NAME%::Data::InputBits[i] = -1;
sysEndVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputBits");
break;
default:
writeDbg(MbError, "OnModbusReadBitsFailed: Unkown error: %d", error);
OnModbusClientPanics(SwitchArgumentInvalid);
return;
}
switch (error)
{
case Exception:
case Timeout:
case NotSent:
break;
case FinalTimeout:
sysBeginVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputBits");
for (i = 0; i < @sysvar::Modbus::%NODE_NAME%::Info::InputBits; i++)
@sysvar::Modbus::%NODE_NAME%::Data::InputBits[i] = -1;
sysEndVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputBits");
break;
default:
writeDbg(MbError, "OnModbusReadBitsFailed: Unkown error: %d", error);
OnModbusClientPanics(SwitchArgumentInvalid);
return;
}
}
// 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;
byte i;
switch (error)
{
case Exception:
case Timeout:
case NotSent:
break;
case FinalTimeout:
sysBeginVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputRegisters");
for (i = 0; i < @sysvar::Modbus::%NODE_NAME%::Info::InputRegisters; i++)
@sysvar::Modbus::%NODE_NAME%::Data::InputRegisters[i] = -1;
sysEndVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputRegisters");
break;
default:
writeDbg(MbError, "OnModbusReadBitsFailed: Unkown error: %d", error);
OnModbusClientPanics(SwitchArgumentInvalid);
return;
}
switch (error)
{
case Exception:
case Timeout:
case NotSent:
break;
case FinalTimeout:
sysBeginVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputRegisters");
for (i = 0; i < @sysvar::Modbus::%NODE_NAME%::Info::InputRegisters; i++)
@sysvar::Modbus::%NODE_NAME%::Data::InputRegisters[i] = -1;
sysEndVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputRegisters");
break;
default:
writeDbg(MbError, "OnModbusReadBitsFailed: Unkown error: %d", error);
OnModbusClientPanics(SwitchArgumentInvalid);
return;
}
}
// This method gets called when an error occured while trying to set a bit (ModbusWriteBit())
@ -122,78 +122,78 @@ void OnModbusWriteBitsFailed(enum ModbusRequestError error, enum ModbusException
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
/// 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;
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 ReadBitsOut: // Read output bits
sysBeginVariableStructUpdate("Modbus::%NODE_NAME%::Data", "OutputBits");
switch (mbres.Header.FuncCode) // We assume that we separate between 0x01 and 0x02 even though the address space may be the same
{
case ReadBitsOut: // Read output bits
sysBeginVariableStructUpdate("Modbus::%NODE_NAME%::Data", "OutputBits");
offset = mbreq.Address - thisDev.Addr.Read.OutputBits; // Get the offset to the base output bit address
for (i = 0; i < mbreq.Count; i++)
@sysvar::Modbus::%NODE_NAME%::Data::OutputBits[i + offset] = bitStatus[i];
offset = mbreq.Address - thisDev.Addr.Read.OutputBits; // Get the offset to the base output bit address
for (i = 0; i < mbreq.Count; i++)
@sysvar::Modbus::%NODE_NAME%::Data::OutputBits[i + offset] = bitStatus[i];
sysEndVariableStructUpdate("Modbus::%NODE_NAME%::Data", "OutputBits");
break;
sysEndVariableStructUpdate("Modbus::%NODE_NAME%::Data", "OutputBits");
break;
case ReadBitsIn: // Read input bits
sysBeginVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputBits");
case ReadBitsIn: // Read input bits
sysBeginVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputBits");
offset = mbreq.Address - thisDev.Addr.Read.InputBits; // Get the offset to the base input bit address
for (i = 0; i < mbreq.Count; i++)
@sysvar::Modbus::%NODE_NAME%::Data::InputBits[i + offset] = bitStatus[i];
offset = mbreq.Address - thisDev.Addr.Read.InputBits; // Get the offset to the base input bit address
for (i = 0; i < mbreq.Count; i++)
@sysvar::Modbus::%NODE_NAME%::Data::InputBits[i + offset] = bitStatus[i];
sysEndVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputBits");
break;
sysEndVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputBits");
break;
default:
writeDbg(MbError, "OnModbusReadBitsSuccess: Unexpected function code: 0x%02X", mbreq.Header.FuncCode);
OnModbusClientPanics(FuncCodeIncorrect);
return;
}
default:
writeDbg(MbError, "OnModbusReadBitsSuccess: Unexpected function code: 0x%02X", mbreq.Header.FuncCode);
OnModbusClientPanics(FuncCodeIncorrect);
return;
}
}
// 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;
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 ReadRegistersOut: // Read output registers
sysBeginVariableStructUpdate("Modbus::%NODE_NAME%::Data", "OutputRegisters");
switch (mbres.Header.FuncCode) // We assume that we separate between 0x03 and 0x04 even though the address space may be the same
{
case ReadRegistersOut: // Read output registers
sysBeginVariableStructUpdate("Modbus::%NODE_NAME%::Data", "OutputRegisters");
offset = mbreq.Address - thisDev.Addr.Read.OutputRegisters; // Get the offset to the base output register address
for (i = 0; i < mbreq.Count; i++)
@sysvar::Modbus::%NODE_NAME%::Data::OutputRegisters[i + offset] = mbres.Data[i];
offset = mbreq.Address - thisDev.Addr.Read.OutputRegisters; // Get the offset to the base output register address
for (i = 0; i < mbreq.Count; i++)
@sysvar::Modbus::%NODE_NAME%::Data::OutputRegisters[i + offset] = mbres.Data[i];
sysEndVariableStructUpdate("Modbus::%NODE_NAME%::Data", "OutputRegisters");
break;
sysEndVariableStructUpdate("Modbus::%NODE_NAME%::Data", "OutputRegisters");
break;
case ReadRegistersIn: // Read input registers
sysBeginVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputRegisters");
case ReadRegistersIn: // Read input registers
sysBeginVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputRegisters");
offset = mbreq.Address - thisDev.Addr.Read.InputRegisters; // Get the offset to the base input bit address
for (i = 0; i < mbreq.Count; i++)
@sysvar::Modbus::%NODE_NAME%::Data::InputRegisters[i + offset] = mbres.Data[i];
offset = mbreq.Address - thisDev.Addr.Read.InputRegisters; // Get the offset to the base input bit address
for (i = 0; i < mbreq.Count; i++)
@sysvar::Modbus::%NODE_NAME%::Data::InputRegisters[i + offset] = mbres.Data[i];
sysEndVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputRegisters");
break;
sysEndVariableStructUpdate("Modbus::%NODE_NAME%::Data", "InputRegisters");
break;
default:
writeDbg(MbError, "OnModbusReadBitsSuccess: Unexpected function code: 0x%02X", mbreq.Header.FuncCode);
OnModbusClientPanics(FuncCodeIncorrect);
return;
}
default:
writeDbg(MbError, "OnModbusReadBitsSuccess: Unexpected function code: 0x%02X", mbreq.Header.FuncCode);
OnModbusClientPanics(FuncCodeIncorrect);
return;
}
}
// This method gets called when a bit was set successfully.
@ -212,79 +212,79 @@ void OnModbusWriteMasksSuccess(struct ModbusResConfirmMasks mbres){}
// It will pass as argument what happened. Please see the log (increase debug level in preStart) for more details.
void OnModbusClientPanics(enum FatalErrors reason)
{
switch(reason)
{
case ParsingBuffer:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error while parsing the received buffer");
runError(1001, reason);
break;
case ModbusPackageWasSplit:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error while parsing the received buffer: The Modbus package was split", reason);
runError(1001, reason);
break;
case VendorIdUnknown:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Vendor ID unknown");
runError(1001, reason);
break;
case FuncCodeIncorrect:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Function code is incorrect");
runError(1001, reason);
break;
case AddressFailure:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Start address is incorrect");
runError(1001, reason);
break;
case ConnectionError:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Connection Error");
gtRead.Cancel();
break;
case SwitchArgumentInvalid:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: A argument of a switch statement is incorrect");
runError(1001, reason);
break;
}
stop();
switch(reason)
{
case ParsingBuffer:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error while parsing the received buffer");
runError(1001, reason);
break;
case ModbusPackageWasSplit:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error while parsing the received buffer: The Modbus package was split", reason);
runError(1001, reason);
break;
case VendorIdUnknown:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Vendor ID unknown");
runError(1001, reason);
break;
case FuncCodeIncorrect:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Function code is incorrect");
runError(1001, reason);
break;
case AddressFailure:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: Start address is incorrect");
runError(1001, reason);
break;
case ConnectionError:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Connection Error");
gtRead.Cancel();
break;
case SwitchArgumentInvalid:
writeLineEx(0, 4, "<%NODE_NAME%> Fatal Error: A argument of a switch statement is incorrect");
runError(1001, reason);
break;
}
stop();
}
// -------------------------------------------------------------------------
// The timer will continuously poll the input registers and intput bits
on timer gtRead
{
ModbusReadRegisters(thisDev.Addr.Read.InputRegisters, @sysvar::Modbus::%NODE_NAME%::Info::InputRegisters);
ModbusReadBits(thisDev.Addr.Read.InputBits, @sysvar::Modbus::%NODE_NAME%::Info::InputBits);
ModbusReadRegisters(thisDev.Addr.Read.InputRegisters, @sysvar::Modbus::%NODE_NAME%::Info::InputRegisters);
ModbusReadBits(thisDev.Addr.Read.InputBits, @sysvar::Modbus::%NODE_NAME%::Info::InputBits);
}
// If Data::OutputBits is changed we will send this update to the device
on sysvar Modbus::%NODE_NAME%::Data::OutputBits
{
word count, i;
byte bitStatus[1968];
word count, i;
byte bitStatus[1968];
count = @sysvar::Modbus::%NODE_NAME%::Info::OutputBits;
count = @sysvar::Modbus::%NODE_NAME%::Info::OutputBits;
for (i = 0; i < count; i++) // Copy the data from SysVars to byte[]
bitStatus[i] = @sysvar::Modbus::%NODE_NAME%::Data::OutputBits[i];
for (i = 0; i < count; i++) // Copy the data from SysVars to byte[]
bitStatus[i] = @sysvar::Modbus::%NODE_NAME%::Data::OutputBits[i];
ModbusWriteBitsB(0, count, bitStatus); // Send update command
ModbusWriteBitsB(0, count, bitStatus); // Send update command
}
// If Data::OutputRergisters is changed we will send this update to the device
on sysvar Modbus::%NODE_NAME%::Data::OutputRegisters
{
word count, i;
word regValues[123];
word count, i;
word regValues[123];
count = @sysvar::Modbus::%NODE_NAME%::Info::OutputRegisters;
count = @sysvar::Modbus::%NODE_NAME%::Info::OutputRegisters;
for (i = 0; i < count; i++) // Copy the data from SysVars to word[]
regValues[i] = @sysvar::Modbus::%NODE_NAME%::Data::OutputRegisters[i];
for (i = 0; i < count; i++) // Copy the data from SysVars to word[]
regValues[i] = @sysvar::Modbus::%NODE_NAME%::Data::OutputRegisters[i];
ModbusWriteRegisters(0, count, regValues); // Send update command
ModbusWriteRegisters(0, count, regValues); // Send update command
}
// Config::Interval is changed we will update the timer gtRead accordingly
on sysvar Modbus::%NODE_NAME%::Config::Interval
{
if (@this <= 0)
gtRead.Cancel();
else
setTimerCyclic(gtRead, @this);
if (@this <= 0)
gtRead.Cancel();
else
setTimerCyclic(gtRead, @this);
}

File diff suppressed because it is too large Load diff

View file

@ -8,31 +8,31 @@
variables
{
enum DebugLvl {
Debug = 0x00,
AlgoDebug = 0x02,
ConnDebug = 0x04,
MbDebug = 0x07,
enum DebugLvl {
Debug = 0x00,
AlgoDebug = 0x02,
ConnDebug = 0x04,
MbDebug = 0x07,
Info = 0x10,
AlgoInfo = 0x12,
ConnInfo = 0x14,
MbInfo = 0x17,
Info = 0x10,
AlgoInfo = 0x12,
ConnInfo = 0x14,
MbInfo = 0x17,
Warning = 0x20,
AlgoWarning = 0x22,
ConnWarning = 0x24,
MbWarning = 0x27,
Warning = 0x20,
AlgoWarning = 0x22,
ConnWarning = 0x24,
MbWarning = 0x27,
Error = 0x30,
AlgoError = 0x32,
ConnError = 0x34,
MbError = 0x37,
Error = 0x30,
AlgoError = 0x32,
ConnError = 0x34,
MbError = 0x37,
Mute = 0xFF
Mute = 0xFF
};
char DebugLvlStr[4][8] = {"Debug", "Info", "Warning", "Error"};
enum DebugLvl OutputDebugLevel = Debug;
char DebugLvlStr[4][8] = {"Debug", "Info", "Warning", "Error"};
enum DebugLvl OutputDebugLevel = Debug;
}
// This method creates a format string in 'msg' which can then be passed to writeLineEx().
@ -40,221 +40,221 @@ variables
/// <writeDbg>
void writeDbgFormat(byte lVl, char msg[], char format[])
{
strncpy(msg, "<%NODE_NAME%> ", elCount(msg));
strncat(msg, DebugLvlStr[lVl], elCount(msg));
strncat(msg, ": ", elCount(msg));
strncat(msg, format, elCount(msg)-strlen(msg));
strncpy(msg, "<%NODE_NAME%> ", elCount(msg));
strncat(msg, DebugLvlStr[lVl], elCount(msg));
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
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[])
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg);
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
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], char string[])
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, 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
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], char string1[], char string2[])
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string1, 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
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], char string1[], char string2[], long num1, long num2)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string1, string2, num1, num2);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string1, string2, num1, num2);
}
// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], char string1[], char string2[], long num1)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string1, string2, num1);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string1, string2, num1);
}
// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num, char string[])
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num, 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
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], char string[], long num)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string, 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
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], char string[], long num, float fl)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string, num, fl);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string, num, fl);
}
// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num1)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, 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
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, char string[])
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, string);
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
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, 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
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, 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
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3, char str[])
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, num3, str);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, num3, str);
}
// This method prints the specified arguments to CAPL/Write Window if the debug level is high enough
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3, long num4)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, num3, num4);
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
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3, long num4, long num5, long num6)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, num3, num4, num5, num6);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, num3, num4, num5, num6);
}
@ -263,84 +263,84 @@ void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3,
// You have to allocate 60 bytes for 'result'
void bin_to_strhex(byte bin[], char result[])
{
char hex_str[17] = "0123456789ABCDEF";
word i;
word binsz;
binsz = elCount(bin);
if (binsz > 20)
binsz = 20;
char hex_str[17] = "0123456789ABCDEF";
word i;
word binsz;
binsz = elCount(bin);
if (binsz > 20)
binsz = 20;
for (i = 0; i < binsz; i++)
{
result[i * 3 + 0] = hex_str[bin[i] >> 4 ];
result[i * 3 + 1] = hex_str[bin[i] & 0x0F];
result[i * 3 + 2] = ' ';
}
for (i = 0; i < binsz; i++)
{
result[i * 3 + 0] = hex_str[bin[i] >> 4 ];
result[i * 3 + 1] = hex_str[bin[i] & 0x0F];
result[i * 3 + 2] = ' ';
}
if (elCount(bin) > 20) // trailing "..."
{
result[56] = '.';
result[57] = '.';
result[58] = '.';
}
result[binsz * 3 - 1] = 0;
if (elCount(bin) > 20) // trailing "..."
{
result[56] = '.';
result[57] = '.';
result[58] = '.';
}
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";
word i;
word binsz;
binsz = elCount(bin);
if (binsz > 20)
binsz = 20;
char hex_str[17] = "0123456789ABCDEF";
word i;
word binsz;
binsz = elCount(bin);
if (binsz > 20)
binsz = 20;
for (i = 0; i < binsz; i++)
{
result[i * 2 + 0] = hex_str[bin[i] & 0x0F];
result[i * 2 + 1] = ' ';
}
for (i = 0; i < binsz; i++)
{
result[i * 2 + 0] = hex_str[bin[i] & 0x0F];
result[i * 2 + 1] = ' ';
}
if (elCount(bin) > 20) // trailing "..."
{
result[36] = '.';
result[37] = '.';
result[38] = '.';
}
result[binsz * 2 - 1] = 0;
if (elCount(bin) > 20) // trailing "..."
{
result[36] = '.';
result[37] = '.';
result[38] = '.';
}
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";
word i;
word binsz;
byte offset;
binsz = elCount(bin);
if (binsz > 20)
binsz = 20;
char hex_str[17] = "0123456789ABCDEF";
word i;
word binsz;
byte offset;
binsz = elCount(bin);
if (binsz > 20)
binsz = 20;
for (i = 0; i < binsz; i++)
{
result[i * 5 + 0] = hex_str[(bin[i] >> 12) & 0x0F];
result[i * 5 + 1] = hex_str[(bin[i] >> 8) & 0x0F];
result[i * 5 + 2] = hex_str[(bin[i] >> 4) & 0x0F];
result[i * 5 + 3] = hex_str[(bin[i] ) & 0x0F];
result[i * 5 + 4] = ' ';
}
for (i = 0; i < binsz; i++)
{
result[i * 5 + 0] = hex_str[(bin[i] >> 12) & 0x0F];
result[i * 5 + 1] = hex_str[(bin[i] >> 8) & 0x0F];
result[i * 5 + 2] = hex_str[(bin[i] >> 4) & 0x0F];
result[i * 5 + 3] = hex_str[(bin[i] ) & 0x0F];
result[i * 5 + 4] = ' ';
}
if (elCount(bin) > 20) // trailing "..."
{
result[96] = '.';
result[97] = '.';
result[98] = '.';
}
result[(byte)(binsz * 2.5) - 1] = 0;
if (elCount(bin) > 20) // trailing "..."
{
result[96] = '.';
result[97] = '.';
result[98] = '.';
}
result[(byte)(binsz * 2.5) - 1] = 0;
}

View file

@ -13,55 +13,55 @@
/// This file is used at two positions: 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
{
// This structure is used by the Modbus Client. It describes the device
struct
{
struct // Start addresses
{
struct // Start addresses Read
{
word OutputBits;
word OutputRegisters;
word InputBits;
word InputRegisters;
} Read;
struct // Start addresses Write
{
word OutputBits;
word OutputRegisters;
} Write;
} Addr;
word MaxBitCount; // Max number of bits
word MaxRegisterCount; // Max number of registers
byte ReceiveWindow;
} thisDev;
// This structure is used by the Modbus Client. It describes the device
struct
{
struct // Start addresses
{
struct // Start addresses Read
{
word OutputBits;
word OutputRegisters;
word InputBits;
word InputRegisters;
} Read;
struct // Start addresses Write
{
word OutputBits;
word OutputRegisters;
} Write;
} Addr;
word MaxBitCount; // Max number of bits
word MaxRegisterCount; // Max number of registers
byte ReceiveWindow;
} thisDev;
enum Vendor // The Vendor enum. All Vendors have to listed here and all listed vendors have to be implemented in this file
{
All = 0xFF,
Wago = 23, // Wago
BuR = 2 // B&R
};
struct deviceIOs // A structure which contains quantity information about connected IO. Used in MakeConfig. These info will be written into the SysVars
{
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. Used in MakeConfig.
{
char Ip[16]; // String: The IP address
char Ip4[4]; // String: The last byte of the IP address.
char Ip3[4]; // String: The third byte of the IP.
char Ip2[4]; // String: The second byte of the IP.
char Ip1[4]; // String: Thefirst byte of the IP.
enum Vendor Vendor; // The Vendor (Wago / B&R)
word SerialCode; // Serial Code
word DeviceCode; // Device Code
struct deviceIOs DeviceIOs; // A structure with more information about IOs
};
enum Vendor // The Vendor enum. All Vendors have to listed here and all listed vendors have to be implemented in this file
{
All = 0xFF,
Wago = 23, // Wago
BuR = 2 // B&R
};
struct deviceIOs // A structure which contains quantity information about connected IO. Used in MakeConfig. These info will be written into the SysVars
{
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. Used in MakeConfig.
{
char Ip[16]; // String: The IP address
char Ip4[4]; // String: The last byte of the IP address.
char Ip3[4]; // String: The third byte of the IP.
char Ip2[4]; // String: The second byte of the IP.
char Ip1[4]; // String: Thefirst byte of the IP.
enum Vendor Vendor; // The Vendor (Wago / B&R)
word SerialCode; // Serial Code
word DeviceCode; // Device Code
struct deviceIOs DeviceIOs; // A structure with more information about IOs
};
}
// This is for both the normal client and for making the sysvars.
@ -69,39 +69,39 @@ variables
/// <ModbusClient>
void DeviceInit(byte vendor)
{
switch ((enum Vendor) vendor)
{
case All: // information that may apply to all devices
thisDev.MaxBitCount = 0x0100;
thisDev.MaxRegisterCount = 0x0100;
thisDev.ReceiveWindow = 1;
break;
case Wago:
thisDev.Addr.Read.InputBits = 0x0000; // Wago inputs start at 0x000
thisDev.Addr.Read.InputRegisters = 0x0000;
thisDev.Addr.Read.OutputBits = 0x0200; // Wago reading outputs start at 0x200
thisDev.Addr.Read.OutputRegisters = 0x0200;
thisDev.Addr.Write.OutputBits = 0x0000; // Wago writing outputs start at 0x000
thisDev.Addr.Write.OutputRegisters = 0x0000;
thisDev.MaxBitCount = 0x0200; // Wago allows up to 512 digital inputs (trial & error)
thisDev.MaxRegisterCount = 0x0100; // Wago allows up to 256 analog inputs (trial & error)
thisDev.ReceiveWindow = 5; // Wago can handle 5 requests simultaneously
break;
case BuR:
thisDev.Addr.Read.InputBits = 0x0000; // B&R inputs start at 0x000
thisDev.Addr.Read.InputRegisters = 0x0000;
thisDev.Addr.Read.OutputBits = 0x0000; // B&R reading digital outputs start at 0x000
thisDev.Addr.Read.OutputRegisters = 0x0800; // B&R reading analog outputs start at 0x800
thisDev.Addr.Write.OutputBits = 0x0000; // B&R writing outputs start at 0x000
thisDev.Addr.Write.OutputRegisters = 0x0000;
thisDev.MaxBitCount = 0x4000; // B&R allows up to 16384 digital inputs
thisDev.MaxRegisterCount = 0x0800; // B&R allows up to 2048 analog inputs
thisDev.ReceiveWindow = 8; // B&R can handle 8 requests at a time
break;
default:
OnModbusClientPanics(VendorIdUnknown);
break;
}
switch ((enum Vendor) vendor)
{
case All: // information that may apply to all devices
thisDev.MaxBitCount = 0x0100;
thisDev.MaxRegisterCount = 0x0100;
thisDev.ReceiveWindow = 1;
break;
case Wago:
thisDev.Addr.Read.InputBits = 0x0000; // Wago inputs start at 0x000
thisDev.Addr.Read.InputRegisters = 0x0000;
thisDev.Addr.Read.OutputBits = 0x0200; // Wago reading outputs start at 0x200
thisDev.Addr.Read.OutputRegisters = 0x0200;
thisDev.Addr.Write.OutputBits = 0x0000; // Wago writing outputs start at 0x000
thisDev.Addr.Write.OutputRegisters = 0x0000;
thisDev.MaxBitCount = 0x0200; // Wago allows up to 512 digital inputs (trial & error)
thisDev.MaxRegisterCount = 0x0100; // Wago allows up to 256 analog inputs (trial & error)
thisDev.ReceiveWindow = 5; // Wago can handle 5 requests simultaneously
break;
case BuR:
thisDev.Addr.Read.InputBits = 0x0000; // B&R inputs start at 0x000
thisDev.Addr.Read.InputRegisters = 0x0000;
thisDev.Addr.Read.OutputBits = 0x0000; // B&R reading digital outputs start at 0x000
thisDev.Addr.Read.OutputRegisters = 0x0800; // B&R reading analog outputs start at 0x800
thisDev.Addr.Write.OutputBits = 0x0000; // B&R writing outputs start at 0x000
thisDev.Addr.Write.OutputRegisters = 0x0000;
thisDev.MaxBitCount = 0x4000; // B&R allows up to 16384 digital inputs
thisDev.MaxRegisterCount = 0x0800; // B&R allows up to 2048 analog inputs
thisDev.ReceiveWindow = 8; // B&R can handle 8 requests at a time
break;
default:
OnModbusClientPanics(VendorIdUnknown);
break;
}
}
@ -113,120 +113,120 @@ void DeviceInit(byte vendor)
/// <MakeConfig>
void _DeviceParseCode(word dev, enum Vendor vendor, struct deviceIOs dios)
{
byte input; // Yes: INPUT module; No: OUTPUT module
byte numChannels; // Quantity of channels
char module[10];
strncpy(module, "..%d,", elCount(module));
byte input; // Yes: INPUT module; No: OUTPUT module
byte numChannels; // Quantity of channels
char module[10];
strncpy(module, "..%d,", elCount(module));
switch(vendor)
{
case Wago: // if this is a Wago device
if (dev & 0x8000) // Digital Module
{
module[0] = 'D';
numChannels = (dev >> 8) & 0x007F;
switch(vendor)
{
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;
else if (dev & 0x0002) // Output Module
input = 0;
else // mhm... What is it? Input and Output?
writeDbg(AlgoError, "DeviceParseCode: Device code 0x%X cannot be decoded", dev);
}
else // Analog (=Complex) module
{
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
return;
case 491: // devices that have 1 inputs
input = 1;
numChannels = 1;
break;
case 452: // devices that have 2 inputs
case 465:
case 470:
case 472:
case 480:
case 454:
case 473:
case 474:
case 466:
case 484:
case 485:
case 492:
case 482:
case 475:
case 467:
case 477:
case 478:
case 456:
case 479:
case 476:
case 483:
case 461:
case 481:
case 462:
case 469:
case 487:
input = 1;
numChannels = 2;
break;
case 493: // devices that have 3 inputs
case 494:
case 495:
input = 1;
numChannels = 3;
break;
case 459: // devices that have 4 inputs
case 453:
case 455:
case 468:
case 457:
case 464:
case 460:
case 463:
input = 1;
numChannels = 4;
break;
case 552: // devices that have 2 outputs
case 585:
case 563:
case 554:
case 550:
case 560:
case 562:
case 556:
input = 0;
numChannels = 2;
break;
case 555: // devices that have 4 outputs
case 553:
case 557:
case 559:
input = 0;
numChannels = 4;
break;
default: // unknown device
writeDbg(AlgoInfo, "Connected device: 750-%d", dev);
return;
}
}
break; // switch(vendor)
default:
writeDbg(AlgoError, "ParseDeviceCode: Unknown vendor id: %d", vendor);
OnModbusClientPanics(VendorIdUnknown);
return;
}
module[1] = input ? 'I' : 'O'; // Set input/output char
if (dev & 0x0001) // Input Module
input = 1;
else if (dev & 0x0002) // Output Module
input = 0;
else // mhm... What is it? Input and Output?
writeDbg(AlgoError, "DeviceParseCode: Device code 0x%X cannot be decoded", dev);
}
else // Analog (=Complex) module
{
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
return;
case 491: // devices that have 1 inputs
input = 1;
numChannels = 1;
break;
case 452: // devices that have 2 inputs
case 465:
case 470:
case 472:
case 480:
case 454:
case 473:
case 474:
case 466:
case 484:
case 485:
case 492:
case 482:
case 475:
case 467:
case 477:
case 478:
case 456:
case 479:
case 476:
case 483:
case 461:
case 481:
case 462:
case 469:
case 487:
input = 1;
numChannels = 2;
break;
case 493: // devices that have 3 inputs
case 494:
case 495:
input = 1;
numChannels = 3;
break;
case 459: // devices that have 4 inputs
case 453:
case 455:
case 468:
case 457:
case 464:
case 460:
case 463:
input = 1;
numChannels = 4;
break;
case 552: // devices that have 2 outputs
case 585:
case 563:
case 554:
case 550:
case 560:
case 562:
case 556:
input = 0;
numChannels = 2;
break;
case 555: // devices that have 4 outputs
case 553:
case 557:
case 559:
input = 0;
numChannels = 4;
break;
default: // unknown device
writeDbg(AlgoInfo, "Connected device: 750-%d", dev);
return;
}
}
break; // switch(vendor)
default:
writeDbg(AlgoError, "ParseDeviceCode: Unknown vendor id: %d", vendor);
OnModbusClientPanics(VendorIdUnknown);
return;
}
module[1] = input ? 'I' : 'O'; // Set input/output char
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
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 returns the count of expected responses (Modbus telegrams). It is called by AnalyzeDevices() in MakeConfig
@ -235,32 +235,32 @@ void _DeviceParseCode(word dev, enum Vendor vendor, struct deviceIOs dios)
/// <MakeConfig>
byte _DeviceGetInformation(enum Vendor vendor)
{
switch (vendor)
{
case Wago:
ModbusReadRegisters(0x2011, 1); // Serial Code
ModbusReadRegisters(0x2012, 1); // Device Code
ModbusReadRegisters(0x1022, 1); // Number of AOs (= size in bits)
ModbusReadRegisters(0x1023, 1); // Number of AIs (= size in bits)
ModbusReadRegisters(0x1024, 1); // Number of DOs
ModbusReadRegisters(0x1025, 1); // Number of DIs
ModbusReadRegisters(0x2030, 65); // Connected IO 1
ModbusReadRegisters(0x2031, 64); // Connected IO 2
ModbusReadRegisters(0x2032, 64); // Connected IO 3
ModbusReadRegisters(0x2033, 63); // Connected IO 4
return 10; // 10 is the number of lines above (number of responses that we expect)
case BuR:
ModbusReadRegisters(0x1083, 1); // Product Code
ModbusReadRegisters(0x1101, 1); // Number of AIs
ModbusReadRegisters(0x1103, 1); // Number of AOs
ModbusReadRegisters(0x1105, 1); // Number of DIs
ModbusReadRegisters(0x1107, 1); // Number of DOs
return 5; // 5 is the number of lines above (number of responses that we expect)
default:
writeDbg(AlgoError, "DeviceGetInformation: Unknown vendor id: %d", vendor);
OnModbusClientPanics(VendorIdUnknown);
}
return 0;
switch (vendor)
{
case Wago:
ModbusReadRegisters(0x2011, 1); // Serial Code
ModbusReadRegisters(0x2012, 1); // Device Code
ModbusReadRegisters(0x1022, 1); // Number of AOs (= size in bits)
ModbusReadRegisters(0x1023, 1); // Number of AIs (= size in bits)
ModbusReadRegisters(0x1024, 1); // Number of DOs
ModbusReadRegisters(0x1025, 1); // Number of DIs
ModbusReadRegisters(0x2030, 65); // Connected IO 1
ModbusReadRegisters(0x2031, 64); // Connected IO 2
ModbusReadRegisters(0x2032, 64); // Connected IO 3
ModbusReadRegisters(0x2033, 63); // Connected IO 4
return 10; // 10 is the number of lines above (number of responses that we expect)
case BuR:
ModbusReadRegisters(0x1083, 1); // Product Code
ModbusReadRegisters(0x1101, 1); // Number of AIs
ModbusReadRegisters(0x1103, 1); // Number of AOs
ModbusReadRegisters(0x1105, 1); // Number of DIs
ModbusReadRegisters(0x1107, 1); // Number of DOs
return 5; // 5 is the number of lines above (number of responses that we expect)
default:
writeDbg(AlgoError, "DeviceGetInformation: Unknown vendor id: %d", vendor);
OnModbusClientPanics(VendorIdUnknown);
}
return 0;
}
// This function parses the received registers (requested by DeviceGetInformation())
@ -268,71 +268,71 @@ byte _DeviceGetInformation(enum Vendor vendor)
/// <MakeConfig>
void _DeviceParseRegister(struct device device, word address, word data[], word count)
{
byte i;
byte i;
//writeDbg(Debug, "_DeviceParseRegister: Address: 0x%04X, Data[0]: %d", address, data[0]);
switch (device.Vendor)
{
case Wago:
// Parse the received data
switch (address)
{
case 0x2011: // Serial Code
device.serialCode = data[0];
break;
case 0x2012: // Device Code
device.deviceCode = data[0];
break;
case 0x1022: // Number of output registers (AO)
device.DeviceIOs.OutputRegisters = data[0] / 16; // Wago returns the allocated bits.
break;
case 0x1023: // Number of input registers (AI)
device.DeviceIOs.InputRegisters = data[0] / 16;
break;
case 0x1024: // Number of output bits (DO)
device.DeviceIOs.OutputBits = data[0];
break;
case 0x1025: // Number of input bits (DI)
device.DeviceIOs.InputBits = data[0];
break;
case 0x2030: // Codes of connected modules. Pass to DeviceParseCode()
case 0x2031:
case 0x2032:
case 0x2033:
for (i = 0; i < count; i++)
{
if (data[i] == 0x0000) // No more devices --> end
break;
_DeviceParseCode(data[i], device.Vendor, device.DeviceIOs);
}
break;
}
break;
case BuR:
// Parse the received data
switch (address)
{
case 0x1083: // Product Code
device.serialCode = data[0];
break;
case 0x1101: // Number of input registers (AI)
device.DeviceIOs.InputRegisters = data[0];
break;
case 0x1103: // Number of output registers (AO)
device.DeviceIOs.OutputRegisters = data[0];
break;
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: // Number of output bits (DO)
device.DeviceIOs.OutputBits = data[0] * 8;
break;
}
break;
default:
writeDbg(AlgoError, "DeviceGetInformation: Unknown vendor id: %d", device.Vendor);
OnModbusClientPanics(VendorIdUnknown);
}
//writeDbg(Debug, "_DeviceParseRegister: Address: 0x%04X, Data[0]: %d", address, data[0]);
switch (device.Vendor)
{
case Wago:
// Parse the received data
switch (address)
{
case 0x2011: // Serial Code
device.serialCode = data[0];
break;
case 0x2012: // Device Code
device.deviceCode = data[0];
break;
case 0x1022: // Number of output registers (AO)
device.DeviceIOs.OutputRegisters = data[0] / 16; // Wago returns the allocated bits.
break;
case 0x1023: // Number of input registers (AI)
device.DeviceIOs.InputRegisters = data[0] / 16;
break;
case 0x1024: // Number of output bits (DO)
device.DeviceIOs.OutputBits = data[0];
break;
case 0x1025: // Number of input bits (DI)
device.DeviceIOs.InputBits = data[0];
break;
case 0x2030: // Codes of connected modules. Pass to DeviceParseCode()
case 0x2031:
case 0x2032:
case 0x2033:
for (i = 0; i < count; i++)
{
if (data[i] == 0x0000) // No more devices --> end
break;
_DeviceParseCode(data[i], device.Vendor, device.DeviceIOs);
}
break;
}
break;
case BuR:
// Parse the received data
switch (address)
{
case 0x1083: // Product Code
device.serialCode = data[0];
break;
case 0x1101: // Number of input registers (AI)
device.DeviceIOs.InputRegisters = data[0];
break;
case 0x1103: // Number of output registers (AO)
device.DeviceIOs.OutputRegisters = data[0];
break;
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: // Number of output bits (DO)
device.DeviceIOs.OutputBits = data[0] * 8;
break;
}
break;
default:
writeDbg(AlgoError, "DeviceGetInformation: Unknown vendor id: %d", device.Vendor);
OnModbusClientPanics(VendorIdUnknown);
}
}

File diff suppressed because it is too large Load diff

View file

@ -2,134 +2,134 @@
// 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
/// - _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 receives packets and hands them to _OnModbusReceive()
includes
{
#include "Common.cin"
#include "TcpUdpEilCommon.cin"
#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.
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;
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();
}
// 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);
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};
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;
}
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);
// 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;
}
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;
}
gSocketState = CONNECTING;
gSocketState = CONNECTING;
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);
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");
EthReceivePacket("OnEthReceivePacket");
EthCompletePacket(gPacket);
EthOutputPacket(gPacket);
EthReleasePacket(gPacket);
gSocketState = CONNECTING;
gtModbusArp.set(@sysvar::Config::Modbus::RequestTimeout);
return 0;
EthCompletePacket(gPacket);
EthOutputPacket(gPacket);
EthReleasePacket(gPacket);
gSocketState = CONNECTING;
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;
}
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);
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;
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;
if (gPacket != 0)
{
EthReleasePacket(gPacket);
gPacket = 0;
}
gSocketState = CLOSED;
}
// This method will wait for data from the remote device.
@ -142,83 +142,83 @@ void _ModbusRecv()
// Here: It fills the packet 'gPacket' and sends it
byte _ModbusSnd(byte buffer[], word length)
{
char str[20*3];
char str[20*3];
switch (gSocketState)
{
case CLOSED:
_ModbusConnectTo(gRemoteIP, gRemotePort);
if (gSocketState != OK)
{
writeDbg(ConnWarning, "_ModbusSnd: Reconnecting failed!");
return 1;
}
case OK:
break;
case NULL: // Delay
case CONNECTING:
return 1;
case ERROR:
writeDbg(ConnError, "_ModbusSnd: Socket status is not OK!");
OnModbusClientPanics(ConnectionError);
return 1;
default:
writeDbg(ConnError, "_ModbusSnd: Unknown socket status: %d", gSocketState);
OnModbusClientPanics(SwitchArgumentInvalid);
return 1;
}
switch (gSocketState)
{
case CLOSED:
_ModbusConnectTo(gRemoteIP, gRemotePort);
if (gSocketState != OK)
{
writeDbg(ConnWarning, "_ModbusSnd: Reconnecting failed!");
return 1;
}
case OK:
break;
case NULL: // Delay
case CONNECTING:
return 1;
case ERROR:
writeDbg(ConnError, "_ModbusSnd: Socket status is not OK!");
OnModbusClientPanics(ConnectionError);
return 1;
default:
writeDbg(ConnError, "_ModbusSnd: Unknown socket status: %d", gSocketState);
OnModbusClientPanics(SwitchArgumentInvalid);
return 1;
}
bin_to_strhex(buffer, str);
writeDbg(ConnDebug, "_ModbusSnd: %s (Länge: %d)", str, length);
bin_to_strhex(buffer, str);
writeDbg(ConnDebug, "_ModbusSnd: %s (Länge: %d)", str, length);
EthResizeToken(gPacket, "udp", "data" , length*8);
EthSetTokenData(gPacket, "udp", "data" , length, buffer);
EthResizeToken(gPacket, "udp", "data" , length*8);
EthSetTokenData(gPacket, "udp", "data" , length, buffer);
EthCompletePacket(gPacket);
EthOutputPacket(gPacket);
EthCompletePacket(gPacket);
EthOutputPacket(gPacket);
return 0;
return 0;
}
// This Method simply combines the two EthGetLastError functions
long _ModbusGetLastConnectionError(char string[])
{
EthGetLastErrorText(elCount(string), string);
return EthGetLastError();
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);
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;
byte buffer[gModbusMaxTelegramSize];
long size;
if (dir == TX)
return;
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, "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
}
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
}
}

View file

@ -1,182 +1,182 @@
/*@!Encoding:1252*/
variables
{
// Constant maximum values according to Modbus Specification v1.1
const word gMaxBitsPerRead = 2000;
const word gMaxRegsPerRead = 125;
const word gMaxBitsPerWrite = 1968; // Multiple of 8!
const word gMaxRegsPerWrite = 123;
// Constant maximum values according to Modbus Specification v1.1
const word gMaxBitsPerRead = 2000;
const word gMaxRegsPerRead = 125;
const word gMaxBitsPerWrite = 1968; // Multiple of 8!
const word gMaxRegsPerWrite = 123;
// Function Codes according to Modbus Specification v1.1
enum ModbusFuncCode
{
ReadBitsOut = 0x01, // Read Coils
ReadBitsIn = 0x02, // Read Discrete Inputs
ReadRegistersOut = 0x03, // Read Holding Registers
ReadRegistersIn = 0x04, // Read Input Registers
WriteBit = 0x05, // Write Single Coil
WriteRegister = 0x06, // Write Single Holding Register
WriteBits = 0x0F, // Write Multiple Coils
WriteRegisters = 0x10, // Write Multiple Holding Registers
MaskRegister = 0x16, // Mask Write Holding Register
ReadWriteRegisters = 0x17 // Read/Write Multiple Registers
};
// Function Codes according to Modbus Specification v1.1
enum ModbusFuncCode
{
ReadBitsOut = 0x01, // Read Coils
ReadBitsIn = 0x02, // Read Discrete Inputs
ReadRegistersOut = 0x03, // Read Holding Registers
ReadRegistersIn = 0x04, // Read Input Registers
WriteBit = 0x05, // Write Single Coil
WriteRegister = 0x06, // Write Single Holding Register
WriteBits = 0x0F, // Write Multiple Coils
WriteRegisters = 0x10, // Write Multiple Holding Registers
MaskRegister = 0x16, // Mask Write Holding Register
ReadWriteRegisters = 0x17 // Read/Write Multiple Registers
};
// Modbus Application Header
// Every Modbus Packet begins with these 7 (+FuncCode) Bytes
_align(1) struct ModbusApHeader
{
word TxID;
word Protocol;
word Length;
byte UnitID;
byte FuncCode;
};
// Modbus Application Header
// Every Modbus Packet begins with these 7 (+FuncCode) Bytes
_align(1) struct ModbusApHeader
{
word TxID;
word Protocol;
word Length;
byte UnitID;
byte FuncCode;
};
/// Request structures following
// Read Data from the host. We only need the start address and the number of bits/registers we want to read
_align(1) struct ModbusReqRead
{
struct ModbusApHeader Header;
word Address;
word Count;
};
// Write a single value to a bit/register
_align(1) struct ModbusReqWriteSingle
{
struct ModbusApHeader Header;
word Address;
word Value;
};
// Write several values to a bit/register starting with Address
_align(1) struct ModbusReqWriteBits
{
struct ModbusApHeader Header;
word Address;
word Count;
byte ByteCount;
byte Data[gMaxBitsPerWrite/8]; // Max length: 1968 bits
};
// Write several values to bits starting with Address
_align(1) struct ModbusReqWriteRegisters
{
struct ModbusApHeader Header;
word Address;
word Count;
byte ByteCount;
word Data[gMaxRegsPerWrite]; // Max length: 123 registers
};
// Write AND and OR masks to a holding register
_align(1) struct ModbusReqWriteMasks
{
struct ModbusApHeader Header;
word Address;
word And;
word Or;
};
// Read and write multiple registers
_align(1) struct ModbusReqReadWriteRegisters
{
struct ModbusApHeader Header;
word ReadAddress;
word ReadCount;
word WriteAddress;
word WriteCount;
byte ByteCount;
word Data[gMaxRegsPerWrite-2]; // Max length: 123-2 registers
};
/// Request structures following
// Read Data from the host. We only need the start address and the number of bits/registers we want to read
_align(1) struct ModbusReqRead
{
struct ModbusApHeader Header;
word Address;
word Count;
};
// Write a single value to a bit/register
_align(1) struct ModbusReqWriteSingle
{
struct ModbusApHeader Header;
word Address;
word Value;
};
// Write several values to a bit/register starting with Address
_align(1) struct ModbusReqWriteBits
{
struct ModbusApHeader Header;
word Address;
word Count;
byte ByteCount;
byte Data[gMaxBitsPerWrite/8]; // Max length: 1968 bits
};
// Write several values to bits starting with Address
_align(1) struct ModbusReqWriteRegisters
{
struct ModbusApHeader Header;
word Address;
word Count;
byte ByteCount;
word Data[gMaxRegsPerWrite]; // Max length: 123 registers
};
// Write AND and OR masks to a holding register
_align(1) struct ModbusReqWriteMasks
{
struct ModbusApHeader Header;
word Address;
word And;
word Or;
};
// Read and write multiple registers
_align(1) struct ModbusReqReadWriteRegisters
{
struct ModbusApHeader Header;
word ReadAddress;
word ReadCount;
word WriteAddress;
word WriteCount;
byte ByteCount;
word Data[gMaxRegsPerWrite-2]; // Max length: 123-2 registers
};
/// Response structures following
// Receive several bit values
_align(1) struct ModbusResReceiveBits
{
struct ModbusApHeader Header;
byte ByteCount;
byte Data[gMaxBitsPerRead/8]; // Max length: 2000 bits
};
// Receive several register values
_align(1) struct ModbusResReceiveRegisters
{
struct ModbusApHeader Header;
byte ByteCount;
word Data[gMaxRegsPerRead]; // Max length: 125 registers
};
// Confirm the write of a single bit/register
_align(1) struct ModbusResConfirmSingle
{
struct ModbusApHeader Header;
word Address;
int Value;
};
// Confirm the write of several bits/registers
_align(1) struct ModbusResConfirmMultiple
{
struct ModbusApHeader Header;
word Address;
word Count;
};
// Confirm the write of AND and OR mask
_align(1) struct ModbusResConfirmMasks
{
struct ModbusApHeader Header;
word Address;
word And;
word Or;
};
/// Response structures following
// Receive several bit values
_align(1) struct ModbusResReceiveBits
{
struct ModbusApHeader Header;
byte ByteCount;
byte Data[gMaxBitsPerRead/8]; // Max length: 2000 bits
};
// Receive several register values
_align(1) struct ModbusResReceiveRegisters
{
struct ModbusApHeader Header;
byte ByteCount;
word Data[gMaxRegsPerRead]; // Max length: 125 registers
};
// Confirm the write of a single bit/register
_align(1) struct ModbusResConfirmSingle
{
struct ModbusApHeader Header;
word Address;
int Value;
};
// Confirm the write of several bits/registers
_align(1) struct ModbusResConfirmMultiple
{
struct ModbusApHeader Header;
word Address;
word Count;
};
// Confirm the write of AND and OR mask
_align(1) struct ModbusResConfirmMasks
{
struct ModbusApHeader Header;
word Address;
word And;
word Or;
};
// The maximum modbus telegram size.
// Several telegrams use this many bytes, but CAPL does not support a constant max function to let it calculate it. So we use ModbusResReceiveRegisters
const word gModbusMaxTelegramSize = __size_of(struct ModbusResReceiveRegisters);
// The maximum modbus telegram size.
// Several telegrams use this many bytes, but CAPL does not support a constant max function to let it calculate it. So we use ModbusResReceiveRegisters
const word gModbusMaxTelegramSize = __size_of(struct ModbusResReceiveRegisters);
// Some errors that may occur when sending a request
enum ModbusRequestError
{
Exception, // Modbus exception was returned
Timeout, // Timeout occured, resending the packet
FinalTimeout, // Timeout occured, not resending the packet
NotSent // Sending the packet was impossible
};
// Modbus exception codes according to Modbus Specification v1.1
enum ModbusException
{
None = 0x00,
IllegalFuncCode = 0x01,
IllegalDataAddress = 0x02,
IllegalDataValue = 0x03,
ServerFailure = 0x04,
Acknowledge = 0x05,
ServerBusy = 0x06,
GatewayPathsNA = 0x0A,
TargetOffline = 0x0B
};
// Modbus Exception descriptions
char ModbusExceptions[11][72] = {
"Illegal func code (0x01). The function code is unknown by the server",
"Illegal data address (0x02). Please check your configuration",
"Illegal data value (0x03)",
"Server failure (0x04). The server failed during execution",
"Acknowledge (0x05). The server needs more time to generate the response",
"Server busy (0x06). The request could not be accepted",
"",
"",
"",
"Gateway problem (0x0A). Gateway paths not available",
"Gateway problem (0x0B). The targeted device failed to respond"
};
// Fatal errors that may stop the code execution
enum FatalErrors
{
ParsingBuffer,
ModbusPackageWasSplit,
DeviceCodeUnknown,
VendorIdUnknown,
ConnectionError,
FuncCodeIncorrect,
AddressFailure,
SwitchArgumentInvalid
};
// Some errors that may occur when sending a request
enum ModbusRequestError
{
Exception, // Modbus exception was returned
Timeout, // Timeout occured, resending the packet
FinalTimeout, // Timeout occured, not resending the packet
NotSent // Sending the packet was impossible
};
// Modbus exception codes according to Modbus Specification v1.1
enum ModbusException
{
None = 0x00,
IllegalFuncCode = 0x01,
IllegalDataAddress = 0x02,
IllegalDataValue = 0x03,
ServerFailure = 0x04,
Acknowledge = 0x05,
ServerBusy = 0x06,
GatewayPathsNA = 0x0A,
TargetOffline = 0x0B
};
// Modbus Exception descriptions
char ModbusExceptions[11][72] = {
"Illegal func code (0x01). The function code is unknown by the server",
"Illegal data address (0x02). Please check your configuration",
"Illegal data value (0x03)",
"Server failure (0x04). The server failed during execution",
"Acknowledge (0x05). The server needs more time to generate the response",
"Server busy (0x06). The request could not be accepted",
"",
"",
"",
"Gateway problem (0x0A). Gateway paths not available",
"Gateway problem (0x0B). The targeted device failed to respond"
};
// Fatal errors that may stop the code execution
enum FatalErrors
{
ParsingBuffer,
ModbusPackageWasSplit,
DeviceCodeUnknown,
VendorIdUnknown,
ConnectionError,
FuncCodeIncorrect,
AddressFailure,
SwitchArgumentInvalid
};
}

View file

@ -2,235 +2,235 @@
// This file contains 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
/// - _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
/// - Some function that receives packets and hands them to _OnModbusReceive()
includes
{
#include "Common.cin"
#include "TcpUdpEilCommon.cin"
#include "Common.cin"
#include "TcpUdpEilCommon.cin"
}
variables
{
TcpSocket gSocket;
TcpSocket gSocket;
}
// This method opens an TCP socket. It has to check for several errors.
word _TcpOpenSocket()
{
byte i;
char errorText[200];
long error;
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;
}
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, "_TcpOpenSocket: Tcp socket opened.");
}
return 0;
// 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, "_TcpOpenSocket: 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;
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;
}
// 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);
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 error;
long error;
// Try to open a socket
error = _TcpOpenSocket();
if (error != 0)
{
gSocketState = ERROR;
return error;
}
// Try to open a socket
error = _TcpOpenSocket();
if (error != 0)
{
gSocketState = ERROR;
return error;
}
gRemoteIP = remoteIp;
gRemotePort = remotePort;
gRemoteIP = remoteIp;
gRemotePort = remotePort;
// Connect to Server
if (gSocket.Connect(remoteIp, remotePort) != 0)
{
error = gSocket.GetLastSocketError();
// Connect to Server
if (gSocket.Connect(remoteIp, remotePort) != 0)
{
error = gSocket.GetLastSocketError();
if (error != WSAEWOULDBLOCK) // OnTcpConnect will be called otherwise
{
writeDbg(ConnError, "_ModbusConnectTo: No connection established: %d", error);
gSocketState = ERROR;
OnModbusClientPanics(ConnectionError);
return error;
}
else
{
writeDbg(ConnDebug, "_ModbusConnectTo: WSAE would block");
gSocketState = CONNECTING;
}
return 0;
}
gSocketState = CONNECTING; // Don't set state OK because the Stack doesn't tell us WSAEWOULDBLOCK each time whensoever is neccessary
return 0;
if (error != WSAEWOULDBLOCK) // OnTcpConnect will be called otherwise
{
writeDbg(ConnError, "_ModbusConnectTo: No connection established: %d", error);
gSocketState = ERROR;
OnModbusClientPanics(ConnectionError);
return error;
}
else
{
writeDbg(ConnDebug, "_ModbusConnectTo: WSAE would block");
gSocketState = CONNECTING;
}
return 0;
}
gSocketState = CONNECTING; // Don't set state OK because the Stack doesn't tell us WSAEWOULDBLOCK each time whensoever is neccessary
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();
}
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()
{
writeDbg(ConnDebug, "_ModbusDisconnect: Disconnecting");
gSocket.Close();
gSocketState = CLOSED;
writeDbg(ConnDebug, "_ModbusDisconnect: Disconnecting");
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;
int result;
if (gSocketState != OK)
{
writeDbg(ConnWarning, "_ModbusRecv: Socket status is not OK!");
_ModbusDisconnect();
// OnModbusClientPanics(ConnectionError);
return;
}
result = gSocket.Receive(gRxBuffer, elcount(gRxBuffer));
if (gSocketState != OK)
{
writeDbg(ConnWarning, "_ModbusRecv: Socket status is not OK!");
_ModbusDisconnect();
// OnModbusClientPanics(ConnectionError);
return;
}
result = gSocket.Receive(gRxBuffer, elcount(gRxBuffer));
if (result != 0) // Calling OnTcpReceive otherwise
{
gIpLastErr = gSocket.GetLastSocketError();
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();
}
}
if (gIpLastErr != WSA_IO_PENDING) // Calling OnTcpReceive otherwise
{
gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr));
writeDbg(ConnError, "_ModbusRecv: (%d) %s", gIpLastErr, gIpLastErrStr);
_ModbusDisconnect();
}
}
return;
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];
char str[20*3];
switch (gSocketState)
{
case OK: // Go on and send
break;
case CLOSED: // If the connection is closed
_ModbusConnectTo(gRemoteIP, gRemotePort); // Try to (re)connect
if (gSocketState < OK) // If this didn't work (state != OK)
{
if (gSocketState < CONNECTING)
{
writeDbg(ConnError, "_ModbusSnd: Reconnecting failed!");
OnModbusClientPanics(ConnectionError);
}
return 1; // Abort sending in all cases
}
case NULL: // Delay
case CONNECTING:
return 1;
case ERROR:
writeDbg(ConnError, "_ModbusSnd: Socket status is not OK!");
OnModbusClientPanics(ConnectionError);
return 1;
default:
writeDbg(ConnError, "_ModbusSnd: Unknown socket status: %d", gSocketState);
OnModbusClientPanics(SwitchArgumentInvalid);
return 1;
}
switch (gSocketState)
{
case OK: // Go on and send
break;
case CLOSED: // If the connection is closed
_ModbusConnectTo(gRemoteIP, gRemotePort); // Try to (re)connect
if (gSocketState < OK) // If this didn't work (state != OK)
{
if (gSocketState < CONNECTING)
{
writeDbg(ConnError, "_ModbusSnd: Reconnecting failed!");
OnModbusClientPanics(ConnectionError);
}
return 1; // Abort sending in all cases
}
case NULL: // Delay
case CONNECTING:
return 1;
case ERROR:
writeDbg(ConnError, "_ModbusSnd: Socket status is not OK!");
OnModbusClientPanics(ConnectionError);
return 1;
default:
writeDbg(ConnError, "_ModbusSnd: Unknown socket status: %d", gSocketState);
OnModbusClientPanics(SwitchArgumentInvalid);
return 1;
}
bin_to_strhex(buffer, str);
bin_to_strhex(buffer, str);
if (gSocket.Send(buffer, length) != 0)
{
gIpLastErr = gSocket.GetLastSocketError();
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!
}
writeDbg(ConnDebug, "_ModbusSnd: %s (Länge: %d)", str, length);
return 0;
if (gIpLastErr != WSA_IO_PENDING)
{
gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr));
writeDbg(ConnError, "_ModbusSnd: (%d) %s", gIpLastErr, gIpLastErrStr);
_ModbusDisconnect();
return 1;
}
// else: tough luck!
}
writeDbg(ConnDebug, "_ModbusSnd: %s (Länge: %d)", str, length);
return 0;
}
// This method simply combines the two EthGetLastError functions
long _ModbusGetLastConnectionError(char string[])
{
gSocket.GetLastSocketErrorAsString(string, elCount(string));
return gSocket.GetLastSocketError();
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
_OnModbusReceive(socket, result, address, port, buffer, size); // Hand to Modbus layer
}

View file

@ -2,188 +2,188 @@
// This file contains functions that abstract the UDP/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'
/// - 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"
#include "Common.cin"
#include "TcpUdpEilCommon.cin"
}
variables
{
UdpSocket gSocket;
UdpSocket gSocket;
}
// This method opens an UDP socket. It has to check for several errors.
word _UdpOpenSocket()
{
byte i;
char errorText[200];
long error;
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;
}
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;
// 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;
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;
}
// 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);
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 error;
long error;
// Try to open a socket
error = _UdpOpenSocket();
if (error != 0)
{
gSocketState = ERROR;
return error;
}
// Try to open a socket
error = _UdpOpenSocket();
if (error != 0)
{
gSocketState = ERROR;
return error;
}
gRemoteIP = remoteIp;
gRemotePort = remotePort;
gSocketState = OK;
return 0;
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;
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;
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 (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 (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();
}
}
if (gIpLastErr != WSA_IO_PENDING) // Calling OnUdpReceive otherwise
{
gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elCount(gIpLastErrStr));
writeDbg(ConnError, "UdpReceiveFrom: (%d) %s", gIpLastErr, gIpLastErrStr);
_ModbusDisconnect();
}
}
return;
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];
char str[20*3];
switch (gSocketState)
{
case OK: // Send
break;
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 NULL: // Delay
case CONNECTING:
return 1;
case ERROR:
writeDbg(ConnError, "_ModbusSnd: Socket status is not OK!");
OnModbusClientPanics(ConnectionError);
return 1;
default:
writeDbg(ConnError, "_ModbusSnd: Unknown socket status: %d", gSocketState);
OnModbusClientPanics(SwitchArgumentInvalid);
return 1;
}
switch (gSocketState)
{
case OK: // Send
break;
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 NULL: // Delay
case CONNECTING:
return 1;
case ERROR:
writeDbg(ConnError, "_ModbusSnd: Socket status is not OK!");
OnModbusClientPanics(ConnectionError);
return 1;
default:
writeDbg(ConnError, "_ModbusSnd: Unknown socket status: %d", gSocketState);
OnModbusClientPanics(SwitchArgumentInvalid);
return 1;
}
bin_to_strhex(buffer, str);
writeDbg(ConnDebug, "ModbusSnd: %s (Länge: %d, Time: %f)", str, length, timeNowFloat()/100000.0);
if (gSocket.SendTo(gRemoteIP, gRemotePort, buffer, length) != 0)
{
gIpLastErr = gSocket.GetLastSocketError();
bin_to_strhex(buffer, str);
writeDbg(ConnDebug, "ModbusSnd: %s (Länge: %d, Time: %f)", str, length, timeNowFloat()/100000.0);
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;
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();
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
_OnModbusReceive(socket, result, address, port, buffer, size); // Hand to Modbus layer
}

View file

@ -2,21 +2,21 @@
variables
{
// Some constants
const long WSA_IO_PENDING = 997;
const long WSAEWOULDBLOCK = 10035;
const dword INVALID_IP = 0xFFFFFFFF;
// Some constants
const long WSA_IO_PENDING = 997;
const long WSAEWOULDBLOCK = 10035;
const dword INVALID_IP = 0xFFFFFFFF;
long gIpLastErr = 0;
char gIpLastErrStr[512] = "";
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 {ERROR = 0, NULL = 100, CONNECTING = 120, CLOSED = 140, OK = 200};
enum SocketState gSocketState = NULL;
// The state of the socket will be safed here. This way we can check if we can send/receive packets
enum SocketState {ERROR = 0, NULL = 100, CONNECTING = 120, CLOSED = 140, OK = 200};
enum SocketState gSocketState = NULL;
dword gRemoteIP = INVALID_IP;
word gRemotePort = 0;
dword gRemoteIP = INVALID_IP;
word gRemotePort = 0;
// The buffer in which received telegrams are saved
byte gRxBuffer[8192];
// The buffer in which received telegrams are saved
byte gRxBuffer[8192];
}

View file

@ -1,155 +1,155 @@
/*@!Encoding:1252*/
includes
{
#include "include/ModbusUdpClientCommon.cin"
#include "include/ModbusFunctions.cin"
#include "include/ModbusUdpClientCommon.cin"
#include "include/ModbusFunctions.cin"
}
variables
{
struct device
{
char Ip[16];
char IpLsb[4];
char IpNet[4];
enum Vendor Vendor;
word SerialCode;
word DeviceCode;
struct deviceIOs DeviceIOs;
};
struct device
{
char Ip[16];
char IpLsb[4];
char IpNet[4];
enum Vendor Vendor;
word SerialCode;
word DeviceCode;
struct deviceIOs DeviceIOs;
};
char[16] gIps[long];
char gScanFirstIp[16];
char gScanLastIp[16];
char[16] gIps[long];
char gScanFirstIp[16];
char gScanLastIp[16];
char fnSysvar[40]; // Filename of Sysvars
char fnDbc[40]; // Filename of DBC
char name[20]; // Name of project
dword ips[50]; // detected IPs
char fnSysvar[40]; // Filename of Sysvars
char fnDbc[40]; // Filename of DBC
char name[20]; // Name of project
dword ips[50]; // detected IPs
file f;
byte gIpNets[long];
struct device gIpsSorted[long];
dword gScanFirst, gScanLast;
word ADi, ADn, ADl;
file f;
byte gIpNets[long];
struct device gIpsSorted[long];
dword gScanFirst, gScanLast;
word ADi, ADn, ADl;
byte gMaxTransmissionCount;
byte gMaxTransmissionCount;
}
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);
*/
// 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);
*/
// Scan a range of IPs for devices. Start and Stop go here
// Please note: Currelty .255 will be skipped!
strncpy(gScanFirstIp, "192.168.1.2", 16);
strncpy(gScanLastIp, "192.168.1.10", 16);
// Scan a range of IPs for devices. Start and Stop go here
// Please note: Currelty .255 will be skipped!
strncpy(gScanFirstIp, "192.168.1.2", 16);
strncpy(gScanLastIp, "192.168.1.10", 16);
// Name of the project
strncpy(name, "Modbus", elCount(name));
// Paths to the generated files relative to .cfg
strncpy(fnSysvar, "include/SysVars/generated.vsysvar", elCount(fnSysvar));
strncpy(fnDbc, "include/DBC/generated.dbc", elCount(fnDbc));
OutputDebugLevel = Error;
// Name of the project
strncpy(name, "Modbus", elCount(name));
// Paths to the generated files relative to .cfg
strncpy(fnSysvar, "include/SysVars/generated.vsysvar", elCount(fnSysvar));
strncpy(fnDbc, "include/DBC/generated.dbc", elCount(fnDbc));
OutputDebugLevel = Error;
}
on start
{
gMaxTransmissionCount = @sysvar::Config::Modbus::MaxTransmissionCount;
gMaxTransmissionCount = @sysvar::Config::Modbus::MaxTransmissionCount;
if (gIps.Size() == 0)
DetectDevices();
else
MakeIpNets();
if (gIps.Size() == 0)
DetectDevices();
else
MakeIpNets();
}
/// <PutString>
void PutString(file f, char str[])
{
f.PutString(str, strlen(str));
f.PutString(str, strlen(str));
}
/// <PutString>
void PutString(file f, word d)
{
char str[6];
ltoa(d, str, 10);
f.PutString(str, strlen(str));
char str[6];
ltoa(d, str, 10);
f.PutString(str, strlen(str));
}
/// <PutString>
void PutString(file f, byte d)
{
char str[4];
ltoa(d, str, 10);
f.PutString(str, strlen(str));
char str[4];
ltoa(d, str, 10);
f.PutString(str, strlen(str));
}
// Step 1: Detect active devices and collect IP addresses
/// <Step1>
void DetectDevices()
{
@sysvar::Config::Modbus::MaxTransmissionCount = 1;
write("Scanning from %s to %s with timeout of %d ms", gScanFirstIp, gScanLastIp, @sysvar::Config::Modbus::RequestTimeout);
gScanFirst = ipGetAddressAsNumber(gScanFirstIp);
gScanLast = ipGetAddressAsNumber(gScanLastIp);
write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24);
ModbusConnectTo(gScanFirst, @sysvar::Config::Modbus::Port);
ModbusReadBits(0, 1);
@sysvar::Config::Modbus::MaxTransmissionCount = 1;
write("Scanning from %s to %s with timeout of %d ms", gScanFirstIp, gScanLastIp, @sysvar::Config::Modbus::RequestTimeout);
gScanFirst = ipGetAddressAsNumber(gScanFirstIp);
gScanLast = ipGetAddressAsNumber(gScanLastIp);
write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24);
ModbusConnectTo(gScanFirst, @sysvar::Config::Modbus::Port);
ModbusReadBits(0, 1);
}
/// <Step1>
void DetectDevicesNext()
{
// next IP
// 0xFE...... --> Skip xxx.xxx.xxx.255 which is broadcast address in 192.168.xxx.0 nets
if ((gScanFirst & 0xFFFFFF00) == 0xFEFFFF00)
{
gScanFirst &= 0x000000FF;
gScanFirst += 0x00000001;
write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24);
}
else if ((gScanFirst & 0xFFFF0000) == 0xFEFF0000)
{
gScanFirst &= 0x0000FFF;
gScanFirst += 0x00000100;
write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24);
}
else if ((gScanFirst & 0xFF000000) == 0xFE000000)
{
gScanFirst &= 0x00FFFFFF;
gScanFirst += 0x00010000;
write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24);
}
else
{
gScanFirst += 0x01000000;
}
if (gScanFirst == gScanLast)
{
@sysvar::Config::Modbus::MaxTransmissionCount = gMaxTransmissionCount;
MakeIpNets();
return;
}
writeEx(1, 1, ".");
gRemoteIP = gScanFirst; // Don't open new socket, it takes too much time.
ModbusReadBits(0, 1);
// next IP
// 0xFE...... --> Skip xxx.xxx.xxx.255 which is broadcast address in 192.168.xxx.0 nets
if ((gScanFirst & 0xFFFFFF00) == 0xFEFFFF00)
{
gScanFirst &= 0x000000FF;
gScanFirst += 0x00000001;
write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24);
}
else if ((gScanFirst & 0xFFFF0000) == 0xFEFF0000)
{
gScanFirst &= 0x0000FFF;
gScanFirst += 0x00000100;
write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24);
}
else if ((gScanFirst & 0xFF000000) == 0xFE000000)
{
gScanFirst &= 0x00FFFFFF;
gScanFirst += 0x00010000;
write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24);
}
else
{
gScanFirst += 0x01000000;
}
if (gScanFirst == gScanLast)
{
@sysvar::Config::Modbus::MaxTransmissionCount = gMaxTransmissionCount;
MakeIpNets();
return;
}
writeEx(1, 1, ".");
gRemoteIP = gScanFirst; // Don't open new socket, it takes too much time.
ModbusReadBits(0, 1);
}
/// <Step1>
void OnModbusReadBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap)
{
DetectDevicesNext();
DetectDevicesNext();
}
/// <Step1>
void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[], struct ModbusReqRead mbreq)
{
ipGetAddressAsString(gScanFirst, gIps[gScanFirst], 16);
DetectDevicesNext();
ipGetAddressAsString(gScanFirst, gIps[gScanFirst], 16);
DetectDevicesNext();
}
// Step 2: Sort into subnets
@ -157,28 +157,28 @@ void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[]
/// <Step2>
void MakeIpNets()
{
long ipNum;
long ipNum;
if (gIps.Size() == 0)
{
stop();
return;
}
if (gIps.Size() == 0)
{
stop();
return;
}
for (long i : gIps)
{
ipNum = ipGetAddressAsNumber(gIps[i]); // convert IP to dword
for (long i : gIps)
{
ipNum = ipGetAddressAsNumber(gIps[i]); // convert IP to dword
gIpNets[(ipNum >> 16) & 0xFF] = 1; // add subnet
ips[gIpsSorted.size()] = ipNum; // add ip to array
strncpy(gIpsSorted[ipNum].IP, gIps[i], 16); // copy to new array
ltoa((ipNum >> 16) & 0xFF, gIpsSorted[ipNum].IpNet, 10); // add .IpNet
ltoa((ipNum >> 24) & 0xFF, gIpsSorted[ipNum].IpLsb, 10); // add .IpLsb
gIpNets[(ipNum >> 16) & 0xFF] = 1; // add subnet
ips[gIpsSorted.size()] = ipNum; // add ip to array
strncpy(gIpsSorted[ipNum].IP, gIps[i], 16); // copy to new array
ltoa((ipNum >> 16) & 0xFF, gIpsSorted[ipNum].IpNet, 10); // add .IpNet
ltoa((ipNum >> 24) & 0xFF, gIpsSorted[ipNum].IpLsb, 10); // add .IpLsb
gIps.Remove(i);
}
gIps.Remove(i);
}
AnalyzeDevices();
AnalyzeDevices();
}
@ -186,302 +186,302 @@ void MakeIpNets()
/// <Step3>
void AnalyzeDevices()
{
ADn = 0;
ADi = 0;
ADl = gIpsSorted.Size();
write("Analyzing %s...", gIpsSorted[ips[ADi]].Ip);
gIpsSorted[ips[ADi]].Vendor = Wago;
if (gRemoteIP != INVALID_IP)
gRemoteIP = ips[ADi];
else
ModbusConnectTo(ips[ADi], @sysvar::Config::Modbus::Port);
ModbusReadRegisters(0x2011, 1);
ModbusReadRegisters(0x2012, 1);
ModbusReadRegisters(0x2030, 65);
ModbusReadRegisters(0x2031, 64);
ModbusReadRegisters(0x2032, 64);
ModbusReadRegisters(0x2033, 63);
ADn = 0;
ADi = 0;
ADl = gIpsSorted.Size();
write("Analyzing %s...", gIpsSorted[ips[ADi]].Ip);
gIpsSorted[ips[ADi]].Vendor = Wago;
if (gRemoteIP != INVALID_IP)
gRemoteIP = ips[ADi];
else
ModbusConnectTo(ips[ADi], @sysvar::Config::Modbus::Port);
ModbusReadRegisters(0x2011, 1);
ModbusReadRegisters(0x2012, 1);
ModbusReadRegisters(0x2030, 65);
ModbusReadRegisters(0x2031, 64);
ModbusReadRegisters(0x2032, 64);
ModbusReadRegisters(0x2033, 63);
}
/// <Step3>
void AnalyzeDevicesNext()
{
if (++ADi >= ADl) // we have analyzed all devices
{
MakeFiles();
return;
}
if (++ADi >= ADl) // we have analyzed all devices
{
MakeFiles();
return;
}
ADn = 0;
gRemoteIP = ips[ADi];
write("Analyzing %s...", gIpsSorted[ips[ADi]].Ip);
ModbusReadRegisters(0x2011, 1);
ModbusReadRegisters(0x2012, 1);
ModbusReadRegisters(0x2030, 65);
ModbusReadRegisters(0x2031, 64);
ModbusReadRegisters(0x2032, 64);
ModbusReadRegisters(0x2033, 63);
ADn = 0;
gRemoteIP = ips[ADi];
write("Analyzing %s...", gIpsSorted[ips[ADi]].Ip);
ModbusReadRegisters(0x2011, 1);
ModbusReadRegisters(0x2012, 1);
ModbusReadRegisters(0x2030, 65);
ModbusReadRegisters(0x2031, 64);
ModbusReadRegisters(0x2032, 64);
ModbusReadRegisters(0x2033, 63);
}
/// <Step3>
void OnModbusReadRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap)
{
switch (error)
{
case FinalTimeout:
writeLineEx(0, 3, "Error while analyzing %s! The device did not respond! Ignoring...", gIpsSorted[ips[ADi]].IP);
gQueueAck.Clear();
gQueuePending.Clear();
gQueueSent.Clear();
gIpsSorted.Remove(ips[ADi]);
AnalyzeDevicesNext();
break;
case Exception:
writeLineEx(0, 3, "Error while analyzing %s! The device respond with exception code %d! Ignoring...", gIpsSorted[ips[ADi]].IP, ex);
gQueueAck.Clear();
gQueuePending.Clear();
gQueueSent.Clear();
gIpsSorted.Remove(ips[ADi]);
AnalyzeDevicesNext();
break;
}
switch (error)
{
case FinalTimeout:
writeLineEx(0, 3, "Error while analyzing %s! The device did not respond! Ignoring...", gIpsSorted[ips[ADi]].IP);
gQueueAck.Clear();
gQueuePending.Clear();
gQueueSent.Clear();
gIpsSorted.Remove(ips[ADi]);
AnalyzeDevicesNext();
break;
case Exception:
writeLineEx(0, 3, "Error while analyzing %s! The device respond with exception code %d! Ignoring...", gIpsSorted[ips[ADi]].IP, ex);
gQueueAck.Clear();
gQueuePending.Clear();
gQueueSent.Clear();
gIpsSorted.Remove(ips[ADi]);
AnalyzeDevicesNext();
break;
}
}
/// <Step3>
void OnModbusReadRegistersSuccess(struct ModbusResReceiveRegisters mbres, struct ModbusReqRead mbreq)
{
byte i;
byte i;
switch (mbreq.Address)
{
case 0x2011:
gIpsSorted[ips[ADi]].serialCode = mbres.Data[0];
break;
case 0x2012:
gIpsSorted[ips[ADi]].deviceCode = mbres.Data[0];
break;
case 0x2030:
case 0x2031:
case 0x2032:
case 0x2033:
for (i = 0; i < mbreq.Count; i++)
{
if (mbres.Data[i] == 0x0000)
break;
ParseDeviceCode(mbres.Data[i], gIpsSorted[ips[ADi]].Vendor, gIpsSorted[ips[ADi]].DeviceIOs);
}
break;
}
switch (mbreq.Address)
{
case 0x2011:
gIpsSorted[ips[ADi]].serialCode = mbres.Data[0];
break;
case 0x2012:
gIpsSorted[ips[ADi]].deviceCode = mbres.Data[0];
break;
case 0x2030:
case 0x2031:
case 0x2032:
case 0x2033:
for (i = 0; i < mbreq.Count; i++)
{
if (mbres.Data[i] == 0x0000)
break;
ParseDeviceCode(mbres.Data[i], gIpsSorted[ips[ADi]].Vendor, gIpsSorted[ips[ADi]].DeviceIOs);
}
break;
}
if (++ADn == 6)
AnalyzeDevicesNext();
if (++ADn == 6)
AnalyzeDevicesNext();
}
// Step 4: Create the files with the queried data
/// <Step4>
void MakeFiles()
{
GenSysvars();
GenDbc();
stop();
GenSysvars();
GenDbc();
stop();
}
// Generate the SysVars XML
/// <Step4>
void GenSysvars()
{
write("GenSysvars() -> %s", fnSysvar);
f.Open(fnSysvar, 0, 0); // rewrite file in ASCII
write("GenSysvars() -> %s", fnSysvar);
f.Open(fnSysvar, 0, 0); // rewrite file in ASCII
PutString(f, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
PutString(f, "<systemvariables version=\"4\">\n");
PutString(f, " <namespace name=\"\" comment=\"\">\n");
PutString(f, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
PutString(f, "<systemvariables version=\"4\">\n");
PutString(f, " <namespace name=\"\" comment=\"\">\n");
PutString(f, " <namespace name=\"Config\" comment=\"\">\n");
PutString(f, " <namespace name=\"Modbus\" comment=\"\">\n");
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"ms\" name=\"RequestTimeout\" comment=\"The maximum duration for a Modbus-UDP/-TCP request in milliseconds. After timeout a retransmission may be started (see MaxRetransmissionCount). Use `ping` to get the maximum latency to a device, double it and add 2-3 ms for processing.\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, (word)@sysvar::Config::Modbus::RequestTimeout);
PutString(f, "\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"1000\" maxValuePhys=\"1000\" />\n");
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"Port\" comment=\"\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, (word)@sysvar::Config::Modbus::Port);
PutString(f, "\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"65535\" maxValuePhys=\"65535\" />\n");
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"times\" name=\"MaxTransmissionCount\" comment=\"How often a retransmission of a request will be sent until it gets discarded and an error is thrown.\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, (byte)@sysvar::Config::Modbus::MaxTransmissionCount);
PutString(f, "\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"10\" maxValuePhys=\"10\" />\n");
PutString(f, " </namespace>\n");
PutString(f, " <namespace name=\"TcpIp\" comment=\"\">\n");
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"AdapterIndex\" comment=\"Index of network interface to use\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"2\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"20\" maxValuePhys=\"20\" />\n");
PutString(f, " </namespace>\n");
PutString(f, " </namespace>\n");
PutString(f, " <namespace name=\"Config\" comment=\"\">\n");
PutString(f, " <namespace name=\"Modbus\" comment=\"\">\n");
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"ms\" name=\"RequestTimeout\" comment=\"The maximum duration for a Modbus-UDP/-TCP request in milliseconds. After timeout a retransmission may be started (see MaxRetransmissionCount). Use `ping` to get the maximum latency to a device, double it and add 2-3 ms for processing.\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, (word)@sysvar::Config::Modbus::RequestTimeout);
PutString(f, "\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"1000\" maxValuePhys=\"1000\" />\n");
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"Port\" comment=\"\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, (word)@sysvar::Config::Modbus::Port);
PutString(f, "\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"65535\" maxValuePhys=\"65535\" />\n");
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"times\" name=\"MaxTransmissionCount\" comment=\"How often a retransmission of a request will be sent until it gets discarded and an error is thrown.\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, (byte)@sysvar::Config::Modbus::MaxTransmissionCount);
PutString(f, "\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"10\" maxValuePhys=\"10\" />\n");
PutString(f, " </namespace>\n");
PutString(f, " <namespace name=\"TcpIp\" comment=\"\">\n");
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"AdapterIndex\" comment=\"Index of network interface to use\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"2\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"20\" maxValuePhys=\"20\" />\n");
PutString(f, " </namespace>\n");
PutString(f, " </namespace>\n");
for (long net : gIpNets)
{
byte nett;
nett = net;
PutString(f, " <namespace name=\"Ethernet");
PutString(f, nett);
PutString(f, "\" comment=\"Subnet: 192.168.");
PutString(f, nett);
PutString(f, ".\">\n");
for (long net : gIpNets)
{
byte nett;
nett = net;
PutString(f, " <namespace name=\"Ethernet");
PutString(f, nett);
PutString(f, "\" comment=\"Subnet: 192.168.");
PutString(f, nett);
PutString(f, ".\">\n");
for (long ipN : gIpsSorted)
{
for (long ipN : gIpsSorted)
{
if (((ipN >> 16) & 0xFF) != net)
continue;
if (((ipN >> 16) & 0xFF) != net)
continue;
PutString(f, " <namespace name=\"Client_");
//PutString(f, netS);
//PutString(f, "_");
PutString(f, gIpsSorted[ipN].IpLsb);
PutString(f, "\" comment=\"Server with ip address '");
PutString(f, gIpsSorted[ipN].Ip);
PutString(f, "'\">\n");
PutString(f, " <namespace name=\"Client_");
//PutString(f, netS);
//PutString(f, "_");
PutString(f, gIpsSorted[ipN].IpLsb);
PutString(f, "\" comment=\"Server with ip address '");
PutString(f, gIpsSorted[ipN].Ip);
PutString(f, "'\">\n");
// Namespace Config
PutString(f, " <namespace name=\"Config\" comment=\"Configuration section for this server\">\n");
// IP
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"IP\" comment=\"The IP address of this server\" bitcount=\"8\" isSigned=\"true\" encoding=\"65001\" type=\"string\" startValue=\"");
PutString(f, gIpsSorted[ipN].Ip);
PutString(f, "\" />\n");
// Intveral
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"ms\" name=\"Interval\" comment=\"The interval with which the device will be queried\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"100\" minValue=\"10\" minValuePhys=\"10\" maxValue=\"10000\" maxValuePhys=\"10000\" />\n");
PutString(f, " </namespace>\n");
// Namespace Config
PutString(f, " <namespace name=\"Config\" comment=\"Configuration section for this server\">\n");
// IP
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"IP\" comment=\"The IP address of this server\" bitcount=\"8\" isSigned=\"true\" encoding=\"65001\" type=\"string\" startValue=\"");
PutString(f, gIpsSorted[ipN].Ip);
PutString(f, "\" />\n");
// Intveral
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"ms\" name=\"Interval\" comment=\"The interval with which the device will be queried\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"100\" minValue=\"10\" minValuePhys=\"10\" maxValue=\"10000\" maxValuePhys=\"10000\" />\n");
PutString(f, " </namespace>\n");
//Namespace Info
PutString(f, " <namespace name=\"Info\" comment=\"Some information about the device\">\n");
// Vendor
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"Vendor\" comment=\"The vendor of the device\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, (byte)gIpsSorted[ipN].Vendor);
PutString(f, "\">\n");
PutString(f, " <valuetable definesMinMax=\"true\">\n");
PutString(f, " <valuetableentry value=\"2\" description=\"BuR\" />\n");
PutString(f, " <valuetableentry value=\"23\" description=\"Wago\" />\n");
PutString(f, " </valuetable>\n");
PutString(f, " </variable>\n");
// SerialCode
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"SerialCode\" comment=\"The serial code of the server\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, gIpsSorted[ipN].SerialCode);
PutString(f, "\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"10000\" maxValuePhys=\"10000\" />\n");
// DeviceCode
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"DeviceCode\" comment=\"The device code of the server\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, gIpsSorted[ipN].DeviceCode);
PutString(f, "\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"10000\" maxValuePhys=\"10000\" />\n");
// Modules
gIpsSorted[ipN].DeviceIOs.Modules[strlen(gIpsSorted[ipN].DeviceIOs.Modules)-1] = 0;
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"Modules\" comment=\"The type and number of inputs of modules that are connected to the server\" bitcount=\"8\" isSigned=\"true\" encoding=\"65001\" type=\"string\" startValue=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.Modules);
PutString(f, "\" />\n");
// InputRegisters
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"InputRegisters\" comment=\"Number of input registers\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.InputRegisters);
PutString(f, "\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"123\" maxValuePhys=\"123\" />\n");
// InputBits
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"InputBits\" comment=\"Number of input bits\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.InputBits);
PutString(f, "\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"2000\" maxValuePhys=\"2000\" />\n");
// OutputRegisters
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"OutputRegisters\" comment=\"Number of output registers\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.OutputRegisters);
PutString(f, "\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"123\" maxValuePhys=\"123\" />\n");
// OutputBits
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"OutputBits\" comment=\"Number of output bits\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.OutputBits);
PutString(f, "\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"2000\" maxValuePhys=\"2000\" />\n");
PutString(f, " </namespace>\n");
//Namespace Info
PutString(f, " <namespace name=\"Info\" comment=\"Some information about the device\">\n");
// Vendor
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"Vendor\" comment=\"The vendor of the device\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, (byte)gIpsSorted[ipN].Vendor);
PutString(f, "\">\n");
PutString(f, " <valuetable definesMinMax=\"true\">\n");
PutString(f, " <valuetableentry value=\"2\" description=\"BuR\" />\n");
PutString(f, " <valuetableentry value=\"23\" description=\"Wago\" />\n");
PutString(f, " </valuetable>\n");
PutString(f, " </variable>\n");
// SerialCode
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"SerialCode\" comment=\"The serial code of the server\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, gIpsSorted[ipN].SerialCode);
PutString(f, "\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"10000\" maxValuePhys=\"10000\" />\n");
// DeviceCode
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"DeviceCode\" comment=\"The device code of the server\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, gIpsSorted[ipN].DeviceCode);
PutString(f, "\" minValue=\"1\" minValuePhys=\"1\" maxValue=\"10000\" maxValuePhys=\"10000\" />\n");
// Modules
gIpsSorted[ipN].DeviceIOs.Modules[strlen(gIpsSorted[ipN].DeviceIOs.Modules)-1] = 0;
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"Modules\" comment=\"The type and number of inputs of modules that are connected to the server\" bitcount=\"8\" isSigned=\"true\" encoding=\"65001\" type=\"string\" startValue=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.Modules);
PutString(f, "\" />\n");
// InputRegisters
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"InputRegisters\" comment=\"Number of input registers\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.InputRegisters);
PutString(f, "\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"123\" maxValuePhys=\"123\" />\n");
// InputBits
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"InputBits\" comment=\"Number of input bits\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.InputBits);
PutString(f, "\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"2000\" maxValuePhys=\"2000\" />\n");
// OutputRegisters
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"OutputRegisters\" comment=\"Number of output registers\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.OutputRegisters);
PutString(f, "\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"123\" maxValuePhys=\"123\" />\n");
// OutputBits
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"true\" valueSequence=\"false\" unit=\"\" name=\"OutputBits\" comment=\"Number of output bits\" bitcount=\"32\" isSigned=\"true\" encoding=\"65001\" type=\"int\" startValue=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.OutputBits);
PutString(f, "\" minValue=\"0\" minValuePhys=\"0\" maxValue=\"2000\" maxValuePhys=\"2000\" />\n");
PutString(f, " </namespace>\n");
// Namespace Data
PutString(f, " <namespace name=\"Data\" comment=\"The actual process image\">\n");
// InputRegisters
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"InputRegisters\" comment=\"The values of the input registers\" bitcount=\"9\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.InputRegisters);
PutString(f, "\" />\n");
// InputBits
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"InputBits\" comment=\"The state of the input bits\" bitcount=\"2\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.InputBits);
PutString(f, "\" />\n");
// OutputRegisters
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"OutputRegisters\" comment=\"The values of the output registers. Write here and the values will be sent to the device\" bitcount=\"9\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.OutputRegisters);
PutString(f, "\" />\n");
// OutputBits
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"OutputBits\" comment=\"The state of the output bits. Write here and the values will be sent to the device\" bitcount=\"2\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.OutputBits);
PutString(f, "\" />\n");
// Namespace Data
PutString(f, " <namespace name=\"Data\" comment=\"The actual process image\">\n");
// InputRegisters
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"InputRegisters\" comment=\"The values of the input registers\" bitcount=\"9\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.InputRegisters);
PutString(f, "\" />\n");
// InputBits
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"InputBits\" comment=\"The state of the input bits\" bitcount=\"2\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.InputBits);
PutString(f, "\" />\n");
// OutputRegisters
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"OutputRegisters\" comment=\"The values of the output registers. Write here and the values will be sent to the device\" bitcount=\"9\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.OutputRegisters);
PutString(f, "\" />\n");
// OutputBits
PutString(f, " <variable anlyzLocal=\"2\" readOnly=\"false\" valueSequence=\"false\" unit=\"\" name=\"OutputBits\" comment=\"The state of the output bits. Write here and the values will be sent to the device\" bitcount=\"2\" isSigned=\"true\" encoding=\"65001\" type=\"intarray\" arrayLength=\"");
PutString(f, gIpsSorted[ipN].DeviceIOs.OutputBits);
PutString(f, "\" />\n");
PutString(f, " </namespace>\n");
PutString(f, " </namespace>\n");
}
PutString(f, " </namespace>\n");
}
PutString(f, " </namespace>\n");
PutString(f, " </namespace>\n");
}
PutString(f, " </namespace>\n");
}
PutString(f, " </namespace>\n");
PutString(f, "</systemvariables>\n");
f.Close();
PutString(f, " </namespace>\n");
PutString(f, "</systemvariables>\n");
f.Close();
}
// Generate the Database
/// <Step4>
void GenDbc()
{
write("GenDbc() -> %s", fnDbc);
f.Open(fnDbc, 0, 0); // rewrite file in ASCII
write("GenDbc() -> %s", fnDbc);
f.Open(fnDbc, 0, 0); // rewrite file in ASCII
PutString(f, "VERSION \"\"\n\n\n");
PutString(f, "NS_ :\n");
PutString(f, " NS_DESC_\n");
PutString(f, " CM_\n");
PutString(f, " BA_DEF_\n");
PutString(f, " BA_\n");
PutString(f, " VAL_\n");
PutString(f, " CAT_DEF_\n");
PutString(f, " CAT_\n");
PutString(f, " FILTER\n");
PutString(f, " BA_DEF_DEF_\n");
PutString(f, " EV_DATA_\n");
PutString(f, " ENVVAR_DATA_\n");
PutString(f, " SGTYPE_\n");
PutString(f, " SGTYPE_VAL_\n");
PutString(f, " BA_DEF_SGTYPE_\n");
PutString(f, " BA_SGTYPE_\n");
PutString(f, " SIG_TYPE_REF_\n");
PutString(f, " VAL_TABLE_\n");
PutString(f, " SIG_GROUP_\n");
PutString(f, " SIG_VALTYPE_\n");
PutString(f, " SIGTYPE_VALTYPE_\n");
PutString(f, " BO_TX_BU_\n");
PutString(f, " BA_DEF_REL_\n");
PutString(f, " BA_REL_\n");
PutString(f, " BA_DEF_DEF_REL_\n");
PutString(f, " BU_SG_REL_\n");
PutString(f, " BU_EV_REL_\n");
PutString(f, " BU_BO_REL_\n");
PutString(f, " SG_MUL_VAL_\n");
PutString(f, "\n");
PutString(f, "BS_:\n");
PutString(f, "\nBU_:");
PutString(f, "VERSION \"\"\n\n\n");
PutString(f, "NS_ :\n");
PutString(f, " NS_DESC_\n");
PutString(f, " CM_\n");
PutString(f, " BA_DEF_\n");
PutString(f, " BA_\n");
PutString(f, " VAL_\n");
PutString(f, " CAT_DEF_\n");
PutString(f, " CAT_\n");
PutString(f, " FILTER\n");
PutString(f, " BA_DEF_DEF_\n");
PutString(f, " EV_DATA_\n");
PutString(f, " ENVVAR_DATA_\n");
PutString(f, " SGTYPE_\n");
PutString(f, " SGTYPE_VAL_\n");
PutString(f, " BA_DEF_SGTYPE_\n");
PutString(f, " BA_SGTYPE_\n");
PutString(f, " SIG_TYPE_REF_\n");
PutString(f, " VAL_TABLE_\n");
PutString(f, " SIG_GROUP_\n");
PutString(f, " SIG_VALTYPE_\n");
PutString(f, " SIGTYPE_VALTYPE_\n");
PutString(f, " BO_TX_BU_\n");
PutString(f, " BA_DEF_REL_\n");
PutString(f, " BA_REL_\n");
PutString(f, " BA_DEF_DEF_REL_\n");
PutString(f, " BU_SG_REL_\n");
PutString(f, " BU_EV_REL_\n");
PutString(f, " BU_BO_REL_\n");
PutString(f, " SG_MUL_VAL_\n");
PutString(f, "\n");
PutString(f, "BS_:\n");
PutString(f, "\nBU_:");
for (long ipN : gIpsSorted)
{
PutString(f, " Client_");
//PutString(f, gIpsSorted[ipN].IpNet);
//PutString(f, "_");
PutString(f, gIpsSorted[ipN].IpLsb);
}
PutString(f, "\n\n\n\n");
PutString(f, "BA_DEF_ BU_ \"NodeLayerModules\" STRING ;\n");
PutString(f, "BA_DEF_ \"DBName\" STRING ;\n");
PutString(f, "BA_DEF_ \"BusType\" STRING ;\n");
PutString(f, "BA_DEF_DEF_ \"NodeLayerModules\" \"Ethernet_IL.DLL\";\n");
PutString(f, "BA_DEF_DEF_ \"DBName\" \"\";\n");
PutString(f, "BA_DEF_DEF_ \"BusType\" \"Ethernet\";\n");
PutString(f, "BA_ \"BusType\" \"Ethernet\";\n");
PutString(f, "BA_ \"DBName\" \"");
PutString(f, name);
PutString(f, "\";\n");
for (long ipN : gIpsSorted)
{
PutString(f, " Client_");
//PutString(f, gIpsSorted[ipN].IpNet);
//PutString(f, "_");
PutString(f, gIpsSorted[ipN].IpLsb);
}
PutString(f, "\n\n\n\n");
PutString(f, "BA_DEF_ BU_ \"NodeLayerModules\" STRING ;\n");
PutString(f, "BA_DEF_ \"DBName\" STRING ;\n");
PutString(f, "BA_DEF_ \"BusType\" STRING ;\n");
PutString(f, "BA_DEF_DEF_ \"NodeLayerModules\" \"Ethernet_IL.DLL\";\n");
PutString(f, "BA_DEF_DEF_ \"DBName\" \"\";\n");
PutString(f, "BA_DEF_DEF_ \"BusType\" \"Ethernet\";\n");
PutString(f, "BA_ \"BusType\" \"Ethernet\";\n");
PutString(f, "BA_ \"DBName\" \"");
PutString(f, name);
PutString(f, "\";\n");
f.Close();
f.Close();
}
@ -494,16 +494,16 @@ void GenDbc()
/// <zzzModbus>
void OnModbusClientPanics(enum FatalErrors reason)
{
writeLineEx(0, 4, "<%NODE_NAME%> FATAL! %d", reason);
switch(reason)
{
case ParsingBuffer:
case ModbusPackageWasSplit:
case DeviceCodeUnknown:
case VendorIdUnknown:
case ConnectionError:
break;
}
writeLineEx(0, 4, "<%NODE_NAME%> FATAL! %d", reason);
switch(reason)
{
case ParsingBuffer:
case ModbusPackageWasSplit:
case DeviceCodeUnknown:
case VendorIdUnknown:
case ConnectionError:
break;
}
}
/// <zzzModbus>
void OnModbusWriteBitFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}

View file

@ -2,83 +2,83 @@
includes
{
#include "include\ModbusEilDllClientCommon.cin"
#include "include\ModbusFunctions.cin"
#include "include\ModbusEilDllClientCommon.cin"
#include "include\ModbusFunctions.cin"
}
variables
{
msTimer gtRead;
//message Client_2_RecBits rb;
//message Client_2_Ex81 ex;
msTimer gtRead;
//message Client_2_RecBits rb;
//message Client_2_Ex81 ex;
}
// Get information of local network interface such like ip address
on preStart
{
writeClear(0);
setStartdelay(10);
OutputDebugLevel = Debug;
writeClear(0);
setStartdelay(10);
OutputDebugLevel = Debug;
}
on start
{
ModbusInit();
ModbusInit();
// Read serial code, additional stuff is done in OnModbusReceiveRegisters
// This has to be done by MakeConfig to properly size the arrays
//ModbusReadRegisters(0x2011, 1);
//ModbusReadRegisters(0x2012, 1);
//ModbusReadRegisters(0x2030, 65);
//ModbusReadRegisters(0x2031, 64);
//ModbusReadRegisters(0x2032, 64);
//ModbusReadRegisters(0x2033, 63);
//if (@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits > 0)
// ModbusReadBits(0x200, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits);
// Read serial code, additional stuff is done in OnModbusReceiveRegisters
// This has to be done by MakeConfig to properly size the arrays
//ModbusReadRegisters(0x2011, 1);
//ModbusReadRegisters(0x2012, 1);
//ModbusReadRegisters(0x2030, 65);
//ModbusReadRegisters(0x2031, 64);
//ModbusReadRegisters(0x2032, 64);
//ModbusReadRegisters(0x2033, 63);
//if (@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits > 0)
// ModbusReadBits(0x200, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits);
setTimerCyclic(gtRead, 1, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config::Interval);
setTimerCyclic(gtRead, 1, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config::Interval);
}
// Modbus events ----------------------------------------------------------------------
void OnModbusReadBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap)
{
word i;
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;
}
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;
}
}
void OnModbusReadRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap)
{
byte i;
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;
}
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;
}
}
void OnModbusWriteBitFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap)
@ -103,51 +103,51 @@ void OnModbusWriteRegistersFailed(enum ModbusRequestError error, enum ModbusExce
void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[], struct ModbusReqRead mbreq)
{
word i;
word i;
switch(mbreq.Address)
{
case 0x200: // set output bits
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits[i] = bitStatus[i];
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
break;
default: // set input bits
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputBits");
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputBits; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::InputBits[i] = bitStatus[i];
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputBits");
break;
}
switch(mbreq.Address)
{
case 0x200: // set output bits
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits[i] = bitStatus[i];
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
break;
default: // set input bits
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputBits");
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputBits; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::InputBits[i] = bitStatus[i];
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputBits");
break;
}
}
void OnModbusReadRegistersSuccess(struct ModbusResReceiveRegisters mbres, struct ModbusReqRead mbreq)
{
char str[20*5];
long fehler;
byte i;
char str[20*5];
long fehler;
byte i;
switch (mbreq.Address)
{
case 0x200: // set output registers
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputRegisters");
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputRegisters[i] = mbres.Data[i];
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputRegisters");
break;
case 0x000: // set output registers
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputRegisters");
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::InputRegisters[i] = mbres.Data[i];
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputRegisters");
break;
default:
// Not recognized
dbin_to_strhex(mbres.Data, str);
writeLineEx(0, 1, "<%NODE_NAME%> OnModbusReceiveRegisters: Received %d bytes at 0x%04X: %s", mbres.ByteCount, mbreq.Address, str);
break;
}
switch (mbreq.Address)
{
case 0x200: // set output registers
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputRegisters");
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputRegisters[i] = mbres.Data[i];
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputRegisters");
break;
case 0x000: // set output registers
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputRegisters");
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::InputRegisters[i] = mbres.Data[i];
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "InputRegisters");
break;
default:
// Not recognized
dbin_to_strhex(mbres.Data, str);
writeLineEx(0, 1, "<%NODE_NAME%> OnModbusReceiveRegisters: Received %d bytes at 0x%04X: %s", mbres.ByteCount, mbreq.Address, str);
break;
}
}
void OnModbusWriteBitSuccess(struct ModbusResConfirmSingle mbc)
@ -168,88 +168,88 @@ void OnModbusWriteMasksSuccess(struct ModbusResConfirmMasks mbc)
void OnModbusClientPanics(enum FatalErrors reason)
{
writeLineEx(0, 4, "<%NODE_NAME%> FATAL! %d", reason);
switch(reason)
{
case ParsingBuffer:
case ModbusPackageWasSplit:
case DeviceCodeUnknown:
case VendorIdUnknown:
runError(1001, reason);
break;
case ConnectionError:
gtRead.Cancel();
break;
}
writeLineEx(0, 4, "<%NODE_NAME%> FATAL! %d", reason);
switch(reason)
{
case ParsingBuffer:
case ModbusPackageWasSplit:
case DeviceCodeUnknown:
case VendorIdUnknown:
runError(1001, reason);
break;
case ConnectionError:
gtRead.Cancel();
break;
}
}
// Key events -------------------------------------------------------------------------
on timer gtRead
{
if (@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters > 0)
ModbusReadRegisters(0x0000, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters);
if (@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputBits > 0)
ModbusReadBits(@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters * 2, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputBits);
if (@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters > 0)
ModbusReadRegisters(0x0000, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters);
if (@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputBits > 0)
ModbusReadBits(@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters * 2, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputBits);
}
/*
on sysvar %BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits
{
word firstBitAddr, count, i;
byte bitStatus[1968];
word firstBitAddr, count, i;
byte bitStatus[1968];
firstBitAddr = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters * 2;
count = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits;
firstBitAddr = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::InputRegisters * 2;
count = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits;
for (i = 0; i < count; i++)
bitStatus[i] = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits[i];
for (i = 0; i < count; i++)
bitStatus[i] = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits[i];
ModbusWriteBitsB(firstBitAddr, count, bitStatus);
ModbusWriteBitsB(firstBitAddr, count, bitStatus);
}
on sysvar %BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputRegisters
{
word count, i;
word regValues[123];
word count, i;
word regValues[123];
count = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters;
count = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters;
for (i = 0; i < count; i++)
regValues[i] = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputRegisters[i];
for (i = 0; i < count; i++)
regValues[i] = @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputRegisters[i];
ModbusWriteRegisters(0x000, count, regValues);
ModbusWriteRegisters(0x000, count, regValues);
}*/
on sysvar %BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config::Interval
{
if (@this <= 0)
gtRead.Cancel();
else
setTimerCyclic(gtRead, @this);
if (@this <= 0)
gtRead.Cancel();
else
setTimerCyclic(gtRead, @this);
}
on key '+'
{
word i;
word i;
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits[i] = 1;
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits[i] = 1;
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
}
on key '-'
{
word i;
word i;
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits[i] = 0;
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
sysBeginVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
for (i = 0; i < @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits; i++)
@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data::OutputBits[i] = 0;
sysEndVariableStructUpdate("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Data", "OutputBits");
}
/*
on message rb
{
write("Hello! Read Bits =)");
write("Hello! Read Bits =)");
}
on message ex
{
write("Oh no, exception!");
write("Oh no, exception!");
}*/

View file

@ -2,181 +2,181 @@
variables
{
enum DebugLvl {
Debug = 0x00,
AlgoDebug = 0x02,
ConnDebug = 0x04,
MbDebug = 0x07,
enum DebugLvl {
Debug = 0x00,
AlgoDebug = 0x02,
ConnDebug = 0x04,
MbDebug = 0x07,
Info = 0x10,
AlgoInfo = 0x12,
ConnInfo = 0x14,
MbInfo = 0x17,
Info = 0x10,
AlgoInfo = 0x12,
ConnInfo = 0x14,
MbInfo = 0x17,
Warning = 0x20,
AlgoWarning = 0x22,
ConnWarning = 0x24,
MbWarning = 0x27,
Warning = 0x20,
AlgoWarning = 0x22,
ConnWarning = 0x24,
MbWarning = 0x27,
Error = 0x30,
AlgoError = 0x32,
ConnError = 0x34,
MbError = 0x37
Error = 0x30,
AlgoError = 0x32,
ConnError = 0x34,
MbError = 0x37
};
char DebugLvlStr[4][8] = {"Debug", "Info", "Warning", "Error"};
enum DebugLvl OutputDebugLevel = Debug;
char DebugLvlStr[4][8] = {"Debug", "Info", "Warning", "Error"};
enum DebugLvl OutputDebugLevel = Debug;
}
/// <writeDbg>
void writeDbgFormat(byte lVl, char msg[], char format[])
{
strncpy(msg, "<%NODE_NAME%> ", elCount(msg));
strncat(msg, DebugLvlStr[lVl], elCount(msg));
strncat(msg, ": ", elCount(msg));
strncat(msg, format, elCount(msg)-strlen(msg));
strncpy(msg, "<%NODE_NAME%> ", elCount(msg));
strncat(msg, DebugLvlStr[lVl], elCount(msg));
strncat(msg, ": ", elCount(msg));
strncat(msg, format, elCount(msg)-strlen(msg));
}
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[])
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg);
}
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], char string[])
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string);
}
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], char string1[], char string2[])
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string1, string2);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string1, string2);
}
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num, char string[])
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num, string);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num, string);
}
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], char string[], long num)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string, num);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, string, num);
}
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num1)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1);
}
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, char string[])
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, string);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, string);
}
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2);
}
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, num3);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, num3);
}
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3, long num4)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, num3, num4);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, num3, num4);
}
/// <writeDbg>
void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3, long num4, long num5, long num6)
{
char msg[500];
byte lVl;
char msg[500];
byte lVl;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
if (lvl < OutputDebugLevel)
return;
lVl = (byte)lvl >> 4;
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, num3, num4, num5, num6);
writeDbgFormat(lVl, msg, format);
writeLineEx(1, lVl, msg, num1, num2, num3, num4, num5, num6);
}
@ -184,80 +184,80 @@ void writeDbg(enum DebugLvl lvl, char format[], long num1, long num2, long num3,
void bin_to_strhex(byte bin[], char result[])
{
char hex_str[17] = "0123456789ABCDEF";
word i;
word binsz;
binsz = elCount(bin);
if (binsz > 20)
binsz = 20;
char hex_str[17] = "0123456789ABCDEF";
word i;
word binsz;
binsz = elCount(bin);
if (binsz > 20)
binsz = 20;
for (i = 0; i < binsz; i++)
{
result[i * 3 + 0] = hex_str[bin[i] >> 4 ];
result[i * 3 + 1] = hex_str[bin[i] & 0x0F];
result[i * 3 + 2] = ' ';
}
for (i = 0; i < binsz; i++)
{
result[i * 3 + 0] = hex_str[bin[i] >> 4 ];
result[i * 3 + 1] = hex_str[bin[i] & 0x0F];
result[i * 3 + 2] = ' ';
}
if (elCount(bin) > 20) // trailing "..."
{
result[56] = '.';
result[57] = '.';
result[58] = '.';
}
result[binsz * 3 - 1] = 0;
if (elCount(bin) > 20) // trailing "..."
{
result[56] = '.';
result[57] = '.';
result[58] = '.';
}
result[binsz * 3 - 1] = 0;
}
void hbin_to_strhex(byte bin[], char result[])
{
char hex_str[17] = "0123456789ABCDEF";
word i;
word binsz;
binsz = elCount(bin);
if (binsz > 20)
binsz = 20;
char hex_str[17] = "0123456789ABCDEF";
word i;
word binsz;
binsz = elCount(bin);
if (binsz > 20)
binsz = 20;
for (i = 0; i < binsz; i++)
{
result[i * 2 + 0] = hex_str[bin[i] & 0x0F];
result[i * 2 + 1] = ' ';
}
for (i = 0; i < binsz; i++)
{
result[i * 2 + 0] = hex_str[bin[i] & 0x0F];
result[i * 2 + 1] = ' ';
}
if (elCount(bin) > 20) // trailing "..."
{
result[36] = '.';
result[37] = '.';
result[38] = '.';
}
result[binsz * 2 - 1] = 0;
if (elCount(bin) > 20) // trailing "..."
{
result[36] = '.';
result[37] = '.';
result[38] = '.';
}
result[binsz * 2 - 1] = 0;
}
void dbin_to_strhex(word bin[], char result[])
{
char hex_str[17] = "0123456789ABCDEF";
word i;
word binsz;
byte offset;
binsz = elCount(bin);
if (binsz > 20)
binsz = 20;
char hex_str[17] = "0123456789ABCDEF";
word i;
word binsz;
byte offset;
binsz = elCount(bin);
if (binsz > 20)
binsz = 20;
for (i = 0; i < binsz; i++)
{
result[i * 5 + 0] = hex_str[(bin[i] >> 12) & 0x0F];
result[i * 5 + 1] = hex_str[(bin[i] >> 8) & 0x0F];
result[i * 5 + 2] = hex_str[(bin[i] >> 4) & 0x0F];
result[i * 5 + 3] = hex_str[(bin[i] ) & 0x0F];
result[i * 5 + 4] = ' ';
}
for (i = 0; i < binsz; i++)
{
result[i * 5 + 0] = hex_str[(bin[i] >> 12) & 0x0F];
result[i * 5 + 1] = hex_str[(bin[i] >> 8) & 0x0F];
result[i * 5 + 2] = hex_str[(bin[i] >> 4) & 0x0F];
result[i * 5 + 3] = hex_str[(bin[i] ) & 0x0F];
result[i * 5 + 4] = ' ';
}
if (elCount(bin) > 20) // trailing "..."
{
result[96] = '.';
result[97] = '.';
result[98] = '.';
}
result[(byte)(binsz * 2.5) - 1] = 0;
if (elCount(bin) > 20) // trailing "..."
{
result[96] = '.';
result[97] = '.';
result[98] = '.';
}
result[(byte)(binsz * 2.5) - 1] = 0;
}

View file

@ -1,119 +1,119 @@
/*@!Encoding:1252*/
includes
{
#include "Common.cin"
#include "TcpUdpCommon.cin"
#include "Common.cin"
#include "TcpUdpCommon.cin"
}
variables
{
long gPacket;
msTimer gtArp;
byte gLocalMac[6];
byte gRemoteMac[6];
dword gLocalIP = 0xC0A80101;
long gPacket;
msTimer gtArp;
byte gLocalMac[6];
byte gRemoteMac[6];
dword gLocalIP = 0xC0A80101;
}
word EilConnectTo(char Remote_IP[], word remotePort)
{
dword remoteIp;
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();
}
// 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);
return EilConnectTo(remoteIp, remotePort);
}
word EilConnectTo(dword remoteIp, word remotePort)
{
long error;
byte broadcastMac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
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;
}
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);
// 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;
}
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);
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");
EthReceivePacket("OnEthReceivePacket");
EthCompletePacket(gPacket);
EthOutputPacket(gPacket);
EthReleasePacket(gPacket);
gSocketState = NULL;
gtArp.set(@sysvar::Config::Modbus::RequestTimeout);
return 0;
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;
}
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);
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;
gSocketState = OK;
}
void EilDisconnect()
{
if (gPacket != 0)
{
EthReleasePacket(gPacket);
gPacket = 0;
}
gSocketState = CLOSED;
if (gPacket != 0)
{
EthReleasePacket(gPacket);
gPacket = 0;
}
gSocketState = CLOSED;
}
@ -123,45 +123,45 @@ void EilRecv()
byte EilSnd(byte buffer[], word length)
{
char str[20*3];
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;
}
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);
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);
EthResizeToken(gPacket, "udp", "data" , length*8);
EthSetTokenData(gPacket, "udp", "data" , length, buffer);
EthCompletePacket(gPacket);
EthOutputPacket(gPacket);
EthCompletePacket(gPacket);
EthOutputPacket(gPacket);
return 0;
return 0;
}
long EilGetLastConnectionError(char string[])
{
EthGetLastErrorText(elCount(string), string);
return EthGetLastError();
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);
gSocketState = ERROR;
writeDbg(ConnError, "No (valid) ARP response detected. The host seems to be offline!");
OnModbusClientPanics(ConnectionError);
}

View file

@ -1,119 +1,119 @@
/*@!Encoding:1252*/
includes
{
#include "Common.cin"
#include "TcpUdpCommon.cin"
#include "Common.cin"
#include "TcpUdpCommon.cin"
}
variables
{
long gPacket;
msTimer gtArp;
byte gLocalMac[6];
byte gRemoteMac[6];
dword gLocalIP = 0xC0A80101;
long gPacket;
msTimer gtArp;
byte gLocalMac[6];
byte gRemoteMac[6];
dword gLocalIP = 0xC0A80101;
}
word EilConnectTo(char Remote_IP[], word remotePort)
{
dword remoteIp;
dword remoteIp;
// Convert IP string to Number
remoteIp = IpGetAddressAsNumber(Remote_IP);
if (remoteIp == ~1)
{
writeDbg(ConnError, "EilConnectTo: invalid server Ip address!");
OnModbusClientPanics(ConnectionError);
return ipGetLastError();
}
// Convert IP string to Number
remoteIp = IpGetAddressAsNumber(Remote_IP);
if (remoteIp == ~1)
{
writeDbg(ConnError, "EilConnectTo: invalid server Ip address!");
OnModbusClientPanics(ConnectionError);
return ipGetLastError();
}
return EilConnectTo(remoteIp, remotePort);
return EilConnectTo(remoteIp, remotePort);
}
word EilConnectTo(dword remoteIp, word remotePort)
{
long error;
byte broadcastMac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
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;
}
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);
// 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;
}
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);
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");
EthReceivePacket("OnEthReceivePacket");
EthCompletePacket(gPacket);
EthOutputPacket(gPacket);
EthReleasePacket(gPacket);
gSocketState = NULL;
gtArp.set(@sysvar::Config::Modbus::RequestTimeout);
return 0;
EthCompletePacket(gPacket);
EthOutputPacket(gPacket);
EthReleasePacket(gPacket);
gSocketState = NULL;
gtArp.set(@sysvar::Config::Modbus::RequestTimeout);
return 0;
}
void EilConnectTo2()
{
gPacket = EthInitPacket("modbus");
if (gPacket == 0)
{
gSocketState = ERROR;
writeDbg(ConnError, "EthInitPacket: Could not create Modbus packet: %d", EthGetLastError());
OnModbusClientPanics(ConnectionError);
return;
}
gPacket = EthInitPacket("modbus");
if (gPacket == 0)
{
gSocketState = ERROR;
writeDbg(ConnError, "EthInitPacket: Could not create Modbus 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); // we dont need those
//EthSetTokenInt(gPacket, "udp", "destination" , 502); // we do use modbus defaults
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); // we dont need those
//EthSetTokenInt(gPacket, "udp", "destination" , 502); // we do use modbus defaults
gSocketState = OK;
gSocketState = OK;
}
void EilDisconnect()
{
if (gPacket != 0)
{
EthReleasePacket(gPacket);
gPacket = 0;
}
gSocketState = CLOSED;
if (gPacket != 0)
{
EthReleasePacket(gPacket);
gPacket = 0;
}
gSocketState = CLOSED;
}
@ -123,45 +123,45 @@ void EilRecv()
byte EilSnd(byte buffer[], word length)
{
char str[20*3];
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;
}
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);
bin_to_strhex(buffer, str);
writeDbg(ConnDebug, "EilSnd: %s (Länge: %d)", str, length);
EthResizeToken(gPacket, "modbus", "data" , length*8);
EthSetTokenData(gPacket, "modbus", "data" , length, buffer);
EthResizeToken(gPacket, "modbus", "data" , length*8);
EthSetTokenData(gPacket, "modbus", "data" , length, buffer);
EthCompletePacket(gPacket);
EthOutputPacket(gPacket);
EthCompletePacket(gPacket);
EthOutputPacket(gPacket);
return 0;
return 0;
}
long EilGetLastConnectionError(char string[])
{
EthGetLastErrorText(elCount(string), string);
return EthGetLastError();
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);
gSocketState = ERROR;
writeDbg(ConnError, "No (valid) ARP response detected. The host seems to be offline!");
OnModbusClientPanics(ConnectionError);
}

File diff suppressed because it is too large Load diff

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<breakpoints>
<breakpoint Active="true" HaltsSimulation="true" AbsoluteLine="352" Function="" RelativeLine="9" />
</breakpoints>

View file

@ -2,63 +2,63 @@
includes
{
#include "EilCommon.cin"
#include "ModbusClientCommon.cin"
#include "EilCommon.cin"
#include "ModbusClientCommon.cin"
}
void ModbusConnectTo(char Remote_IP[], word Remote_Port)
{
EilConnectTo(Remote_IP, Remote_Port);
EilConnectTo(Remote_IP, Remote_Port);
}
void ModbusConnectTo(dword Remote_IP, word Remote_Port)
{
EilConnectTo(Remote_IP, Remote_Port);
EilConnectTo(Remote_IP, Remote_Port);
}
byte ModbusSnd(byte buffer[], word length)
{
return EilSnd(buffer, length);
return EilSnd(buffer, length);
}
void ModbusRecv()
{
EilRecv();
EilRecv();
}
void ModbusDisconnect()
{
EilDisconnect();
EilDisconnect();
}
long ModbusGetLastConnectionError(char string[])
{
return EilGetLastConnectionError(string);
return EilGetLastConnectionError(string);
}
void OnEthReceivePacket(long channel, long dir, long packet)
{
byte buffer[gMaxPacketLength];
long size;
byte buffer[gMaxPacketLength];
long size;
if (dir == TX)
return;
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, "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);
}
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);
}
}

View file

@ -2,60 +2,60 @@
includes
{
#include "EilDllCommon.cin"
#include "ModbusClientCommon.cin"
#include "EilDllCommon.cin"
#include "ModbusClientCommon.cin"
}
void ModbusConnectTo(char Remote_IP[], word Remote_Port)
{
EilConnectTo(Remote_IP, Remote_Port);
EilConnectTo(Remote_IP, Remote_Port);
}
void ModbusConnectTo(dword Remote_IP, word Remote_Port)
{
EilConnectTo(Remote_IP, Remote_Port);
EilConnectTo(Remote_IP, Remote_Port);
}
byte ModbusSnd(byte buffer[], word length)
{
return EilSnd(buffer, length);
return EilSnd(buffer, length);
}
void ModbusRecv()
{
EilRecv();
EilRecv();
}
void ModbusDisconnect()
{
EilDisconnect();
EilDisconnect();
}
long ModbusGetLastConnectionError(char string[])
{
return EilGetLastConnectionError(string);
return EilGetLastConnectionError(string);
}
void OnEthReceivePacket(long channel, long dir, long packet)
{
if (dir == TX)
return;
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, "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
{
OnModbusReceive2(packet);
}
if (EthGetTokenInt(packet, "ipv4", "protocol") == 0x11 && EthGetTokenInt(packet, "ipv4", "source") == gRemoteIP) // if this is a UDP package from our server
{
OnModbusReceive2(packet);
}
}

View file

@ -1,86 +1,86 @@
/*@!Encoding:1252*/
variables
{
enum Vendor
{
BuR = 2,
Wago = 23
};
enum Vendor
{
BuR = 2,
Wago = 23
};
struct deviceIOs
{
byte InputRegisters;
word InputBits;
byte OutputRegisters;
word OutputBits;
char Modules[1024];
};
struct deviceIOs
{
byte InputRegisters;
word InputBits;
byte OutputRegisters;
word OutputBits;
char Modules[1024];
};
}
void ParseDeviceCode(word dev, enum Vendor vendor, struct deviceIOs dios)
{
byte input;
byte numChannels;
char module[10];
byte input;
byte numChannels;
char module[10];
switch(vendor)
{
case Wago: // if this is a Wago device
switch(vendor)
{
case Wago: // if this is a Wago device
if (dev & 0x8000) // Digital Module
{
numChannels = (dev >> 8) & 0x007F;
if (dev & 0x8000) // Digital Module
{
numChannels = (dev >> 8) & 0x007F;
if (dev & 0x0001) // Input Module
{
input = 1;
strncpy(module, "DI%d,", elCount(module));
dios.InputBits += numChannels;
}
else if (dev & 0x0002) // Output Module
{
input = 0;
strncpy(module, "DO%d,", elCount(module));
dios.OutputBits += numChannels;
}
else // blööd
{
writeDbg(AlgoError, "ParseDeviceCode: Device code 0x%X cannot be decoded", dev);
OnModbusClientPanics(DeviceCodeUnknown);
}
}
else
{
switch (dev)
{
case 881: // devices that have no inputs/outputs
return;
case 477: // devices that have 2 outputs
input = 0;
numChannels = 2;
break;
default: // unknown device. Ouch!
writeDbg(AlgoInfo, "Connected device: 750-%d", dev);
return;
}
if (input)
{
strncpy(module, "AI%d,", elCount(module));
dios.InputRegisters += numChannels;
}
else
{
strncpy(module, "AO%d,", elCount(module));
dios.OutputRegisters += numChannels;
}
}
break; // switch(vendor)
default:
writeDbg(AlgoError, "ParseDeviceCode: Unknown vendor id: %d", vendor);
OnModbusClientPanics(VendorIdUnknown);
return;
}
if (dev & 0x0001) // Input Module
{
input = 1;
strncpy(module, "DI%d,", elCount(module));
dios.InputBits += numChannels;
}
else if (dev & 0x0002) // Output Module
{
input = 0;
strncpy(module, "DO%d,", elCount(module));
dios.OutputBits += numChannels;
}
else // blööd
{
writeDbg(AlgoError, "ParseDeviceCode: Device code 0x%X cannot be decoded", dev);
OnModbusClientPanics(DeviceCodeUnknown);
}
}
else
{
switch (dev)
{
case 881: // devices that have no inputs/outputs
return;
case 477: // devices that have 2 outputs
input = 0;
numChannels = 2;
break;
default: // unknown device. Ouch!
writeDbg(AlgoInfo, "Connected device: 750-%d", dev);
return;
}
if (input)
{
strncpy(module, "AI%d,", elCount(module));
dios.InputRegisters += numChannels;
}
else
{
strncpy(module, "AO%d,", elCount(module));
dios.OutputRegisters += numChannels;
}
}
break; // switch(vendor)
default:
writeDbg(AlgoError, "ParseDeviceCode: Unknown vendor id: %d", vendor);
OnModbusClientPanics(VendorIdUnknown);
return;
}
snprintf(module, elCount(module), module, numChannels);
strncat(dios.Modules, module, elCount(dios.Modules));
snprintf(module, elCount(module), module, numChannels);
strncat(dios.Modules, module, elCount(dios.Modules));
}

View file

@ -1,135 +1,135 @@
/*@!Encoding:1252*/
variables
{
// A normal Modbus Application Header. Every Modbus Packet begins with these 7 (+FuncCode) Bytes
_align(1) struct ModbusApHeader
{
word TxID;
word Protocol;
word Length;
byte UnitID;
byte FuncCode;
};
// Read Data from the host. We only need the start address and the number of bits/registers we want to read
_align(1) struct ModbusReqRead
{
word Address;
word Count;
};
// Write a single value to a bit/register
_align(1) struct ModbusReqWriteSingle
{
word Address;
word Value;
};
// Write several values to a bit/register starting with Address
_align(1) struct ModbusReqWriteBits
{
word Address;
word Count;
byte ByteCount;
byte Data[246]; // Max length: 1968 bits
};
// Write several values to bits starting with Address
_align(1) struct ModbusReqWriteRegisters
{
word Address;
word Count;
byte ByteCount;
word Data[123]; // Max length: 123 registers
};
// Write AND and OR masks to a holding register
_align(1) struct ModbusReqWriteMasks
{
word Address;
word And;
word Or;
};
// Read and write multiple registers
_align(1) struct ModbusReqReadWriteRegisters
{
word ReadAddress;
word ReadCount;
word WriteAddress;
word WriteCount;
byte ByteCount;
word Data[121]; // Max length: 123-2 registers
};
// Receive several bit values
_align(1) struct ModbusResReceiveBits
{
byte ByteCount;
byte Data[250]; // Max length: 2000 bits
};
// Receive several register values
_align(1) struct ModbusResReceiveRegisters
{
byte ByteCount;
word Data[125]; // Max length: 125 registers
};
// Confirm the write of a single bit/register
_align(1) struct ModbusResConfirmSingle
{
word Address;
word Value;
};
// Confirm the write of several bits/registers
_align(1) struct ModbusResConfirmMultiple
{
word Address;
word Count;
};
// Confirm the write of AND and OR mask
_align(1) struct ModbusResConfirmMasks
{
word Address;
word And;
word Or;
};
enum ModbusRequestError
{
Exception,
Timeout,
FinalTimeout
// A normal Modbus Application Header. Every Modbus Packet begins with these 7 (+FuncCode) Bytes
_align(1) struct ModbusApHeader
{
word TxID;
word Protocol;
word Length;
byte UnitID;
byte FuncCode;
};
// Read Data from the host. We only need the start address and the number of bits/registers we want to read
_align(1) struct ModbusReqRead
{
word Address;
word Count;
};
// Write a single value to a bit/register
_align(1) struct ModbusReqWriteSingle
{
word Address;
word Value;
};
// Write several values to a bit/register starting with Address
_align(1) struct ModbusReqWriteBits
{
word Address;
word Count;
byte ByteCount;
byte Data[246]; // Max length: 1968 bits
};
// Write several values to bits starting with Address
_align(1) struct ModbusReqWriteRegisters
{
word Address;
word Count;
byte ByteCount;
word Data[123]; // Max length: 123 registers
};
// Write AND and OR masks to a holding register
_align(1) struct ModbusReqWriteMasks
{
word Address;
word And;
word Or;
};
// Read and write multiple registers
_align(1) struct ModbusReqReadWriteRegisters
{
word ReadAddress;
word ReadCount;
word WriteAddress;
word WriteCount;
byte ByteCount;
word Data[121]; // Max length: 123-2 registers
};
enum ModbusException
{
None = 0x00,
IllegalFuncCode = 0x01,
IllegalDataAddress = 0x02,
IllegalDataValue = 0x03,
ServerFailure = 0x04,
Acknowledge = 0x05,
ServerBusy = 0x06,
GatewayPathsNA = 0x0A,
TargetOffline = 0x0B
};
enum ModbusFuncCode
{
ReadBits1 = 0x01,
ReadBits2 = 0x02,
ReadRegisters1 = 0x03,
ReadRegisters2 = 0x04,
WriteBit = 0x05,
WriteRegister = 0x06,
WriteBits = 0x0F,
WriteRegisters = 0x10,
MaskRegister = 0x16,
ReadWriteRegisters = 0x17
};
enum FatalErrors
{
ParsingBuffer = 0x00,
ModbusPackageWasSplit = 0x01,
DeviceCodeUnknown = 0x02,
VendorIdUnknown = 0x03,
ConnectionError = 0x04
};
// Receive several bit values
_align(1) struct ModbusResReceiveBits
{
byte ByteCount;
byte Data[250]; // Max length: 2000 bits
};
// Receive several register values
_align(1) struct ModbusResReceiveRegisters
{
byte ByteCount;
word Data[125]; // Max length: 125 registers
};
// Confirm the write of a single bit/register
_align(1) struct ModbusResConfirmSingle
{
word Address;
word Value;
};
// Confirm the write of several bits/registers
_align(1) struct ModbusResConfirmMultiple
{
word Address;
word Count;
};
// Confirm the write of AND and OR mask
_align(1) struct ModbusResConfirmMasks
{
word Address;
word And;
word Or;
};
enum ModbusRequestError
{
Exception,
Timeout,
FinalTimeout
};
enum ModbusException
{
None = 0x00,
IllegalFuncCode = 0x01,
IllegalDataAddress = 0x02,
IllegalDataValue = 0x03,
ServerFailure = 0x04,
Acknowledge = 0x05,
ServerBusy = 0x06,
GatewayPathsNA = 0x0A,
TargetOffline = 0x0B
};
enum ModbusFuncCode
{
ReadBits1 = 0x01,
ReadBits2 = 0x02,
ReadRegisters1 = 0x03,
ReadRegisters2 = 0x04,
WriteBit = 0x05,
WriteRegister = 0x06,
WriteBits = 0x0F,
WriteRegisters = 0x10,
MaskRegister = 0x16,
ReadWriteRegisters = 0x17
};
enum FatalErrors
{
ParsingBuffer = 0x00,
ModbusPackageWasSplit = 0x01,
DeviceCodeUnknown = 0x02,
VendorIdUnknown = 0x03,
ConnectionError = 0x04
};
}

View file

@ -2,19 +2,19 @@
variables
{
const long WSA_IO_PENDING = 997;
const long WSAEWOULDBLOCK = 10035;
const dword INVALID_IP = 0xffffffff;
long gIpLastErr = 0;
char gIpLastErrStr[512] = "";
const long WSA_IO_PENDING = 997;
const long WSAEWOULDBLOCK = 10035;
const dword INVALID_IP = 0xffffffff;
long gIpLastErr = 0;
char gIpLastErrStr[512] = "";
enum SocketState { NULL, OK, ERROR, CLOSED };
enum SocketState gSocketState = NULL;
enum SocketState { NULL, OK, ERROR, CLOSED };
enum SocketState gSocketState = NULL;
dword gRemoteIP = INVALID_IP;
word gRemotePort = 0;
dword gRemoteIP = INVALID_IP;
word gRemotePort = 0;
byte gRxBuffer[8192];
byte gRxBuffer[8192];
}