Jonny007-MKD
eb6d28c71b
DeviceInformation.cin struct device now contains all 4 bytes of the IP as fields MakeConfig.can Improved code and comments MakeConfig.can PollingModbusClient.can Removed the subnetting stuff. We simply can add the net to the node name
564 lines
No EOL
24 KiB
Text
564 lines
No EOL
24 KiB
Text
/*@!Encoding:1252*/
|
|
includes
|
|
{
|
|
#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 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)
|
|
|
|
byte ggMaxTransmissionCount; // temp var for gMaxTransmissionCount
|
|
|
|
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));
|
|
|
|
// 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;
|
|
|
|
// 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;
|
|
}
|
|
|
|
on start
|
|
{
|
|
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
|
|
}
|
|
|
|
/// <PutString>
|
|
void PutString(char str[])
|
|
{
|
|
f.PutString(str, strlen(str));
|
|
}
|
|
/// <PutString>
|
|
void PutString(word d)
|
|
{
|
|
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));
|
|
}
|
|
|
|
// Step 1: Detect active devices and collect IP addresses
|
|
// This function will convert the IP address, open the socket and start the detection. The rest will be done by events
|
|
/// <Step1>
|
|
void DetectDevices()
|
|
{
|
|
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 :)
|
|
|
|
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);
|
|
}
|
|
|
|
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
|
|
|
|
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
|
|
}
|
|
/// <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
|
|
}
|
|
|
|
|
|
// Step 2: Sort into subnets and create structure
|
|
// Sort the IPs from gIps to gIpsSorted
|
|
/// <Step2>
|
|
void MakeIpNets()
|
|
{
|
|
long ipNum;
|
|
|
|
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
|
|
|
|
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);
|
|
}
|
|
|
|
AnalyzeDevices(); // Continue with step 3
|
|
}
|
|
|
|
|
|
// Step 3: Retreive configuration of devices
|
|
/// <Step3>
|
|
void AnalyzeDevices()
|
|
{
|
|
// Init counters
|
|
ADn = 1; // expect 1 response
|
|
ADi = 0; // First IP address
|
|
ADl = gIpsSorted.Size();
|
|
|
|
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);
|
|
|
|
// 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);
|
|
|
|
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
|
|
}
|
|
/// <Step3>
|
|
void OnModbusReadRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap)
|
|
{
|
|
struct ModbusReqRead mbreq;
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
// request further information
|
|
ADn = _DeviceGetInformation(BuR);
|
|
return;
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// Step 4: Create the files with the queried data
|
|
/// <Step4>
|
|
void MakeFiles()
|
|
{
|
|
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
|
|
|
|
PutString("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
|
|
PutString("<systemvariables version=\"4\">\n");
|
|
PutString(" <namespace name=\"\" comment=\"\">\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=\"Ethernet\" comment=\"\">\n");
|
|
|
|
for (long ipN : gIpsSorted)
|
|
{
|
|
if ((ipN >> 16) & 0xFF != net) // This IP does not belong to the current net
|
|
continue;
|
|
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);
|
|
return;
|
|
default:
|
|
writeDbg(MbError, "The NodeNameStyle %d is unknown, please use a value of the enum!", NodeNameStyle);
|
|
runError(1001, 0);
|
|
break;
|
|
}
|
|
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 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");
|
|
|
|
PutString(" </namespace>\n");
|
|
PutString(" </namespace>\n");
|
|
}
|
|
|
|
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
|
|
|
|
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");
|
|
|
|
f.Close();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The stuff below is not needed
|
|
/// <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;
|
|
}
|
|
*/
|
|
}
|
|
/// <zzzModbus>
|
|
void OnModbusWriteBitFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
|
|
/// <zzzModbus>
|
|
void OnModbusWriteRegisterFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
|
|
/// <zzzModbus>
|
|
void OnModbusWriteMasksFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
|
|
/// <zzzModbus>
|
|
void OnModbusReadWriteRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
|
|
/// <zzzModbus>
|
|
void OnModbusWriteBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
|
|
/// <zzzModbus>
|
|
void OnModbusWriteRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
|
|
/// <zzzModbus>
|
|
void OnModbusWriteBitSuccess(struct ModbusResConfirmSingle mbc){}
|
|
/// <zzzModbus>
|
|
void OnModbusWriteRegisterSuccess(struct ModbusResConfirmSingle mbc){}
|
|
/// <zzzModbus>
|
|
void OnModbusWriteBitsSuccess(struct ModbusResConfirmMultiple mbc){}
|
|
/// <zzzModbus>
|
|
void OnModbusWriteRegistersSuccess(struct ModbusResConfirmMultiple mbc){}
|
|
/// <zzzModbus>
|
|
void OnModbusWriteMasksSuccess(struct ModbusResConfirmMasks mbc){} |