2014-05-08 15:34:28 +02:00
/*@!Encoding:1252*/
2014-05-08 16:44:01 +02:00
// Additionally include ModbusTcpCommon.cin or ModbusUdpCommon.cin
2014-05-08 15:34:28 +02:00
includes
{
2014-05-15 14:43:52 +02:00
#include "ModbusCommonStructs.cin"
2014-05-08 15:34:28 +02:00
}
variables
{
2014-05-15 14:43:52 +02:00
const word gMaxPacketLength = __size_of(struct ModbusReqWriteRegisters);
2014-05-21 13:26:45 +02:00
msTimer gtRobin;
word gTxID = 0x0000; // Transaction Identifier for Modbus. Used as index for gQueue
2014-05-15 14:43:52 +02:00
2014-05-21 13:26:45 +02:00
// Global storage for pending and sent requests, associated by TxID
struct QueueElement
2014-05-15 14:43:52 +02:00
{
2014-05-21 13:26:45 +02:00
word TimeoutTicks;
byte Timeouts;
2014-05-08 15:34:28 +02:00
word Length;
2014-05-15 14:43:52 +02:00
byte Buffer[gMaxPacketLength];
2014-05-21 13:26:45 +02:00
};
struct QueueElement gQueuePending[long, 2];
struct QueueElement gQueueSent[long, 2];
struct QueueElement gQueueAck[long, 2];
2014-05-15 14:43:52 +02:00
}
2014-05-08 15:34:28 +02:00
2014-05-21 13:26:45 +02:00
void ModbusInit()
2014-05-08 15:34:28 +02:00
{
2014-05-21 13:26:45 +02:00
char ip[16];
sysGetVariableString("%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Config", "IP", ip, elCount(ip));
ModbusConnectTo(ip, @sysvar::Config::Modbus::Port);
2014-05-08 15:34:28 +02:00
}
2014-05-15 14:43:52 +02:00
void ModbusMakeHeader(struct ModbusApHeader mbap, word length)
2014-05-08 15:34:28 +02:00
{
2014-05-21 13:26:45 +02:00
mbap.TxID = gTxID++; // [2] Transaction ID
2014-05-08 15:34:28 +02:00
mbap.Protocol = 0x0000; // [2] Protocol ID = 0 = Modbus
2014-05-15 14:43:52 +02:00
mbap.Length = length - __offset_of(struct ModbusApHeader, UnitID); // [2] Length; Number of bytes following
2014-05-08 15:34:28 +02:00
mbap.UnitID = 0xFF; // [1] Unit identifier; not relevant
}
2014-05-15 14:43:52 +02:00
// REGION: ModbusReadBits -------------------------------------------------------------
/// <ModbusReadBits>
2014-05-08 15:34:28 +02:00
void ModbusReadBits(word address, word count)
{
2014-05-15 14:43:52 +02:00
const byte length = __size_of(struct ModbusReqRead);
const byte funcCode = 0x01;
2014-05-08 15:34:28 +02:00
byte buffer[length];
2014-05-15 14:43:52 +02:00
struct ModbusReqRead mbr;
2014-05-08 15:34:28 +02:00
ModbusMakeHeader(mbr.Header, length);
// Payload
2014-05-15 14:43:52 +02:00
mbr.Header.FuncCode = funcCode; // [1] Function Code; 1: Read Coils (DI), 2: Read Discret Inputs (DIO), seems to be the same for WAGO 750-881
2014-05-08 15:34:28 +02:00
mbr.Address = address; // [2] Start address
mbr.Count = count; // [2] Number of items; 1:max 2000=0x7D0
memcpy_h2n(buffer, mbr);
2014-05-21 13:26:45 +02:00
ModbusSend(buffer, length, mbr.Header.TxID);
2014-05-08 15:34:28 +02:00
}
2014-05-15 14:43:52 +02:00
/// <ModbusReadBits>
void OnModbusReceiveBits(byte buffer[])
{
struct ModbusResReceiveBits mbr;
byte bitStatus[1968];
word numBits;
byte i, j;
memcpy_n2h(mbr, buffer);
2014-05-21 13:26:45 +02:00
numBits = (gQueueAck[mbr.Header.TxID].Buffer[10] << 8) + gQueueAck[mbr.Header.TxID].Buffer[11];
2014-05-15 14:43:52 +02:00
for (i = 0; i < mbr.ByteCount; i++)
{
for (j = 0; j < 8; j++)
{
2014-05-21 13:29:29 +02:00
bitStatus[8*i+j] = (mbr.Data[i] >> j) & 0x01;
2014-05-15 14:43:52 +02:00
}
}
OnModbusReadBitsSuccess(mbr, bitStatus, numBits);
}
/// <ModbusReadBits>
void OnModbusReceiveBitsException(struct ModbusApHeader mbap, enum ModbusException ex)
2014-05-08 15:34:28 +02:00
{
2014-05-15 14:43:52 +02:00
OnModbusReadBitsFailed(Exception, ex, mbap);
}
2014-05-08 15:34:28 +02:00
2014-05-15 14:43:52 +02:00
// REGION: ModbusReadRegisters -------------------------------------------------------
/// <ModbusReadRegisters>
2014-05-08 15:34:28 +02:00
void ModbusReadRegisters(word address, word count) // 16 bit
{
2014-05-15 14:43:52 +02:00
const byte length = __size_of(struct ModbusReqRead);
const byte funcCode = 0x03;
2014-05-08 15:34:28 +02:00
byte buffer[length];
2014-05-15 14:43:52 +02:00
struct ModbusReqRead mbr;
2014-05-08 15:34:28 +02:00
ModbusMakeHeader(mbr.Header, length);
// Payload
2014-05-15 14:43:52 +02:00
mbr.Header.FuncCode = funcCode; // [1] Function Code; 3: Read Holding Registers (AI), 4: Read Input Registers (AIO), seems to be the same for WAGO 750-881
2014-05-08 15:34:28 +02:00
mbr.Address = address; // [2] Start address
2014-05-15 14:43:52 +02:00
mbr.Count = count; // [2] Number of items; 1:max 125=0x7D
2014-05-08 15:34:28 +02:00
memcpy_h2n(buffer, mbr);
2014-05-21 13:26:45 +02:00
ModbusSend(buffer, length, mbr.Header.TxID);
2014-05-08 15:34:28 +02:00
}
2014-05-15 14:43:52 +02:00
/// <ModbusReadRegisters>
void OnModbusReceiveRegisters(byte buffer[])
{
struct ModbusResReceiveRegisters mbr;
word numRegs;
memcpy_n2h(mbr, buffer);
2014-05-21 13:26:45 +02:00
numRegs = (gQueueAck[mbr.Header.TxID].Buffer[10] << 8) + gQueueAck[mbr.Header.TxID].Buffer[11];
2014-05-15 14:43:52 +02:00
OnModbusReadRegistersSuccess(mbr, numRegs);
}
/// <ModbusReadRegisters>
void OnModbusReceiveRegistersException(struct ModbusApHeader mbap, enum ModbusException ex)
{
OnModbusReadBitsFailed(Exception, ex, mbap);
}
2014-05-08 15:34:28 +02:00
2014-05-15 14:43:52 +02:00
// REGION: ModbusWriteBit -------------------------------------------------------------
/// <ModbusWriteBit>
2014-05-08 15:34:28 +02:00
void ModbusWriteBit(word address, byte value)
{
2014-05-15 14:43:52 +02:00
const byte length = __size_of(struct ModbusReqWriteSingle);
const byte funcCode = 0x05;
2014-05-08 15:34:28 +02:00
byte buffer[length];
2014-05-15 14:43:52 +02:00
struct ModbusReqWriteSingle mbw;
2014-05-08 15:34:28 +02:00
if (value >= 1)
value = 0xFF;
ModbusMakeHeader(mbw.Header, length);
// Payload
2014-05-15 14:43:52 +02:00
mbw.Header.FuncCode = funcCode; // [1] Function Code; 5: Write Single Coil (DO)
2014-05-08 15:34:28 +02:00
mbw.Address = address; // [2] Output address
mbw.Value = value << 8; // [2] Output value (0x0000: Off, 0xFF00: On)
memcpy_h2n(buffer, mbw);
2014-05-21 13:26:45 +02:00
ModbusSend(buffer, length, mbw.Header.TxID);
2014-05-08 15:34:28 +02:00
}
2014-05-15 14:43:52 +02:00
/// <ModbusWriteBit>
void OnModbusConfirmBit(byte buffer[])
{
struct ModbusResConfirmSingle mbc;
memcpy_n2h(mbc, buffer);
OnModbusWriteBitSuccess(mbc);
}
/// <ModbusWriteBit>
void OnModbusConfirmBitException(struct ModbusApHeader mbap, enum ModbusException ex)
2014-05-08 15:34:28 +02:00
{
2014-05-15 14:43:52 +02:00
OnModbusReadBitsFailed(Exception, ex, mbap);
2014-05-08 15:34:28 +02:00
}
2014-05-15 14:43:52 +02:00
2014-05-08 15:34:28 +02:00
2014-05-15 14:43:52 +02:00
// REGION: ModbusWriteRegister ------------------------------------------------------
/// <ModbusWriteRegister>
void ModbusWriteRegister(word address, int value)
2014-05-08 15:34:28 +02:00
{
2014-05-15 14:43:52 +02:00
const byte length = __size_of(struct ModbusReqWriteSingle);
const byte funcCode = 0x06;
2014-05-08 15:34:28 +02:00
byte buffer[length];
2014-05-15 14:43:52 +02:00
struct ModbusReqWriteSingle mbw;
2014-05-08 15:34:28 +02:00
ModbusMakeHeader(mbw.Header, length);
// Payload
2014-05-15 14:43:52 +02:00
mbw.Header.FuncCode = funcCode; // [1] Function Code; 5: Write Single Register (AO)
2014-05-08 15:34:28 +02:00
mbw.Address = address; // [2] Output address
mbw.Value = value; // [2] Output value
memcpy_h2n(buffer, mbw);
2014-05-21 13:26:45 +02:00
ModbusSend(buffer, length, mbw.Header.TxID);
2014-05-08 15:34:28 +02:00
}
2014-05-15 14:43:52 +02:00
/// <ModbusWriteRegister>
void OnModbusConfirmRegister(byte buffer[])
2014-05-08 15:34:28 +02:00
{
2014-05-15 14:43:52 +02:00
struct ModbusResConfirmSingle mbc;
memcpy_n2h(mbc, buffer);
OnModbusWriteRegisterSuccess(mbc);
2014-05-08 15:34:28 +02:00
}
2014-05-15 14:43:52 +02:00
/// <ModbusWriteRegister>
void OnModbusConfirmRegisterException(struct ModbusApHeader mbap, enum ModbusException ex)
{
OnModbusReadBitsFailed(Exception, ex, mbap);
}
// REGION: ModbusWriteBits ----------------------------------------------------------
/// <ModbusWriteBits>
void ModbusWriteBits(word address, word count, byte values[])
{
const word maxLength = __size_of(struct ModbusReqWriteBits);
const byte funcCode = 0x0F;
byte buffer[maxLength];
struct ModbusReqWriteBits mbw;
byte dataLength;
byte overallLength;
2014-05-08 15:34:28 +02:00
word i;
2014-05-15 14:43:52 +02:00
dataLength = _ceil(count / 8.0);
overallLength = maxLength - 1968/8 + dataLength;
ModbusMakeHeader(mbw.Header, overallLength);
2014-05-08 15:34:28 +02:00
// Payload
2014-05-15 14:43:52 +02:00
mbw.Header.FuncCode = funcCode; // [1] Function Code; 15: Write Multiple Bits (DOs)
2014-05-08 15:34:28 +02:00
mbw.Address = address; // [2] Output address
mbw.Count = count; // [2] Number of items; 1:max 1968=0x7B0
2014-05-15 14:43:52 +02:00
mbw.ByteCount = dataLength; // [1] Number of bytes; = ceil(count/8)
memcpy(mbw.Data, values, dataLength); // this is 1 memcpy too much -.-
2014-05-08 15:34:28 +02:00
memcpy_h2n(buffer, mbw);
2014-05-21 13:26:45 +02:00
ModbusSend(buffer, overallLength, mbw.Header.TxID);
2014-05-08 15:34:28 +02:00
}
2014-05-15 14:43:52 +02:00
/// <ModbusWriteBits>
2014-05-08 15:34:28 +02:00
void ModbusWriteBitsB(word address, word count, byte values[])
{
byte buffer[2]; // length
word length;
dword ellCount;
dword i;
dword j;
2014-05-21 13:29:29 +02:00
///char str1[20*2], str2[20*3];
2014-05-08 15:34:28 +02:00
2014-05-21 13:29:29 +02:00
length = (word)_ceil(count / 8.0);
///writeLineEx(0, 3, "count: %d; length: %d", count, length);
2014-05-08 15:34:28 +02:00
2014-05-21 13:29:29 +02:00
if (count % 8 != 0)
2014-05-08 15:34:28 +02:00
length--;
for (i = 0; i < length; i++)
{
buffer[i] = 0;
for (j = 0; j < 8; j++)
{
buffer[i] |= (values[i*8 + j] & 0x01) << j;
2014-05-21 13:29:29 +02:00
///writeLineEx(0, 3, "j: %d; indx: %d; value: %d; mask: %X", j, i*8 + j, values[i*8 + j], (0x01 << j));
2014-05-08 15:34:28 +02:00
}
2014-05-21 13:29:29 +02:00
///writeLineEx(0, 3, "%d: %X", i, buffer[i]);
2014-05-08 15:34:28 +02:00
}
2014-05-21 13:29:29 +02:00
for (j = 0; j < count % 8; j++) // wont be executed if there is no remainder
2014-05-08 15:34:28 +02:00
{
2014-05-21 13:29:29 +02:00
///writeLineEx(0, 3, "j: %d; indx: %d; value: %d; mask: %X", j, (length-1)*8 + j, values[(length-1)*8 + j], (0x01 << j));
2014-05-08 15:34:28 +02:00
buffer[length] |= (values[(length)*8 + j] & 0x01) << j;
}
2014-05-21 13:29:29 +02:00
///writeLineEx(0, 3, "%d: %X", length-1, buffer[length-1]);
2014-05-08 15:34:28 +02:00
2014-05-21 13:29:29 +02:00
///hbin_to_strhex(values, str1);
///bin_to_strhex(buffer, str2);
///writeLineEx(0, 1, "<%NODE_NAME%> ModbusWriteBitsB: Encoded %s to %s", str1, str2);
2014-05-08 15:34:28 +02:00
ModbusWriteBits(address, count, buffer);
}
2014-05-15 14:43:52 +02:00
/// <ModbusWriteBits>
void OnModbusConfirmBits(byte buffer[])
{
struct ModbusResConfirmMultiple mbc;
memcpy_n2h(mbc, buffer);
OnModbusWriteBitsSuccess(mbc);
}
/// <ModbusWriteBits>
void OnModbusConfirmBitsException(struct ModbusApHeader mbap, enum ModbusException ex)
2014-05-08 15:34:28 +02:00
{
2014-05-15 14:43:52 +02:00
OnModbusReadBitsFailed(Exception, ex, mbap);
2014-05-08 15:34:28 +02:00
}
2014-05-15 14:43:52 +02:00
// REGION: ModbusWriteRegisters -------------------------------------------------------
/// <ModbusWriteRegisters>
2014-05-21 13:29:29 +02:00
void ModbusWriteRegisters(word address, word count, word values[])
2014-05-08 15:34:28 +02:00
{
2014-05-15 14:43:52 +02:00
const word maxLength = __size_of(struct ModbusReqWriteRegisters);
const byte funcCode = 0x10;
byte buffer[maxLength];
struct ModbusReqWriteRegisters mbw;
byte dataLength;
word overallLength;
2014-05-08 15:34:28 +02:00
word i;
2014-05-15 14:43:52 +02:00
dataLength = 2 * count;
overallLength = maxLength - 2*123 + dataLength;
ModbusMakeHeader(mbw.Header, overallLength);
2014-05-08 15:34:28 +02:00
// Payload
2014-05-15 14:43:52 +02:00
mbw.Header.FuncCode = funcCode; // [1] Function Code; 16: Write Multiple Registers (AOs)
2014-05-08 15:34:28 +02:00
mbw.Address = address; // [2] Output address
mbw.Count = count; // [2] Number of items; 1:max 123=0x7B
2014-05-15 14:43:52 +02:00
mbw.ByteCount = dataLength; // [1] Number of bytes; = 2 * count
for (i = 0; i < dataLength; i++)
mbw.Data[i] = values[i];
2014-05-08 15:34:28 +02:00
memcpy_h2n(buffer, mbw);
2014-05-21 13:26:45 +02:00
ModbusSend(buffer, overallLength, mbw.Header.TxID);
2014-05-15 14:43:52 +02:00
}
/// <ModbusWriteRegisters>
void OnModbusConfirmRegisters(byte buffer[])
{
struct ModbusResConfirmMultiple mbc;
memcpy_n2h(mbc, buffer);
OnModbusWriteRegistersSuccess(mbc);
}
/// <ModbusWriteRegisters>
void OnModbusConfirmRegistersException(struct ModbusApHeader mbap, enum ModbusException ex)
{
OnModbusReadBitsFailed(Exception, ex, mbap);
}
2014-05-21 13:26:45 +02:00
2014-05-15 14:43:52 +02:00
// REGION: ModbusWriteMasks ------------------------------------------------------------
/// <ModbusWriteMasks>
void ModbusWriteMasks(word address, word and, word or)
{
const word length = __size_of(struct ModbusReqWriteMasks);
const byte funcCode = 0x16;
byte buffer[length];
struct ModbusReqWriteMasks mbw;
ModbusMakeHeader(mbw.Header, length);
// Payload
mbw.Header.FuncCode = funcCode; // [1] Function Code; 22: Mask Write Registers (AO)
mbw.Address = address; // [2] Output address
mbw.And = and; // [2] AND mask
mbw.Or = or; // [2] OR mask
memcpy_h2n(buffer, mbw);
2014-05-21 13:26:45 +02:00
ModbusSend(buffer, length, mbw.Header.TxID);
2014-05-08 15:34:28 +02:00
}
2014-05-15 14:43:52 +02:00
/// <ModbusWriteMasks>
void OnModbusConfirmMasks(byte buffer[])
2014-05-08 15:34:28 +02:00
{
2014-05-15 14:43:52 +02:00
struct ModbusResConfirmMasks mbc;
memcpy_n2h(mbc, buffer);
OnModbusWriteMasksSuccess(mbc);
2014-05-08 15:34:28 +02:00
}
2014-05-15 14:43:52 +02:00
/// <ModbusWriteMasks>
void OnModbusConfirmMasksException(struct ModbusApHeader mbap, enum ModbusException ex)
{
OnModbusReadBitsFailed(Exception, ex, mbap);
}
2014-05-08 15:34:28 +02:00
2014-05-15 14:43:52 +02:00
// REGION: ModbusReadWriteRegisters -------------------------------------------------------
/// <ModbusReadWriteRegisters>
void ModbusReadWriteRegisters(word readAddress, word readCount, word writeAddress, word writeCount, int values[])
2014-05-08 15:34:28 +02:00
{
2014-05-15 14:43:52 +02:00
const word maxLength = __size_of(struct ModbusReqReadWriteRegisters);
const byte funcCode = 0x17;
byte buffer[maxLength];
struct ModbusReqReadWriteRegisters mbw;
byte dataLength;
word overallLength;
word i;
dataLength = 2 * writeCount;
overallLength = maxLength - 2*121 + dataLength;
ModbusMakeHeader(mbw.Header, overallLength);
// Payload
mbw.Header.FuncCode = funcCode; // [1] Function Code; 16: Write Multiple Registers (AOs)
mbw.ReadAddress = readAddress; // [2] Input address
mbw.ReadCount = readCount; // [2] Number of items; 1:max 125=0x7D
mbw.WriteAddress = writeAddress;// [2] Output address
mbw.WriteCount = writeCount; // [2] Number of items; 1:max 121=0x79
mbw.ByteCount = dataLength; // [1] Number of bytes; = 2 * count
for (i = 0; i < dataLength; i++)
mbw.Data[i] = values[i];
memcpy_h2n(buffer, mbw);
2014-05-21 13:26:45 +02:00
ModbusSend(buffer, overallLength, mbw.Header.TxID);
2014-05-15 14:43:52 +02:00
}
/// <ModbusReadWriteRegisters>
void OnModbusReceiveConfirmRegisters(byte buffer[])
{
byte numRegs;
struct ModbusResReceiveRegisters mbr;
memcpy_n2h(mbr, buffer);
2014-05-21 13:26:45 +02:00
numRegs = (gQueueAck[mbr.Header.TxID].Buffer[10] << 8) + gQueueAck[mbr.Header.TxID].Buffer[11];
2014-05-15 14:43:52 +02:00
OnModbusReadRegistersSuccess(mbr, numRegs);
}
/// <ModbusReadWriteRegisters>
void OnModbusReceiveConfirmRegistersException(struct ModbusApHeader mbap, enum ModbusException ex)
{
OnModbusReadBitsFailed(Exception, ex, mbap);
}
// ------------------------------------------------------------------------------------
// REGION: OnModbusReceive ------------------------------------------------------------
2014-05-21 13:26:45 +02:00
/// <-OnModbusReceive>
2014-05-15 14:43:52 +02:00
void OnModbusReceive(dword socket, long result, dword address, dword port, byte buffer[], dword size)
{
if (result == 0)
{
if (size == 0)
{
// Size of zero indicates that the socket was closed by the communication peer.
2014-05-15 17:05:20 +02:00
writeLineEx(0, 2, "<%NODE_NAME%> OnTcpReceive: Socket closed by peer");
2014-05-15 14:43:52 +02:00
gSocket.Close();
gSocketState = CLOSED;
}
else
{
// Sucessfully received some bytes over the TCP/IP connection.
OnModbusReceive2(buffer, size);
}
}
else
2014-05-08 15:34:28 +02:00
{
2014-05-15 14:43:52 +02:00
gIpLastErr = gSocket.GetLastSocketError();
gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr));
2014-05-15 17:05:20 +02:00
writeLineEx(0, 2, "<%NODE_NAME%> OnTcpReceive error (%d): %s", gIpLastErr, gIpLastErrStr);
2014-05-15 14:43:52 +02:00
gSocket.Close();
gSocketState = CLOSED;
}
}
2014-05-21 13:26:45 +02:00
/// <-OnModbusReceive>
2014-05-15 14:43:52 +02:00
void OnModbusReceive2(byte buffer[], dword size)
{
struct ModbusApHeader mbap;
int offset;
2014-05-15 14:56:23 +02:00
char str[3*20];
2014-05-15 14:43:52 +02:00
if (size < 8) // No complete Modbus Application Header
2014-05-08 15:34:28 +02:00
return;
2014-05-15 14:43:52 +02:00
offset = 0;
//write("OnModbusReceive2: size = %d", size);
do
{
//write("OnModbusReceive2: offset pre = %d", offset);
memcpy_n2h(mbap, buffer, offset);
OnModbusReceive2OnePacket(buffer, offset, mbap);
offset += __offset_of(struct ModbusApHeader, UnitID) + mbap.Length;
//write("OnModbusReceive2: offset post = %d. %d <= %d?", offset, offset, size-8);
2014-05-08 15:34:28 +02:00
}
2014-05-15 14:43:52 +02:00
while(offset <= size-8); // We need at least 8 bytes for a new packet
//write("OnModbusReceive2: yes. finished", offset);
if (offset != size) // Can be removed.
{
2014-05-15 14:56:23 +02:00
bin_to_strhex(buffer, str);
2014-05-21 13:26:45 +02:00
writeLineEx(0, 3, "OnModbusReceive2: Error while going through receive buffer. Our final offset is %d, but the size of the buffer is %d! Buffer: %s", offset, size, str);
2014-05-15 14:43:52 +02:00
runError(1002, 1);
}
}
2014-05-21 13:26:45 +02:00
/// <-OnModbusReceive>
2014-05-15 14:43:52 +02:00
void OnModbusReceive2OnePacket(byte buffer[], int offset, struct ModbusApHeader mbap)
{
// Test transaction identifier?
// Test unit/device identifier?
word i; // counter
word length; // length of current packet
byte mbuffer[__size_of(struct ModbusResReceiveRegisters)]; // second buffer where we copy the message. This way the user won't overwrite other packages.
length = __offset_of(struct ModbusApHeader, UnitID) + mbap.Length;
// We cannot check this properly anymore. We have to trust the TCP/UDP stack and the sender... *sigh*
if (elCount(buffer) < offset + length) // TCP packet not as large enough
2014-05-08 15:34:28 +02:00
{
2014-05-15 17:05:20 +02:00
writeLineEx(0, 3, "<%NODE_NAME%> OnModbusReceive Error: Modbus packet did not fit into Buffer: buffer length = %d, packet length = %d, offset = %d", elCount(buffer), __offset_of(struct ModbusApHeader, UnitID) + mbap.Length, offset);
2014-05-15 14:43:52 +02:00
// I REALLY don't want to assemble the two package fragments.
runError(1001,1);
2014-05-08 15:34:28 +02:00
return;
}
2014-05-15 14:43:52 +02:00
if (mbap.Protocol != 0) // Protocol is not Modbus (0x0000). Wayne.
2014-05-08 15:34:28 +02:00
{
2014-05-15 17:05:20 +02:00
writeLineEx(0, 2, "<%NODE_NAME%> OnModbusReceive Error: Tcp packet is no Modbus packet: Protocol = %d", mbap.Protocol);
2014-05-08 15:34:28 +02:00
return;
}
// MBAP Header is OK :) Go on
2014-05-15 14:43:52 +02:00
2014-05-21 13:26:45 +02:00
if (!gQueueSent.ContainsKey(mbap.TxID)) // We don't wait for this message!
return;
memcpy(gQueueAck[mbap.TxID], gQueueSent[mbap.TxID]);
gQueueSent.Remove(mbap.TxID);
2014-05-15 14:43:52 +02:00
if (mbap.FuncCode > 0x80) // Oh no, we got a exception!
2014-05-08 15:34:28 +02:00
{
2014-05-15 14:43:52 +02:00
OnModbusReceive2Exceptions(buffer[offset+08], mbap);
2014-05-21 13:26:45 +02:00
gQueueAck.Remove(mbap.TxID); // Remove from acknowledge queue
2014-05-15 14:43:52 +02:00
return;
2014-05-08 15:34:28 +02:00
}
2014-05-15 14:43:52 +02:00
// Copy the message
memcpy_off(mbuffer, 0, buffer, offset, length);
2014-05-08 15:34:28 +02:00
// Let's give the PDU to the corresponding function
switch (mbap.FuncCode)
{
case 0x01:
case 0x02:
2014-05-15 14:43:52 +02:00
OnModbusReceiveBits(mbuffer);
2014-05-08 15:34:28 +02:00
break;
case 0x03:
case 0x04:
2014-05-15 14:43:52 +02:00
OnModbusReceiveRegisters(mbuffer);
2014-05-08 15:34:28 +02:00
break;
case 0x05:
2014-05-15 14:43:52 +02:00
OnModbusConfirmBit(mbuffer);
break;
case 0x06:
OnModbusConfirmRegister(mbuffer);
2014-05-08 15:34:28 +02:00
break;
case 0x0F:
2014-05-15 14:43:52 +02:00
OnModbusConfirmBits(mbuffer);
2014-05-08 15:34:28 +02:00
break;
case 0x10:
2014-05-15 14:43:52 +02:00
OnModbusConfirmRegisters(mbuffer);
break;
case 0x16:
OnModbusConfirmMasks(mbuffer);
break;
case 0x17:
OnModbusReceiveConfirmRegisters(mbuffer);
break;
default:
writeLineEx(0, 3, "We received funcCode 0x%X?", mbap.FuncCode);
}
2014-05-21 13:26:45 +02:00
gQueueAck.Remove(mbap.TxID); // Remove from acknowledge queue
2014-05-15 14:43:52 +02:00
}
2014-05-21 13:26:45 +02:00
/// <-OnModbusReceive>
2014-05-15 14:43:52 +02:00
void OnModbusReceive2Exceptions(byte exCode, struct ModbusApHeader mbap)
{
enum ModbusException ex;
ex = (enum ModbusException)exCode;
switch(exCode) // Let's have a look at the reason
{
case 0x01: // Illegal function code: Implementation failure!
2014-05-15 17:05:20 +02:00
writeLineEx(0, 3, "<%NODE_NAME%> Exception: Illegal function code (0x01). The function code %d is unknown by the server.", mbap.FuncCode & 0x0F);
2014-05-15 14:43:52 +02:00
break;
case 0x02: // Illegal data address: Configuration failure!
2014-05-15 17:05:20 +02:00
writeLineEx(0, 3, "<%NODE_NAME%> Exception: Illegal data address (0x02). Please check your configuration!");
2014-05-15 14:43:52 +02:00
break;
case 0x03: // Illegal data value: Depends.
2014-05-15 17:05:20 +02:00
writeLineEx(0, 3, "<%NODE_NAME%> Exception: Illegal data value (0x03).");
2014-05-15 14:43:52 +02:00
break;
case 0x04: // Server failure: We can't do anything about that, can we?
2014-05-15 17:05:20 +02:00
writeLineEx(0, 3, "<%NODE_NAME%> Exception: Server failure (0x04). The server failed during execution.");
2014-05-15 14:43:52 +02:00
break;
case 0x05: // Acknowledge: That's ok.
2014-05-15 17:05:20 +02:00
writeLineEx(0, 1, "<%NODE_NAME%> Exception: Acknowledge (0x05). The server simply needs more time to generate the response.");
2014-05-15 14:43:52 +02:00
break;
case 0x06: // Server busy: We should resend the request.
2014-05-15 17:05:20 +02:00
writeLineEx(0, 3, "<%NODE_NAME%> Exception: Server busy (0x06). The request could not be accepted.");
2014-05-15 14:43:52 +02:00
break;
case 0x0A: // Gateway problem: We don't have gateways :)
2014-05-15 17:05:20 +02:00
writeLineEx(0, 3, "<%NODE_NAME%> Exception: Gateway problem (0x0A). Gateway paths not available.");
2014-05-15 14:43:52 +02:00
break;
case 0x0B: // Gateway problem: We don't have gateways :)
2014-05-15 17:05:20 +02:00
writeLineEx(0, 3, "<%NODE_NAME%> Exception: Gateway problem (0x0B). The targeted device failed to respond.");
2014-05-15 14:43:52 +02:00
break;
}
switch (mbap.FuncCode)
{
case 0x81:
case 0x82:
OnModbusReceiveBitsException(mbap, ex);
break;
case 0x83:
case 0x84:
OnModbusReceiveRegistersException(mbap, ex);
break;
case 0x85:
OnModbusConfirmBitException(mbap, ex);
break;
case 0x86:
OnModbusConfirmRegisterException(mbap, ex);
break;
case 0x8F:
OnModbusConfirmBitsException(mbap, ex);
break;
case 0x90:
OnModbusConfirmRegistersException(mbap, ex);
break;
case 0x96:
OnModbusConfirmMasksException(mbap, ex);
break;
case 0x97:
OnModbusReceiveConfirmRegistersException(mbap, ex);
2014-05-08 15:34:28 +02:00
break;
}
2014-05-21 13:26:45 +02:00
}
// ------------------------------------------------------------------------------------
// REGION: ModbusSend -----------------------------------------------------------------
/// <-ModbusSend>
void ModbusSend(byte buffer[], word length, word TxID)
{
struct QueueElement qe;
qe.Length = length;
memcpy(qe.Buffer, buffer, length);
memcpy(gQueuePending[TxID], qe);
if (gQueuePending.Size() == 1 && gQueueSent.Size() == 0) // start timer at beginning
setTimerCyclic(gtRobin, 1);
}
/// <-ModbusSend>
on timer gtRobin
{
struct ModbusApHeader mbap;
struct QueueElement qe;
enum ModbusRequestError reqError;
//writeLineEx(0, 1, "Queue Sent: %d, Queue Pending: %d, Queue Ack: %d", gQueueSent.Size(), gQueuePending.Size(), gQueueAck.Size());
// First: check timeouts = packets that were sent in previous run and not removed by response
for (long TxID : gQueueSent)
{
if (++gQueueSent[TxID].TimeoutTicks < @sysvar::Config::Modbus::RequestTimeout) // not timed out yet
continue;
// timed out!
if (++gQueueSent[TxID].Timeouts < @sysvar::Config::Modbus::MaxTransmissionCount) // if we may resend it
{
gQueueSent[TxID].TimeoutTicks = 0;
ModbusSnd(gQueueSent[TxID].Buffer, gQueueSent[TxID].Length); // resend it
reqError = Timeout;
ModbusRecv();
}
else // we will NOT resend it
{
reqError = FinalTimeout;
}
memcpy_n2h(mbap, qe.Buffer);
switch(mbap.FuncCode) // throw an error in each case
{
case ReadBits1:
case ReadBits2:
OnModbusReadBitsFailed(reqError, None, mbap);
break;
case ReadRegisters1:
case ReadRegisters2:
OnModbusReadRegistersFailed(reqError, None, mbap);
break;
case WriteBit:
OnModbusWriteBitFailed(reqError, None, mbap);
break;
case WriteRegister:
OnModbusWriteRegisterFailed(reqError, None, mbap);
break;
case WriteBits:
OnModbusWriteBitsFailed(reqError, None, mbap);
break;
case WriteRegisters:
OnModbusWriteRegistersFailed(reqError, None, mbap);
break;
case MaskRegister:
OnModbusWriteMasksFailed(reqError, None, mbap);
break;
case ReadWriteRegisters:
OnModbusReadWriteRegistersFailed(reqError, None, mbap);
break;
}
if (reqError == FinalTimeout) // remove the packet from queue
gQueueSent.Remove(TxID); // wait until here to let the methods access the request
}
// Second: send new packets
for (long TxID : gQueuePending)
{
if (gQueueSent.Size() > 4) // Wago 750-881 cannot handle more than 5 messages at a time :(
continue;
ModbusSnd(gQueuePending[TxID].Buffer, gQueuePending[TxID].Length); // send packet
memcpy(gQueueSent[TxID], gQueuePending[TxID]); // move packet to sent queue
gQueuePending.Remove(TxID);
ModbusRecv();
}
if (gQueueSent.Size() == 0) // Stop timer to reduce latency of first packet
this.Cancel();
2014-05-08 15:34:28 +02:00
}