From 2df8f31eb735b164a816738124f96becf2e95c3e Mon Sep 17 00:00:00 2001 From: Jonny007-MKD Date: Fri, 4 Jul 2014 11:29:18 +0000 Subject: [PATCH] Moved all sysvars to PollingModbusClient.can Fixed bug when splitting Modbus write requests --- .../include/CAPL/PollingModbusClient.can | 2 +- .../include/CAPL/include/ModbusClient.cin | 55 +++++++++++++------ 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/Modbus-CAPL/include/CAPL/PollingModbusClient.can b/Modbus-CAPL/include/CAPL/PollingModbusClient.can index 067c373..98dc343 100644 --- a/Modbus-CAPL/include/CAPL/PollingModbusClient.can +++ b/Modbus-CAPL/include/CAPL/PollingModbusClient.can @@ -31,7 +31,7 @@ on start DeviceInit(@sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::Vendor); // Set all device specific parameters (Wago / B&R) writeDbg(MbInfo, "Connecting to %s:%d", ip, @sysvar::Config::Modbus::Port); - ModbusInit(ip, @sysvar::Config::Modbus::Port); // Connect to device. Opens socket and connection or what ever + ModbusInit(ip, @sysvar::Config::Modbus::Port, @sysvar::Config::Modbus::RequestTimeout, @sysvar::Config::Modbus::MaxTransmissionCount); // Connect to device. Opens socket and connection or what ever ModbusReadOutBits(gDevOutputBitAddrR, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputBits); // Read the start status of the output bits ModbusReadOutRegisters(gDevOutputRegAddrR, @sysvar::%BUS_TYPE%%CHANNEL%::%NODE_NAME%::Info::OutputRegisters); // Read the start status of the output registers diff --git a/Modbus-CAPL/include/CAPL/include/ModbusClient.cin b/Modbus-CAPL/include/CAPL/include/ModbusClient.cin index d2f175f..4f78118 100644 --- a/Modbus-CAPL/include/CAPL/include/ModbusClient.cin +++ b/Modbus-CAPL/include/CAPL/include/ModbusClient.cin @@ -44,11 +44,13 @@ variables { msTimer gtModbusRobin; // Timer that sends the packets and watches for timeouts word gTxID = 0x0000; // Transaction Identifier for Modbus. Used as index for gQueue + word gRequestTimeout; // Timeout of a packet [ms] + byte gMaxTransmissionCount; // Maximum number of transmissions (after timeouts) // Global storage for pending and sent requests, associated by TxID struct QueueElement { - word TimeoutTicks; // Time counter [ms]. Used to watch for timeouts (see @sysvar::Config::Modubs::RequestTimeout) + word TimeoutTicks; // Time counter [ms]. Used to watch for timeouts (see gRequestTimeout) byte Timeouts; word Length; byte Buffer[gModbusMaxTelegramSize]; @@ -73,10 +75,13 @@ variables } // This method prepares anything that sending Modbus requests works. Currently this is only the connection. -// It has to be called by the user/modbus client at the beginning with IP and Port of the Modbus server -void ModbusInit(char Remote_IP[], word remotePort) +// It has to be called by the user/modbus client at the beginning with IP and Port of the Modbus server, +// the timeout value and the number of retries that shall be performed after a timeout +void ModbusInit(char Remote_IP[], word remotePort, word requestTimeout, byte maxRetransmissions) { _ModbusConnectTo(Remote_IP, remotePort); + gRequestTimeout = requestTimeout; + gMaxTransmissionCount = maxRetransmissions; } // This method fills the ModbusApHeader structure 'mbap'. @@ -364,6 +369,7 @@ void ModbusWriteBits(word address, long count, byte values[]) byte dataLength; byte overallLength; word i; + long offset; // FC15: Write Multiple Bits (DOs) while (count > 0) @@ -380,12 +386,18 @@ void ModbusWriteBits(word address, long count, byte values[]) dataLength = _ceil(curCount / 8.0); overallLength = maxLength - gMaxBitsPerWrite/8 + dataLength; } + if (curCount == 1) + { + ModbusWriteBit(address+offset, values[offset/8]); + return; + } _ModbusMakeHeader(mbreq.Header, overallLength, funcCode); - mbreq.Address = address; // [2] Output address + mbreq.Address = address+offset; // [2] Output address mbreq.Count = curCount; // [2] Number of items; 1:max 1968=0x7B0 mbreq.ByteCount = dataLength; // [1] Number of bytes; = ceil(count/8) - memcpy(mbreq.Data, values, dataLength); // [246] Data; this is 1 unneccessary memcpy :( Well, readability... + for (i = 0; i < dataLength; i++) // [264] Byte status, 8 per byte + mbreq.Data[i] = values[i+offset/8]; writeDbg(MbDebug, "Sending 'Write Bits' (0x0F) command. Addr: 0x%04X, Count: %d", address, curCount); @@ -393,7 +405,7 @@ void ModbusWriteBits(word address, long count, byte values[]) _ModbusSend(buffer, overallLength, mbreq.Header.TxID); count -= gMaxBitsPerWrite; - address += gMaxBitsPerWrite; + offset += gMaxBitsPerWrite; } } @@ -478,27 +490,38 @@ void ModbusWriteRegisters(word address, long count, word values[]) byte dataLength; word overallLength; word i; + long offset; // FC16: Write Multiple Registers (AOs) + offset = 0; while (count > 0) { curCount = count > gMaxRegsPerWrite ? gMaxRegsPerWrite : count; dataLength = 2 * curCount; overallLength = maxLength - 2*gMaxRegsPerWrite + dataLength; + if (curCount == 1) + { + ModbusWriteRegister(address, values[offset]); + return; + } + _ModbusMakeHeader(mbreq.Header, overallLength, funcCode); - mbreq.Address = address; // [2] Output address + mbreq.Address = address+offset; // [2] Output address mbreq.Count = curCount; // [2] Number of items; 1:max 123=0x7B mbreq.ByteCount = dataLength; // [1] Number of bytes; = 2 * count for (i = 0; i < curCount; i++) - mbreq.Data[i] = values[i]; + mbreq.Data[i] = values[i+offset]; for ( ; i < gMaxRegsPerWrite; i++) // do we need this? mbreq.Data[i] = 0; memcpy_h2n(buffer, mbreq); _ModbusSend(buffer, overallLength, mbreq.Header.TxID); + + count -= gMaxRegsPerWrite; + offset += gMaxRegsPerWrite; } } @@ -750,7 +773,7 @@ void _OnModbusReceive2OnePacket(byte buffer[], int offset, struct ModbusApHeader if (mbap.FuncCode > 0x80) // Oh no, we got a exception! _OnModbusReceive2Exceptions(buffer[offset+08], mbap); else // Ok, everything is alright - _OnModbusReceive2Success(buffer, mbap); + _OnModbusReceive2Success(buffer, mbap, offset, length); gQueueAck.Remove(mbap.TxID); // Remove from acknowledge queue } @@ -797,7 +820,7 @@ void _OnModbusReceive2Exceptions(byte exCode, struct ModbusApHeader mbap) /// <-OnModbusReceive> // This method will hand the received data to the correct Success() function // It gets called by _OnModbusReceive2OnePacket() -void _OnModbusReceive2Success(byte exCode, struct ModbusApHeader mbap) +void _OnModbusReceive2Success(byte buffer[], struct ModbusApHeader mbap, int offset, word length) { byte mbuffer[gModbusMaxTelegramSize]; // second buffer where we copy the message. This way the user won't overwrite other packages. // Copy the message @@ -880,25 +903,25 @@ on timer gtModbusRobin // 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 + if (++gQueueSent[TxID].TimeoutTicks < gRequestTimeout) // not timed out yet continue; - // timed out! - if (++gQueueSent[TxID].Timeouts < @sysvar::Config::Modbus::MaxTransmissionCount) // if we may resend it + // timed out! + if (++gQueueSent[TxID].Timeouts < gMaxTransmissionCount) // if we may resend it { writeDbg(ConnInfo, "Packet 0x%04X timed out! Retrying...", TxID); gQueueSent[TxID].TimeoutTicks = 0; - _ModbusSnd(gQueueSent[TxID].Buffer, gQueueSent[TxID].Length); // resend it + _ModbusSnd(gQueueSent[TxID].Buffer, gQueueSent[TxID].Length); // resend it reqError = Timeout; _ModbusRecv(); } - else // we will NOT resend it + else // we will NOT resend it { writeDbg(ConnWarning, "Packet 0x%04X timed out! Giving up", TxID); reqError = FinalTimeout; } memcpy_n2h(mbap, gQueueSent[TxID].Buffer); - switch(mbap.FuncCode) // throw an "error" in each case + switch(mbap.FuncCode) // throw an "error" in each case { case ReadBits1: case ReadBits2: