Bachelorthesis/Modbus/include/CAPL/MakeConfig.can
Jonny007-MKD 9d0c136b08 include/CAPL/MakeConfig.can
Generate Config in SysVars using Config in MakeConfig
  Added Vendor
2014-05-26 11:58:28 +00:00

511 lines
No EOL
19 KiB
Text

/*@!Encoding:1252*/
includes
{
#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;
};
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
file f;
byte gIpNets[long];
struct device gIpsSorted[long];
dword gScanFirst, gScanLast;
word ADi, ADn, ADl;
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
strncpy(gScanFirstIp, "192.168.1.1", 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));
}
on start
{
gMaxTransmissionCount = @sysvar::Config::Modbus::MaxTransmissionCount;
if (gIps.Size() == 0)
DetectDevices();
else
MakeIpNets();
}
/// <PutString>
void PutString(file f, char 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));
}
/// <PutString>
void PutString(file f, byte d)
{
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);
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;
}
else if ((gScanFirst & 0xFFFF0000) == 0xFEFF0000)
{
gScanFirst &= 0x0000FFF;
gScanFirst += 0x00000100;
}
else if ((gScanFirst & 0xFF000000) == 0xFE000000)
{
gScanFirst &= 0x00FFFFFF;
gScanFirst += 0x00010000;
}
else
{
gScanFirst += 0x01000000;
}
if (gScanFirst == gScanLast)
{
@sysvar::Config::Modbus::MaxTransmissionCount = gMaxTransmissionCount;
MakeIpNets();
return;
}
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();
}
/// <Step1>
void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbr, byte bitStatus[], word numBits)
{
ipGetAddressAsString(gScanFirst, gIps[gScanFirst], 16);
DetectDevicesNext();
}
// Step 2: Sort into subnets
// Sort the IPs from gIps to gIpsSorted and add their subnet to gIpNets
/// <Step2>
void MakeIpNets()
{
long ipNum;
if (gIps.Size() == 0)
{
stop();
return;
}
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
gIps.Remove(i);
}
AnalyzeDevices();
}
// Step 3: Retreive configuration of devices
/// <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);
}
/// <Step3>
void AnalyzeDevicesNext()
{
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);
}
/// <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;
}
}
/// <Step3>
void OnModbusReadRegistersSuccess(struct ModbusResReceiveRegisters mbr, word numRegs)
{
byte i;
struct ModbusReqRead mbrq;
if (!gQueueAck.ContainsKey(mbr.Header.TxID))
return;
memcpy_n2h(mbrq, gQueueAck[mbr.Header.TxID].Buffer);
switch (mbrq.Address)
{
case 0x2011:
gIpsSorted[ips[ADi]].serialCode = mbr.Data[0];
break;
case 0x2012:
gIpsSorted[ips[ADi]].deviceCode = mbr.Data[0];
break;
case 0x2030:
case 0x2031:
case 0x2032:
case 0x2033:
for (i = 0; i < 65; i++)
{
if (mbr.Data[i] == 0x0000)
break;
ParseDeviceCode(mbr.Data[i], gIpsSorted[ips[ADi]].Vendor, gIpsSorted[ips[ADi]].DeviceIOs);
}
break;
}
if (++ADn == 6)
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(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");
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)
{
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");
// 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 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, "</systemvariables>\n");
f.Close();
}
// Generate the Database
/// <Step4>
void GenDbc()
{
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_:");
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();
}
/// <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){}