Jonny007-MKD
9dc7ffd47d
MakeConfig.can Added ModbusClientCommon.cin Introduced different functions for ModbusFuncCode 0x01 and 0x02. B&R handles them differently
674 lines
No EOL
26 KiB
Text
674 lines
No EOL
26 KiB
Text
/*@!Encoding:1252*/
|
||
includes
|
||
{
|
||
#include "include/ModbusUdpClientCommon.cin"
|
||
#include "include/ModbusFunctions.cin"
|
||
}
|
||
|
||
variables
|
||
{
|
||
struct device // A structure that contains information about an Modbus device
|
||
{
|
||
char Ip[16]; // String: The IP address
|
||
char IpLsb[4]; // String: The last byte of the IP address. Used as index of node name
|
||
char IpNet[4]; // String: The second last byte of the IP. Used as index of net
|
||
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
|
||
};
|
||
|
||
char[16] gIps[long]; // List 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 not be scanned anymore.
|
||
|
||
char fnSysvar[40]; // Filename of Sysvars
|
||
char fnDbc[40]; // Filename of DBC
|
||
char name[20]; // Name of project (not important)
|
||
dword ips[50]; // detected IPs. We need this array for enumeration with integers
|
||
|
||
|
||
file f; // The file we are writing to
|
||
byte gIpNets[long]; // A list of nets
|
||
struct device gIpsSorted[long]; // The final array with the devices
|
||
dword gScanFirst, gScanLast; // The first and last IP address as dword
|
||
word ADi, ADn, ADl; // Some variables for "AnalyzeDevices"
|
||
|
||
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);
|
||
*/
|
||
|
||
// Scan a range of IPs for devices. Start and Stop go here
|
||
// Please note: Currently .255 will be skipped! Don't use it for devices and as stop address
|
||
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 MakeConfig.cfg
|
||
strncpy(fnSysvar, "include/SysVars/generated.vsysvar", elCount(fnSysvar));
|
||
strncpy(fnDbc, "include/DBC/generated.dbc", elCount(fnDbc));
|
||
|
||
OutputDebugLevel = Mute;
|
||
}
|
||
|
||
on start
|
||
{
|
||
gMaxTransmissionCount = @sysvar::Config::Modbus::MaxTransmissionCount; // save the value
|
||
@sysvar::Config::Modbus::MaxTransmissionCount = 1; // and then don't retransmit
|
||
|
||
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()
|
||
{
|
||
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); // 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()
|
||
{
|
||
// next IP
|
||
// Note: IP address is stored as big endian, comments are notated as little endian :)
|
||
// 0xFE...... --> Skip xxx.xxx.xxx.255 which is broadcast address in 192.168.xxx.0 nets
|
||
|
||
// If first three bytes are full (123.255.255.255), set those to 0 and increment the first byte (124.0.0.0)
|
||
if ((gScanFirst & 0xFFFFFF00) == 0xFEFFFF00)
|
||
{
|
||
gScanFirst &= 0x000000FF;
|
||
gScanFirst += 0x00000001;
|
||
write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24);
|
||
}
|
||
// If first two bytes are full (124.111.255.255), set those to 0 and increment the second byte (124.112.0.0)
|
||
else if ((gScanFirst & 0xFFFF0000) == 0xFEFF0000)
|
||
{
|
||
gScanFirst &= 0x0000FFF;
|
||
gScanFirst += 0x00000100;
|
||
write("%d.%d.%d.%d ", gScanFirst & 0xFF, (gScanFirst >> 8) & 0xFF, (gScanFirst >> 16) & 0xFF, gScanFirst >> 24);
|
||
}
|
||
// If first last byte is full (124.112.222.255), set it to 0 and increment the third byte (124.112.223.0)
|
||
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 simply increment the LSB
|
||
else
|
||
{
|
||
gScanFirst += 0x01000000;
|
||
}
|
||
|
||
if (gScanFirst == gScanLast) // If this is the last address we stop the detection
|
||
{
|
||
@sysvar::Config::Modbus::MaxTransmissionCount = gMaxTransmissionCount;
|
||
MakeIpNets();
|
||
return;
|
||
}
|
||
|
||
writeEx(1, 1, "."); // Write something so the user knows something is happening
|
||
gRemoteIP = gScanFirst; // Don't open new socket, it takes too much time. This means we should use UDP here!
|
||
ModbusReadBits(0, 1); // Scan the next device
|
||
}
|
||
/// <Step1>
|
||
void OnModbusReadBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap)
|
||
{
|
||
DetectDevicesNext(); // Timeout! We will go to the next device
|
||
}
|
||
/// <Step1>
|
||
void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[], struct ModbusReqRead mbreq)
|
||
{
|
||
ipGetAddressAsString(gScanFirst, gIps[gScanFirst], 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 and add their subnet to gIpNets
|
||
/// <Step2>
|
||
void MakeIpNets()
|
||
{
|
||
long ipNum;
|
||
|
||
if (gIps.Size() == 0) // If no devices were specified and detected
|
||
{
|
||
stop(); // Don't do anything
|
||
return;
|
||
}
|
||
|
||
for (long i : gIps) // Go through all devices
|
||
{
|
||
ipNum = ipGetAddressAsNumber(gIps[i]); // convert IP to dword
|
||
|
||
gIpNets[(ipNum >> 16) & 0xFF] = 1; // register subnet
|
||
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 >> 16) & 0xFF, gIpsSorted[ipNum].IpNet, 10); // set .IpNet
|
||
ltoa((ipNum >> 24) & 0xFF, gIpsSorted[ipNum].IpLsb, 10); // set .IpLsb
|
||
|
||
gIps.Remove(i);
|
||
}
|
||
|
||
AnalyzeDevices(); // Continue with step 3
|
||
}
|
||
|
||
|
||
// Step 3: Retreive configuration of devices
|
||
/// <Step3>
|
||
void AnalyzeDevices()
|
||
{
|
||
// Init counters
|
||
ADn = 1; // expect 10 responses
|
||
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()
|
||
{
|
||
if (strlen(gIpsSorted[ips[ADi]].DeviceIOs.Modules) > 0)
|
||
gIpsSorted[ips[ADi]].DeviceIOs.Modules[strlen(gIpsSorted[ips[ADi]].DeviceIOs.Modules)-1] = 0;
|
||
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) // we have analyzed all devices
|
||
{
|
||
MakeFiles(); // go to Step4
|
||
return;
|
||
}
|
||
|
||
ADn = 1; // expect 10 responses
|
||
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 Timeout:
|
||
return;
|
||
case Exception:
|
||
memcpy_n2h(mbreq, gQueueAck[mbap.TxID].Buffer);
|
||
|
||
if (mbreq.Address == 0x1000 && ex == IllegalDataAddress) // We requested Wago SerialCode and it didn't work --> Not Wago --> B&R
|
||
{
|
||
gIpsSorted[ips[ADi]].Vendor = Wago;
|
||
// request information
|
||
ADn = 10;
|
||
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;
|
||
}
|
||
|
||
writeLineEx(0, 3, "Error while analyzing %s! The device respond with exception code %d! Ignoring...", gIpsSorted[ips[ADi]].IP, ex);
|
||
break;
|
||
case FinalTimeout:
|
||
writeLineEx(0, 3, "Error while analyzing %s! The device did not respond! Ignoring...", gIpsSorted[ips[ADi]].IP);
|
||
break;
|
||
}
|
||
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)
|
||
{
|
||
byte i;
|
||
|
||
if (mbreq.Address == 0x1000) // We detected a B&R device
|
||
{
|
||
gIpsSorted[ips[ADi]].Vendor = BuR;
|
||
|
||
// request further information
|
||
ADn = 5;
|
||
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;
|
||
}
|
||
|
||
switch (gIpsSorted[ips[ADi]].Vendor)
|
||
{
|
||
case Wago:
|
||
// Parse the received data
|
||
switch (mbreq.Address)
|
||
{
|
||
case 0x2011:
|
||
gIpsSorted[ips[ADi]].serialCode = mbres.Data[0];
|
||
break;
|
||
case 0x2012:
|
||
gIpsSorted[ips[ADi]].deviceCode = mbres.Data[0];
|
||
break;
|
||
case 0x1022:
|
||
gIpsSorted[ips[ADi]].DeviceIOs.OutputRegisters = mbres.Data[0] / 16;
|
||
break;
|
||
case 0x1023:
|
||
gIpsSorted[ips[ADi]].DeviceIOs.InputRegisters = mbres.Data[0] / 16;
|
||
break;
|
||
case 0x1024:
|
||
gIpsSorted[ips[ADi]].DeviceIOs.OutputBits = mbres.Data[0];
|
||
break;
|
||
case 0x1025:
|
||
gIpsSorted[ips[ADi]].DeviceIOs.InputBits = mbres.Data[0];
|
||
break;
|
||
case 0x2030:
|
||
case 0x2031:
|
||
case 0x2032:
|
||
case 0x2033:
|
||
for (i = 0; i < mbreq.Count; i++)
|
||
{
|
||
if (mbres.Data[i] == 0x0000) // No more devices --> end
|
||
break;
|
||
ParseDeviceCode(mbres.Data[i], gIpsSorted[ips[ADi]].Vendor, gIpsSorted[ips[ADi]].DeviceIOs);
|
||
}
|
||
break;
|
||
}
|
||
break;
|
||
case BuR:
|
||
// Parse the received data
|
||
switch (mbreq.Address)
|
||
{
|
||
case 0x1083:
|
||
gIpsSorted[ips[ADi]].serialCode = mbres.Data[0];
|
||
break;
|
||
case 0x1101:
|
||
gIpsSorted[ips[ADi]].DeviceIOs.InputRegisters = mbres.Data[0] - 3; // X20BC0087 has 3 AIs when no module is connected... h<>?
|
||
break;
|
||
case 0x1103:
|
||
gIpsSorted[ips[ADi]].DeviceIOs.OutputRegisters = mbres.Data[0];
|
||
break;
|
||
case 0x1105:
|
||
gIpsSorted[ips[ADi]].DeviceIOs.InputBits = mbres.Data[0] * 8; // Unfortunately this is quite imprecise:
|
||
// in the process image one module will always fill a whole number of bytes.
|
||
// So 4 12DI modules not allocate not 4*12 bit = 6 byte, but 4*16 bit = 64 bit = 8 byte
|
||
break;
|
||
case 0x1107:
|
||
gIpsSorted[ips[ADi]].DeviceIOs.OutputBits = mbres.Data[0] * 8;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (--ADn == 0) // If we received all registers
|
||
AnalyzeDevicesNext();
|
||
}
|
||
|
||
// Step 4: Create the files with the queried data
|
||
/// <Step4>
|
||
void MakeFiles()
|
||
{
|
||
GenSysvars();
|
||
GenDbc();
|
||
stop();
|
||
}
|
||
|
||
// Generate the SysVars XML
|
||
/// <Step4>
|
||
void GenSysvars()
|
||
{
|
||
write("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");
|
||
|
||
for (long net : gIpNets)
|
||
{
|
||
byte nett;
|
||
nett = net;
|
||
PutString(" <namespace name=\"Ethernet");
|
||
PutString(nett);
|
||
PutString("\" comment=\"Subnet: 192.168.");
|
||
PutString(nett);
|
||
PutString(".\">\n");
|
||
|
||
for (long ipN : gIpsSorted)
|
||
{
|
||
|
||
if (((ipN >> 16) & 0xFF) != net)
|
||
continue;
|
||
|
||
PutString(" <namespace name=\"Client_");
|
||
//PutString(netS);
|
||
//PutString("_");
|
||
PutString(gIpsSorted[ipN].IpLsb);
|
||
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=\"");
|
||
if (gIpsSorted[ipN].Vendor == Wago)
|
||
{
|
||
PutString((word)123);
|
||
PutString("\" maxValuePhys=\"");
|
||
PutString((word)123);
|
||
PutString("\" />\n");
|
||
}
|
||
else
|
||
{
|
||
PutString((word)2048);
|
||
PutString("\" maxValuePhys=\"");
|
||
PutString((word)2048);
|
||
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=\"");
|
||
if (gIpsSorted[ipN].Vendor == Wago)
|
||
{
|
||
PutString((word)2000);
|
||
PutString("\" maxValuePhys=\"");
|
||
PutString((word)2000);
|
||
PutString("\" />\n");
|
||
}
|
||
else
|
||
{
|
||
PutString((word)16384);
|
||
PutString("\" maxValuePhys=\"");
|
||
PutString((word)16384);
|
||
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=\"");
|
||
if (gIpsSorted[ipN].Vendor == Wago)
|
||
{
|
||
PutString((word)123);
|
||
PutString("\" maxValuePhys=\"");
|
||
PutString((word)123);
|
||
PutString("\" />\n");
|
||
}
|
||
else
|
||
{
|
||
PutString((word)2048);
|
||
PutString("\" maxValuePhys=\"");
|
||
PutString((word)2048);
|
||
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=\"");
|
||
if (gIpsSorted[ipN].Vendor == Wago)
|
||
{
|
||
PutString((word)2000);
|
||
PutString("\" maxValuePhys=\"");
|
||
PutString((word)2000);
|
||
PutString("\" />\n");
|
||
}
|
||
else
|
||
{
|
||
PutString((word)16384);
|
||
PutString("\" maxValuePhys=\"");
|
||
PutString((word)16384);
|
||
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=\"9\" 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=\"9\" 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("</systemvariables>\n");
|
||
|
||
f.Close();
|
||
}
|
||
|
||
// Generate the Database
|
||
/// <Step4>
|
||
void GenDbc()
|
||
{
|
||
write("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_");
|
||
//PutString(gIpsSorted[ipN].IpNet);
|
||
//PutString("_");
|
||
PutString(gIpsSorted[ipN].IpLsb);
|
||
}
|
||
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\" \"");
|
||
PutString(name);
|
||
PutString("\";\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){} |