Jonny007-MKD
84ab39d5b3
Counting the send attempts to reject packets that cannot be send (e.g. wrong network) MakeConfig.can Introduced options (skip255 and useThirdIpNameForNodeName)
335 lines
12 KiB
Plaintext
335 lines
12 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.
|
|
/// 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 telegrams can be processed at the same time)
|
|
/// This file is used at two position: 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.
|
|
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 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. These info will be written into the 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 an Modbus device. Used in MakeConfig.
|
|
{
|
|
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
|
|
};
|
|
}
|
|
|
|
// 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;
|
|
thisDev.MaxBitCount = 0x4000; // B&R allows up to 16348 digital inputs
|
|
thisDev.MaxRegisterCount = 0x0800; // B&R allows up to 2048 analog inputs
|
|
thisDev.ReceiveWindow = 1; // B&R can only handle 1 request 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;
|
|
byte numChannels;
|
|
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?
|
|
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: // 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;
|
|
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;
|
|
case 555: // devices that have 4 outputs
|
|
case 553:
|
|
case 557:
|
|
case 559:
|
|
input = 0;
|
|
numChannels = 4;
|
|
default: // unknown device
|
|
writeDbg(AlgoInfo, "Connected device: 750-%d", dev);
|
|
return;
|
|
}
|
|
// Prepare the format string
|
|
}
|
|
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;
|
|
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;
|
|
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);
|
|
}
|
|
} |