Converted tabs to spaces.
I still think tabs are better :P
This commit is contained in:
parent
e534955938
commit
6125015cc5
24 changed files with 4466 additions and 4470 deletions
|
@ -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);
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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){}
|
||||
|
|
|
@ -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!");
|
||||
}*/
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<breakpoints>
|
||||
<breakpoint Active="true" HaltsSimulation="true" AbsoluteLine="352" Function="" RelativeLine="9" />
|
||||
</breakpoints>
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue