Bachelorthesis/Modbus/MakeConfig.can
Jonny007-MKD e0a6d84a9f include/ModbusFunctions.cin
Write values to struct

MakeConfig.can
  Detect devices via IP port scan
  Analyze devices and generate sysVars appropriately

ModbusClientUDP.can
  Don't analyze devices because this has to be done in MakeConfig.can
2014-05-26 10:07:04 +00:00

474 lines
No EOL
18 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];
word SerialCode;
word DeviceCode;
struct deviceIOs DeviceIOs;
};
char[16] gIps[long];
char gScanFirstIp[16];
char gScanLastIp[16];
word gScanPort = 502;
char fnSysvar[20]; // Filename of Sysvars
char fnDbc[20]; // 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;
}
on preStart
{/*
strncpy(gIps[0], "192.168.1.3", 16);
strncpy(gIps[2], "192.168.1.4", 16);
strncpy(gIps[3], "192.168.1.8", 16);*/
strncpy(gScanFirstIp, "192.168.1.1", 16);
strncpy(gScanLastIp, "192.168.1.100", 16);
strncpy(name, "Modbus", elCount(name));
strncpy(fnSysvar, "generated.vsysvar", elCount(fnSysvar));
strncpy(fnDbc, "generated.dbc", elCount(fnDbc));
}
on start
{
if (gIps.Size() == 0)
DetectDevices();
else
MakeIpNets();
}
void PutString(file f, char str[])
{
f.PutString(str, strlen(str));
}
void PutString(file f, word d)
{
char str[6];
ltoa(d, str, 10);
f.PutString(str, strlen(str));
}
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()
{
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, 502);
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)
{
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);
if (gRemoteIP != INVALID_IP)
gRemoteIP = ips[ADi];
else
ModbusConnectTo(ips[ADi], gRemotePort);
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]].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=\"5\" 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=\"502\" 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=\"3\" 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;
write("GenSysvars: %s", gIpsSorted[ipN].Ip);
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");
// 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)] = 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=\"32\" 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=\"32\" 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=\"32\" 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=\"32\" 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)
{
write("GenDbc: %s", gIpsSorted[ipN].Ip);
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();
}
void OnModbusWriteBitFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
void OnModbusWriteRegisterFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
void OnModbusWriteMasksFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
void OnModbusReadWriteRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
void OnModbusWriteBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
void OnModbusWriteRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap){}
void OnModbusWriteBitSuccess(struct ModbusResConfirmSingle mbc){}
void OnModbusWriteRegisterSuccess(struct ModbusResConfirmSingle mbc){}
void OnModbusWriteBitsSuccess(struct ModbusResConfirmMultiple mbc){}
void OnModbusWriteRegistersSuccess(struct ModbusResConfirmMultiple mbc){}
void OnModbusWriteMasksSuccess(struct ModbusResConfirmMasks mbc){}