338 lines
16 KiB
Plaintext
338 lines
16 KiB
Plaintext
/*@!Encoding:1252*/
|
|
|
|
// 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
|
|
/// Currently supported are Wago 750-881 and B&R X20BC0087 hopefully only the vendors have to be distinguished
|
|
/// 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)
|
|
/// - 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
|
|
{
|
|
// 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;
|
|
|
|
enum Vendor // The Vendor enum. All Vendors have to be listed here and all listed vendors have to be implemented in this file
|
|
{
|
|
All = 0xFF,
|
|
Wago = 23, // Wago
|
|
BuR = 2 // B&R
|
|
};
|
|
struct deviceIOs // A structure which contains quantity information about connected IO. Used in MakeConfig. This info will be written into SysVars
|
|
{
|
|
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
|
|
};
|
|
struct device // A structure that contains information about a Modbus device. Used in MakeConfig.
|
|
{
|
|
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: The first 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
|
|
};
|
|
}
|
|
|
|
// 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 --> minimum
|
|
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;
|
|
thisDev.MaxBitCount = 0x4000; // B&R allows up to 16384 digital inputs
|
|
thisDev.MaxRegisterCount = 0x0800; // B&R allows up to 2048 analog inputs
|
|
thisDev.ReceiveWindow = 8; // B&R can handle 8 requests at a time
|
|
break;
|
|
default:
|
|
OnModbusClientPanics(VendorIdUnknown);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// This is for making the sysvars (MakeConfig)
|
|
|
|
// 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)
|
|
{
|
|
byte input; // Yes: INPUT module; No: OUTPUT module
|
|
byte numChannels; // Quantity of channels
|
|
char module[10];
|
|
strncpy(module, "..%d,", elCount(module));
|
|
|
|
switch(vendor)
|
|
{
|
|
case Wago: // if this is a Wago device
|
|
if (dev & 0x8000) // Digital Module
|
|
{
|
|
module[0] = 'D';
|
|
numChannels = (dev >> 8) & 0x007F;
|
|
|
|
if (dev & 0x0001) // Input Module
|
|
input = 1;
|
|
else if (dev & 0x0002) // Output Module
|
|
input = 0;
|
|
else // mhm... What is it? Input and Output? Neither?
|
|
writeDbg(AlgoError, "DeviceParseCode: Device code 0x%X cannot be decoded", dev);
|
|
}
|
|
else // Analog (=Complex) module
|
|
{
|
|
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: // modules that have 1 input
|
|
input = 1;
|
|
numChannels = 1;
|
|
break;
|
|
case 452: // modules 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: // modules that have 3 inputs
|
|
case 494:
|
|
case 495:
|
|
input = 1;
|
|
numChannels = 3;
|
|
break;
|
|
case 459: // modules that have 4 inputs
|
|
case 453:
|
|
case 455:
|
|
case 468:
|
|
case 457:
|
|
case 464:
|
|
case 460:
|
|
case 463:
|
|
input = 1;
|
|
numChannels = 4;
|
|
break;
|
|
case 552: // modules that have 2 outputs
|
|
case 585:
|
|
case 563:
|
|
case 554:
|
|
case 550:
|
|
case 560:
|
|
case 562:
|
|
case 556:
|
|
input = 0;
|
|
numChannels = 2;
|
|
break;
|
|
case 555: // modules that have 4 outputs
|
|
case 553:
|
|
case 557:
|
|
case 559:
|
|
input = 0;
|
|
numChannels = 4;
|
|
break;
|
|
default: // unknown modules
|
|
writeDbg(AlgoInfo, "Unknown onnected module: 750-%d", dev);
|
|
return;
|
|
}
|
|
}
|
|
break; // switch(vendor)
|
|
default:
|
|
writeDbg(AlgoError, "ParseDeviceCode: Unknown vendor id: %d", vendor);
|
|
OnModbusClientPanics(VendorIdUnknown);
|
|
return;
|
|
}
|
|
|
|
module[1] = input ? 'I' : 'O'; // Set input/output char
|
|
|
|
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
|
|
}
|
|
|
|
// 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
|
|
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
|
|
return 5; // 5 is the number of lines above (number of responses that we expect)
|
|
default:
|
|
writeDbg(AlgoError, "DeviceGetInformation: Unknown vendor id: %d", vendor);
|
|
OnModbusClientPanics(VendorIdUnknown);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
case 0x2011: // Serial Code
|
|
device.serialCode = data[0];
|
|
break;
|
|
case 0x2012: // Device Code
|
|
device.deviceCode = data[0];
|
|
break;
|
|
case 0x1022: // Number of output registers (AO)
|
|
device.DeviceIOs.OutputRegisters = data[0] / 16; // Wago returns the allocated bits.
|
|
break;
|
|
case 0x1023: // Number of input registers (AI)
|
|
device.DeviceIOs.InputRegisters = data[0] / 16;
|
|
break;
|
|
case 0x1024: // Number of output bits (DO)
|
|
device.DeviceIOs.OutputBits = data[0];
|
|
break;
|
|
case 0x1025: // Number of input bits (DI)
|
|
device.DeviceIOs.InputBits = data[0];
|
|
break;
|
|
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)
|
|
{
|
|
case 0x1083: // Product Code
|
|
device.serialCode = data[0];
|
|
break;
|
|
case 0x1101: // Number of input registers (AI)
|
|
device.DeviceIOs.InputRegisters = data[0];
|
|
break;
|
|
case 0x1103: // Number of output registers (AO)
|
|
device.DeviceIOs.OutputRegisters = data[0];
|
|
break;
|
|
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;
|
|
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);
|
|
}
|
|
} |