diff --git a/Modbus-CAPL/include/CAPL/include/ModbusClient.cin b/Modbus-CAPL/include/CAPL/include/ModbusClient.cin index ea15c17..b64fca4 100644 --- a/Modbus-CAPL/include/CAPL/include/ModbusClient.cin +++ b/Modbus-CAPL/include/CAPL/include/ModbusClient.cin @@ -78,11 +78,11 @@ 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, // 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) +void ModbusInit(char Remote_IP[], word remotePort, word requestTimeout, byte maxTransmissions) { _ModbusConnectTo(Remote_IP, remotePort); gRequestTimeout = requestTimeout; - gMaxTransmissionCount = maxRetransmissions; + gMaxTransmissionCount = maxTransmissions; } // This method cleans up everything: Closes the connection, clears the queues and stops the timer @@ -116,7 +116,7 @@ void ModbusReadInBits(word address, long count) _ModbusReadBits(ReadBitsIn, address, count); } /// -// This method will submit a request to the Modbus server to read coils +// This method will submit a request to the Modbus server to read discrete inputs void ModbusReadBits(word address, long count) { _ModbusReadBits(ReadBitsIn, address, count); @@ -135,11 +135,38 @@ void _ModbusReadBits(enum ModbusFuncCode funcCode, word address, long count) byte buffer[length]; word curCount; struct ModbusReqRead mbreq; + word devStartAddr, devEndAddr; - // FC1: Read Coils (DO), FC2: Read Discret Inputs (DI) - while (count > 0) + // Check the function code. If this method was private we would not need this + switch (funcCode) { - curCount = count > gMaxBitsPerRead ? gMaxBitsPerRead : count; + case ReadBitsOut: + case ReadBitsIn: + break; + default: + writeDbg(MbError, "_ModbusReadBits: Got incorrect function code 0x%02X!", funcCode); + OnModbusClientPanics(FuncCodeIncorrect); + return; + } + + devStartAddr = funcCode == ReadBitsOut ? thisDev.Addr.Read.OutputBits : thisDev.Addr.Read.InputBits; // The start address of the bits + devEndAddr = devStartAddr + thisDev.MaxBitCount; // The address behind the last bit + if (address < devStartAddr) // Oh, reading at the wrong address? + { + writeDbg(MbError, "_ModbusReadBits: The given start address 0x%04X is smaller than the obligatory start address 0x%04X", address, devStartAddr); + OnModbusClientPanics(AddressFailure); + return; + } + // FC1: Read Coils (DO), FC2: Read Discret Inputs (DI) + while (count > 0 && address < devEndAddr) + { + curCount = count > gMaxBitsPerRead ? gMaxBitsPerRead : count; // divide packets that are too large + if (address + curCount > devEndAddr) // upper bound in process image + { + writeDbg(MbWarning, "_ModbusReadBits: Impossible to read %d bits at 0x%04X. Changed count to %d.", curCount, address, devEndAddr - address); + curCount = devEndAddr - address; + } + _ModbusMakeHeader(mbreq.Header, length, funcCode); mbreq.Address = address; // [2] Start address @@ -219,15 +246,45 @@ void _ModbusReadRegisters(enum ModbusFuncCode funcCode, word address, long count byte buffer[length]; word curCount; struct ModbusReqRead mbreq; + word devStartAddr, devEndAddr; // Check the function code. If this method was private we would not need this - // FC3: Read Holding Registers (AO), FC4: Read Input Registers (AI) - while (count > 0) + switch (funcCode) { - curCount = count > gMaxRegsPerRead ? gMaxRegsPerRead : count; + case ReadRegistersOut: + case ReadRegistersIn: + break; + default: + writeDbg(MbError, "_ModbusReadRegisters: Got incorrect function code 0x%02X!", funcCode); + OnModbusClientPanics(FuncCodeIncorrect); + return; + } + + devStartAddr = funcCode == ReadRegistersOut ? thisDev.Addr.Read.OutputRegisters : thisDev.Addr.Read.InputRegisters; // The start address of the bits + devEndAddr = devStartAddr + thisDev.MaxRegisterCount; // The address behind the last register + if (address >= devEndAddr) + devEndAddr = 0xFFFF; // Some other address. We might be reading extra registers (no input) + + if (address < devStartAddr) // Oh, reading at the wrong address? + { + writeDbg(MbError, "_ModbusReadRegisters: The given start address 0x%04X is smaller than the obligatory start address 0x%04X", address, devStartAddr); + OnModbusClientPanics(AddressFailure); + return; + } + // FC3: Read Holding Registers (AO), FC4: Read Input Registers (AI) + while (count > 0 && address < devEndAddr) + { + curCount = count > gMaxRegsPerRead ? gMaxRegsPerRead : count; // divide packets that are too large + if (address + curCount > devEndAddr) // upper bound in process image + { + writeDbg(MbWarning, "_ModbusReadBits: Impossible to read %d bits at 0x%04X. Changed count to %d.", curCount, address, devEndAddr - address); + curCount = devEndAddr - address; + } + write("address = 0x%04X, count = %d, curCount = %d, devStartAddr = 0x%04X, devEndAddr = 0x%04X", address, count, curCount, devStartAddr, devEndAddr); + _ModbusMakeHeader(mbreq.Header, length, funcCode); mbreq.Address = address; // [2] Start address - mbreq.Count = curCount; // [2] Number of items; 1:max 125=0x7D + mbreq.Count = curCount; // [2] Number of items; 1:max 125=0x7D writeDbg(MbDebug, "Sending 'Read Registers' (0x03) command. TxID: 0x%04X, Addr: 0x%04X, Count: %d", mbreq.Header.TxID, address, curCount); @@ -378,7 +435,7 @@ void ModbusWriteBits(word address, long count, byte values[]) struct ModbusReqWriteBits mbreq; word curCount; byte dataLength; - byte overallLength; + word overallLength; word i; long offset; @@ -406,7 +463,7 @@ void ModbusWriteBits(word address, long count, byte values[]) 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); + writeDbg(MbDebug, "Sending 'Write Bits' (0x0F) command. TxID: 0x%04X, Addr: 0x%04X, Count: %d", mbreq.Header.TxID, address, curCount); memcpy_h2n(buffer, mbreq); _ModbusSend(buffer, overallLength, mbreq.Header.TxID); @@ -423,17 +480,14 @@ void ModbusWriteBits(word address, long count, byte values[]) void ModbusWriteBitsB(word address, long count, byte values[]) { byte buffer[0x4000/8]; // Maximum value from B&R devices. *sigh* - word length; + long length; dword ellCount; dword i; byte j; char str1[20*2], str2[20*3]; - length = (word)_ceil(count / 8.0); - writeDbg(AlgoDebug, "ModbusWriteBitsB: count: %d; length: %d", count, length); - - if (count % 8 != 0) - length--; + length = (long)_floor(count / 8.0); + writeDbg(AlgoDebug, "ModbusWriteBitsB: count: %d; length: %d", count, length+1); for (i = 0; i < length; i++) { @@ -441,20 +495,20 @@ void ModbusWriteBitsB(word address, long count, byte values[]) for (j = 0; j < 8; j++) { buffer[i] |= (values[i*8 + j] & 0x01) << j; - writeDbg(AlgoDebug, "ModbusWriteBitsB: j: %d; indx: %d; value: %d; mask: %X", j, i*8 + j, values[i*8 + j], (0x01 << j)); + writeDbg(AlgoDebug, "ModbusWriteBitsB: j: %d; indx: %d; value: %d; mask: %X", j, i*8 + j, values[i*8 + j], (0x01 << j)); } - writeDbg(AlgoDebug, "ModbusWriteBitsB: %d: %X", i, buffer[i]); + writeDbg(AlgoDebug, "ModbusWriteBitsB: Byte %d = %X", i, buffer[i]); } for (j = 0; j < count % 8; j++) // wont be executed if there is no remainder { - writeDbg(AlgoDebug, "ModbusWriteBitsB: j: %d; indx: %d; value: %d; mask: %X", j, (length-1)*8 + j, values[(length-1)*8 + j], (0x01 << j)); - buffer[length] |= (values[(length)*8 + j] & 0x01) << j; + writeDbg(AlgoDebug, "ModbusWriteBitsB: j: %d; indx: %d; value: %d; mask: %X", j, i*8 + j, values[i*8 + j], (0x01 << j)); + buffer[i] |= (values[(i)*8 + j] & 0x01) << j; } - writeDbg(AlgoDebug, "ModbusWriteBitsB: %d: %X", length-1, buffer[length-1]); + writeDbg(AlgoDebug, "ModbusWriteBitsB: Byte %d = %X", i, buffer[i]); hbin_to_strhex(values, str1); bin_to_strhex(buffer, str2); - writeDbg(AlgoDebug, "ModbusWriteBitsB: Encoded %s to %s", str1, str2); + writeDbg(AlgoDebug, "ModbusWriteBitsB: Encoded %s to %s (%d -> %d)", str1, str2, count, length); ModbusWriteBits(address, count, buffer); } @@ -555,6 +609,10 @@ void _OnModbusConfirmRegistersException(struct ModbusApHeader mbap, enum ModbusE // REGION: ModbusWriteMasks ------------------------------------------------------------ /// // This method will submit a request to the Modbus server to mask a holding registers +/// The register will be using the following rule: +/// Result = (Content & AndMask) | (Content & !AndMask) +/// This means, you must not AND the bits you are trying to OR. +/// Example: You want to OR the 4th bit --> AND = 0xFFFB, OR = 0x0004 void ModbusWriteMasks(word address, word and, word or) { const word length = __size_of(struct ModbusReqWriteMasks);