Bachelorthesis/Modbus-CAPL/include/CAPL/include/DeviceInformation.cin

337 lines
12 KiB
Text
Raw Normal View History

/*@!Encoding:1252*/
2014-06-20 12:45:41 +02:00
// This file shall contain all information about device specific parameters.
/// The aim of this file is to centralize this device specific information so that new Vendors can be added easily
2014-08-29 18:13:05 +02:00
/// Currently supported are Wago 750-881 and B&R X20BC0087 hopefully only the vendors have to be distinguished
2014-06-20 12:45:41 +02:00
/// The Modbus specification leaves room for several parameters than can vary from vendor to vendor (and perhaps also from device to device). These parameters are:
/// - Start addresses for reading input bits/registers
/// - Start addresses for reading output bits/registers
/// - Start addresses for writing input bits/registers
/// - Start addresses for writing output bits/registers
/// - Maximum number of connected IO (bits and registers)
2014-08-29 18:13:05 +02:00
/// - Size of the receive window (how many requests can be received and buffered)
/// 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
{
2014-08-29 18:13:05 +02:00
// 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;
2014-08-29 18:13:05 +02:00
enum Vendor // The Vendor enum. All Vendors have to listed here and all listed vendors have to be implemented in this file
{
All = 0xFF,
2014-08-29 18:13:05 +02:00
Wago = 23, // Wago
BuR = 2 // B&R
};
2014-08-29 18:13:05 +02:00
struct deviceIOs // A structure which contains quantity information about connected IO. Used in MakeConfig. These info will be written into the SysVars
{
2014-08-29 18:13:05 +02:00
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
};
2014-08-29 18:13:05 +02:00
struct device // A structure that contains information about an Modbus device. Used in MakeConfig.
{
2014-08-29 18:13:05 +02:00
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
};
}
2014-06-20 12:45:41 +02:00
// This is for both the normal client and for making the sysvars.
// It will set the above mentioned parameters so the client can work properly
/// <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;
2014-07-14 11:37:21 +02:00
thisDev.MaxBitCount = 0x4000; // B&R allows up to 16384 digital inputs
thisDev.MaxRegisterCount = 0x0800; // B&R allows up to 2048 analog inputs
2014-07-14 12:32:08 +02:00
thisDev.ReceiveWindow = 8; // B&R can handle 8 requests at a time
break;
2014-06-20 12:45:41 +02:00
default:
OnModbusClientPanics(VendorIdUnknown);
break;
}
}
// This is for making the sysvars (MakeConfig)
2014-06-20 12:45:41 +02:00
// This function parses the received device code/module code received from the device and writes it to struct deviceIO.Modules.
// The quantity of IO does not have to be parsed here because it is extracted from extra registers
/// <MakeConfig>
void _DeviceParseCode(word dev, enum Vendor vendor, struct deviceIOs dios)
{
2014-08-29 18:13:05 +02:00
byte input; // Yes: INPUT module; No: OUTPUT module
byte numChannels; // Quantity of channels
char module[10];
2014-06-20 12:45:41 +02:00
strncpy(module, "..%d,", elCount(module));
switch(vendor)
{
2014-06-20 12:45:41 +02:00
case Wago: // if this is a Wago device
if (dev & 0x8000) // Digital Module
{
2014-06-20 12:45:41 +02:00
module[0] = 'D';
numChannels = (dev >> 8) & 0x007F;
if (dev & 0x0001) // Input Module
input = 1;
else if (dev & 0x0002) // Output Module
input = 0;
2014-06-20 12:45:41 +02:00
else // mhm... What is it? Input and Output?
writeDbg(AlgoError, "DeviceParseCode: Device code 0x%X cannot be decoded", dev);
}
2014-06-20 12:45:41 +02:00
else // Analog (=Complex) module
{
2014-06-20 12:45:41 +02:00
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;
2014-06-20 12:45:41 +02:00
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;
2014-06-20 12:45:41 +02:00
case 555: // devices that have 4 outputs
case 553:
case 557:
case 559:
input = 0;
numChannels = 4;
2014-06-20 12:45:41 +02:00
default: // unknown device
writeDbg(AlgoInfo, "Connected device: 750-%d", dev);
return;
}
2014-06-20 12:45:41 +02:00
// Prepare the format string
}
break; // switch(vendor)
default:
writeDbg(AlgoError, "ParseDeviceCode: Unknown vendor id: %d", vendor);
OnModbusClientPanics(VendorIdUnknown);
return;
}
2014-06-20 12:45:41 +02:00
module[1] = input ? 'I' : 'O'; // Set input/output char
2014-06-20 12:45:41 +02:00
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
}
2014-06-20 12:45:41 +02:00
// This function requests more information from the device and returns the count of expected responses (Modbus telegrams). It is called by AnalyzeDevices() in MakeConfig
// Requested information has to be: Count of AOs, AIs, DOs, DIs
// Additionaly information can be: Serial Code, Device/Product Code, Codes of connected modules
/// <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
2014-08-29 18:13:05 +02:00
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
2014-08-29 18:13:05 +02:00
return 5; // 5 is the number of lines above (number of responses that we expect)
2014-06-20 12:45:41 +02:00
default:
writeDbg(AlgoError, "DeviceGetInformation: Unknown vendor id: %d", vendor);
OnModbusClientPanics(VendorIdUnknown);
}
return 0;
}
2014-06-20 12:45:41 +02:00
// This function parses the received registers (requested by DeviceGetInformation())
// It is called in the callback OnModbusReadRegistersSuccess() to fill the 'device' structure
/// <MakeConfig>
void _DeviceParseRegister(struct device device, word address, word data[], word count)
{
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)
{
2014-06-20 12:45:41 +02:00
case 0x2011: // Serial Code
device.serialCode = data[0];
break;
2014-06-20 12:45:41 +02:00
case 0x2012: // Device Code
device.deviceCode = data[0];
break;
2014-06-20 12:45:41 +02:00
case 0x1022: // Number of output registers (AO)
device.DeviceIOs.OutputRegisters = data[0] / 16; // Wago returns the allocated bits.
break;
2014-06-20 12:45:41 +02:00
case 0x1023: // Number of input registers (AI)
device.DeviceIOs.InputRegisters = data[0] / 16;
break;
2014-06-20 12:45:41 +02:00
case 0x1024: // Number of output bits (DO)
device.DeviceIOs.OutputBits = data[0];
break;
2014-06-20 12:45:41 +02:00
case 0x1025: // Number of input bits (DI)
device.DeviceIOs.InputBits = data[0];
break;
2014-06-20 12:45:41 +02:00
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)
{
2014-06-20 12:45:41 +02:00
case 0x1083: // Product Code
device.serialCode = data[0];
break;
2014-06-20 12:45:41 +02:00
case 0x1101: // Number of input registers (AI)
device.DeviceIOs.InputRegisters = data[0];
break;
2014-06-20 12:45:41 +02:00
case 0x1103: // Number of output registers (AO)
device.DeviceIOs.OutputRegisters = data[0];
break;
2014-06-20 12:45:41 +02:00
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;
2014-06-20 12:45:41 +02:00
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);
}
}