/*@!Encoding:1252*/ // The state machine in this file will use all functions of the Modbus Client and check the results // The states are: // // 0: Get device information [ -> OnModbusReadRegistersSuccess ] // 5: Reading output bits (check functionality of 0x01) [ -> OnModbusReadBitsSuccess ] // 10: Reading output registers (check functionality of 0x03) [ -> OnModbusReadRegistersSuccess ] // 20: Reading input bits (check functionality of 0x02) [ -> OnModbusReadBitsSuccess ] // 30: Reading input registers (check functionality of 0x04) [ -> OnModbusReadRegistersSuccess -> timr (30 or 130) ] // 40: Write Bits 0x0 - 0x9 to 1 (check functionality of 0x0F) [ -> OnModbusWriteBitsSuccess ] // 50: Write Bit 0x2 to 0 (check functionality of 0x05) [ -> OnModbusWriteBitSuccess -> timr ] // 60: Read Bits 0x0 - 0x9 and check whether result is (check result of 0x0F and 0x05: 1 1 0 1 1 1 1 1 1) [ -> OnModbusReadBitsSuccess ] // 70: Write 1 output registers 0x0 to 0x55555 (check functionality of 0x10) [ -> OnModbusWriteRegistersSuccess -> timr ] // 75: Read output register 0x0 and check (check result of 0x10) [ -> OnModbusReadRegistersSuccess ] // 80: Write output register 0x0 to 0x9999 (check functionality of 0x06) [ -> OnModbusWriteRegisterSuccess -> timr ] // 90: Read output register 0x0 (check result of 0x06) [ -> OnModbusReadRegistersSuccess ] // 100: Writing masks (&00F9 |0x006) to 0x0 (check functionality of 0x16) [ -> OnModbusWriteMasksSuccess -> timr ] // 110: Read output register 0x0 and check the success of 100 (check result of 0x16) [ -> OnModbusReadRegistersSuccess ] // 120: Read 3000 input bits (check whether read bit telegrams can be divided) [ -> OnModbusReadBitsSuccess ] // 130: [ -> OnModbusReadBitsSuccess -> timr ] // 140: Read 300 input registers (check whether read register telegrams can be divided) [ -> OnModbusReadRegistersSuccess ] // 145: [ -> OnModbusReadRegistersSuccess ] // 150: [ -> OnModbusReadRegistersSuccess -> timr (160 or 200) ] // 160: Write 3000 Bits at 0x0 to 0 [ -> OnModbusWriteBitsSuccess ] // 170: Write 3000 Bits at 0x0 to 0 [ -> OnModbusWriteBitsSuccess ] // 180: Write 200 registers (check whether telegrams can be divided) [ -> OnModbusWriteRegistersSuccess ] // 190: [ -> OnModbusWriteRegistersSuccess -> timr ] // 900: Testing the receive window size [ -> OnModbusReadBitsSuccess -> 900 ] // 900: Stop [ -> OnModbusReadBitsFailed ] // Function codes (tested functionality, check results, checked whether telegrams can be divided) // func result divided (B+R) // 0x01 00 X (120) // Read Bits out // 0x02 20 X 120 // Read Bits in // 0x03 10 X (140) // Read Registers out // 0x04 30 X 140 // Read Registers in // 0x05 50 60 X // Write 1 Bit // 0x06 80 90 X // Write 1 Register // 0x0F 40 60 (160) // Write multiple Bits // 0x0F* 160 |___| 160 // Write multiple Bits with encoding // 0x10 70 75 180 // Write multiple Registers // 0x16 100 110 X // Mask Register // 0x17 |___| |___| |___| // Read/Write Registers includes { #include "include/DeviceInformation.cin" #include "include/ModbusUdp.cin" #include "include/ModbusClient.cin" } variables { word state = 0; byte s0i = 0; struct device s0dev; byte s90i = 1; msTimer timr; byte skipOutput = 1; } on preStart { setStartdelay(100); OutputDebugLevel = Debug; } on start { char ip[16]; sysGetVariableString("Device::Config", "IP", ip, 16); DeviceInit(@sysvar::Device::Config::Vendor); ModbusInit(ip, @sysvar::Config::Modbus::Port, @sysvar::Config::Modbus::RequestTimeout, 1/*retry*/); if (gSocketState < CONNECTING) // We are not connecting and not connected return; // Start the Test s0dev.Vendor = (enum Vendor)@sysvar::Device::Config::Vendor; s0i = _DeviceGetInformation((enum Vendor)@sysvar::Device::Config::Vendor); } on preStop { ModbusEnd(); } void OnModbusReadBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) { char reason[100]; struct ModbusReqRead mbreq; switch (error) { case Timeout: break; case FinalTimeout: strncpy(reason, "Timeout", elCount(reason)); break; case Exception: snprintf(reason, elCount(reason), "Exception: %s", ModbusExceptions[ex-1]); break; case NotSent: strncpy(reason, "Impossible to send", elCount(reason)); break; default: writeDbg(MbError, "OnModbusReadBitsFailed: Unkown error: %d", error); OnModbusClientPanics(SwitchArgumentInvalid); return; } memcpy_n2h(mbreq, gQueueAck[mbap.TxID].Buffer); switch (state) { case 0: writeDbg(MbError, "State %d. Reading %d output bit from 0x%04X did not work! Reason: %s", state, mbreq.Count, mbreq.Address, reason); break; case 20: writeDbg(MbError, "State %d. Reading %d input bit from 0x%04X did not work! Reason: %s", state, mbreq.Count, mbreq.Address, reason); break; case 60: case 120: case 130: writeDbg(MbError, "State %d. Reading %d output bits from 0x%04X did not work! Reason: %s", state, mbreq.Count, mbreq.Address, reason); break; case 200: if (error == FinalTimeout) { writeDbg(MbError, "State %d. Packet timed out! Receive window size: %d", state, s90i-1); state = 210; stop(); break; } else writeDbg(MbError, "State %d. Error while writing bit: %s", state, reason); break; default: writeDbg(MbError, "I did not expect state %d in OnModbusReadBitsFailed()!", state); break; } runError(1001, 0); } void OnModbusReadBitsSuccess(struct ModbusResReceiveBits mbres, byte bitStatus[], struct ModbusReqRead mbreq) { byte i; word s6[1] = {0x5555}; switch (state) { case 5: writeDbg(MbDebug, "State %d. Successfully read %d output bit from 0x%04X", state, mbreq.Count, mbreq.Address); ModbusReadOutRegisters(thisDev.Addr.Read.OutputRegisters, 1); state = 10; break; case 20: writeDbg(MbDebug, "State %d. Successfully read %d input bit from 0x%04X", state, mbreq.Count, mbreq.Address); ModbusReadInRegisters(thisDev.Addr.Read.InputRegisters, 1); state = 30; break; case 60: writeDbg(MbDebug, "State %d. Successfully read %d output bits from 0x%04X", state, mbreq.Count, mbreq.Address); for (i = 0; i < mbreq.Count; i++) { writeDbg(MbWarning, "i: %d, Status: %d, Expected: %d", i, bitStatus[i], (i != 2)); if (bitStatus[i] != (i != 2)) { writeDbg(MbError, "State of output bit %d was incorrect: %d (expected %d)", i, bitStatus[i], (i != 2)); runError(1001, 0); return; } } // OK. writeDbg(MbDebug, "State %d. Status of these output bits was correct.", state); ModbusWriteRegisters(thisDev.Addr.Write.OutputRegisters, 1, s6); state = 70; break; case 120: state = 130; break; case 130: writeDbg(MbDebug, "State %d. Successfully received the Read-Bits telegrams (as expected)", state); timr.Set(1); break; case 900: if (gQueueSent.Size() > 0 || gQueuePending.Size() > 0) break; ++s90i; for (i = 0; i < s90i && i < 100; i++) ModbusReadBits(thisDev.Addr.Read.InputBits, 1); break; default: writeDbg(MbError, "I did not expect state %d in OnModbusReadBitsSuccess()!", state); runError(1001, 0); break; } } void OnModbusReadRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) { char reason[100]; struct ModbusReqRead mbreq; switch (error) { case Timeout: break; case FinalTimeout: strncpy(reason, "Timeout", elCount(reason)); break; case Exception: snprintf(reason, elCount(reason), "Exception: %s", ModbusExceptions[ex-1]); break; case NotSent: strncpy(reason, "Impossible to send", elCount(reason)); break; default: writeDbg(MbError, "OnModbusReadRegistersFailed: Unkown error: %d", error); OnModbusClientPanics(SwitchArgumentInvalid); return; } memcpy_n2h(mbreq, gQueueAck[mbap.TxID].Buffer); switch (state) { case 0: case 10: case 110: case 75: writeDbg(MbError, "State %d. Reading %d output register from 0x%04X did not work! Reason: %s", state, mbreq.Count, mbreq.Address, reason); break; case 30: writeDbg(MbError, "State %d. Reading %d input register from 0x%04X did not work! Reason: %s", state, mbreq.Count, mbreq.Address, reason); break; case 90: case 140: case 145: case 150: writeDbg(MbError, "State %d. Reading %d output registers from 0x%04X did not work! Reason: %s", state, mbreq.Count, mbreq.Address, reason); break; default: writeDbg(MbError, "I did not expect state %d in OnModbusReadRegistersFailed()!", state); break; } runError(1001, 0); } void OnModbusReadRegistersSuccess(struct ModbusResReceiveRegisters mbres, struct ModbusReqRead mbreq) { switch (state) { case 0: s0i--; _DeviceParseRegister(s0dev, mbreq.Address, mbres.Data, 0); if (s0i == 0) { if (s0dev.DeviceIOs.OutputBits / 8 + s0dev.DeviceIOs.OutputRegisters < 2) { writeDbg(MbError, "Please connect more output! %d bits and %d registers are not enough, we need at least 1 word (2 bytes). (Input: %d bits, %d regs)", s0dev.DeviceIOs.OutputBits, s0dev.DeviceIOs.OutputRegisters, s0dev.DeviceIOs.InputBits, s0dev.DeviceIOs.InputRegisters); skipOutput = 1; //runError(1001, 0); //return; } state = 5; ModbusReadOutBits(thisDev.Addr.Read.OutputBits, 1); } break; case 10: writeDbg(MbDebug, "State %d. Successfully read %d output register from 0x%04X", state, mbreq.Count, mbreq.Address); ModbusReadInBits(thisDev.Addr.Read.InputBits, 1); state = 20; break; case 30: writeDbg(MbDebug, "State %d. Successfully read %d input register from 0x%04X", state, mbreq.Count, mbreq.Address); if (skipOutput) state = 130; timr.Set(1); break; case 75: writeDbg(MbDebug, "State %d. Successfully read %d input register from 0x%04X", state, mbreq.Count, mbreq.Address); if (mbres.Data[0] != 0x5555) { writeDbg(MbError, "State %d. Value of output register 0 was incorrect: 0x%04X (expected 0x5555). ModbusWriteRegisters failed!", state, mbres.Data[0]); runError(1001, 0); return; } ModbusWriteRegister(thisDev.Addr.Write.OutputRegisters, 0x9999); state = 80; break; case 90: writeDbg(MbDebug, "State %d. Successfully read %d output register from 0x%04X", state, mbreq.Count, mbreq.Address); if (mbres.Data[0] != 0x9999) { writeDbg(MbError, "State %d. Value of output register 0 was incorrect: 0x%04X (expected 0x9999). ModbusWriteRegister failed!", state, mbres.Data[0]); runError(1001, 0); return; } ModbusWriteMasks(thisDev.Addr.Write.OutputRegisters, 0x00F9, 0x0006); state = 100; break; case 110: writeDbg(MbDebug, "State %d. Successfully read %d output register from 0x%04X", state, mbreq.Count, mbreq.Address); if (mbres.Data[0] != 0x009F) { writeDbg(MbError, "State %d. Value of register at 0x%04X is incorrect: 0x%04X (expected 0x009F). ModbusWriteMasks failed!", state, mbreq.Address, mbres.Data[0]); runError(1001, 0); return; } writeDbg(MbDebug, "State %d. Successfully applied the masks at 0x%04X", state, mbreq.Address); ModbusReadInBits(thisDev.Addr.Read.InputBits, 3000); state = 120; if (s0dev.Vendor == Wago) // This test does not work with Wago state = 130; break; case 140: state = 145; break; case 145: state = 150; break; case 150: writeDbg(MbDebug, "State %d. Successfully received three Read-Registers telegrams (as expected)", state); if (skipOutput) state = 900; else { state = 160; if (s0dev.Vendor == Wago) // This test does not work with Wago state = 170; } timr.Set(1); break; default: writeDbg(MbError, "I did not expect state %d in OnModbusReadRegistersSuccess()!", state); runError(1001, 0); break; } } void OnModbusWriteBitFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) { char reason[100]; struct ModbusReqWriteSingle mbreq; switch (error) { case Timeout: break; case FinalTimeout: strncpy(reason, "Timeout", elCount(reason)); break; case Exception: snprintf(reason, elCount(reason), "Exception: %s", ModbusExceptions[ex-1]); break; case NotSent: strncpy(reason, "Impossible to send", elCount(reason)); break; default: writeDbg(MbError, "OnModbusWriteBitFailed: Unkown error: %d", error); OnModbusClientPanics(SwitchArgumentInvalid); return; } memcpy_n2h(mbreq, gQueueAck[mbap.TxID].Buffer); switch (state) { case 50: writeDbg(MbError, "State %d. Setting bit at 0x%04X did not work! Reason: %s", state, mbreq.Address, reason); break; default: writeDbg(MbError, "I did not expect state %d in OnModbusWriteBitFailed()!", state); break; } runError(1001, 0); } void OnModbusWriteBitSuccess(struct ModbusResConfirmSingle mbres) { byte i; switch (state) { case 50: writeDbg(MbDebug, "State %d. Successfully set bit at 0x%04X", state, mbres.Address); timr.Set(1); break; default: writeDbg(MbError, "I did not expect state %d in OnModbusWriteBitSuccess()!", state); runError(1001, 0); break; } } void OnModbusWriteRegisterFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) { char reason[100]; struct ModbusReqWriteSingle mbreq; switch (error) { case Timeout: break; case FinalTimeout: strncpy(reason, "Timeout", elCount(reason)); break; case Exception: snprintf(reason, elCount(reason), "Exception: %s", ModbusExceptions[ex-1]); break; case NotSent: strncpy(reason, "Impossible to send", elCount(reason)); break; default: writeDbg(MbError, "OnModbusWriteRegisterFailed: Unkown error: %d", error); OnModbusClientPanics(SwitchArgumentInvalid); return; } memcpy_n2h(mbreq, gQueueAck[mbap.TxID].Buffer); switch (state) { case 80: writeDbg(MbError, "State %d. Writing output register at 0x%04X did not work! Reason: %s", state, mbreq.Address, reason); break; default: writeDbg(MbError, "I did not expect state %d in OnModbusWriteRegisterFailed()!", state); break; } runError(1001, 0); } void OnModbusWriteRegisterSuccess(struct ModbusResConfirmSingle mbres) { switch (state) { case 80: writeDbg(MbDebug, "State %d. Successfully set output register at 0x%04X to 0x%04X", state, mbres.Address, mbres.Value); timr.Set(1); break; default: writeDbg(MbError, "I did not expect state %d in OnModbusWriteRegisterSuccess()!", state); runError(1001, 0); break; } } void OnModbusWriteMasksFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) { char reason[100]; struct ModbusReqWriteMasks mbreq; switch (error) { case Timeout: break; case FinalTimeout: strncpy(reason, "Timeout", elCount(reason)); break; case Exception: snprintf(reason, elCount(reason), "Exception: %s", ModbusExceptions[ex-1]); break; case NotSent: strncpy(reason, "Impossible to send", elCount(reason)); break; default: writeDbg(MbError, "OnModbusWriteMasksFailed: Unkown error: %d", error); OnModbusClientPanics(SwitchArgumentInvalid); return; } memcpy_n2h(mbreq, gQueueAck[mbap.TxID].Buffer); switch (state) { case 100: writeDbg(MbError, "State %d. Applying masks &0x%04X |0x%04X at 0x%04X did not work! Reason: %s", state, mbreq.And, mbreq.Or, mbreq.Address); break; default: writeDbg(MbError, "I did not expect state %d in OnModbusWriteMasksFailed()!", state); break; } runError(1001, 0); } void OnModbusWriteMasksSuccess(struct ModbusResConfirmMasks mbres) { switch (state) { case 100: writeDbg(MbDebug, "State %d. Successfully applied masks &0x%04X |0x%04X at 0x%04X", state, mbres.And, mbres.Or, mbres.Address); timr.Set(1); break; default: writeDbg(MbError, "I did not expect state %d in OnModbusWriteMasksSuccess()!", state); runError(1001, 0); break; } } void OnModbusReadWriteRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) { char reason[100]; struct ModbusReqReadWriteRegisters mbreq; switch (error) { case Timeout: break; case FinalTimeout: strncpy(reason, "Timeout", elCount(reason)); break; case Exception: snprintf(reason, elCount(reason), "Exception: %s", ModbusExceptions[ex-1]); break; case NotSent: strncpy(reason, "Impossible to send", elCount(reason)); break; default: writeDbg(MbError, "OnModbusReadWriteRegistersFailed: Unkown error: %d", error); OnModbusClientPanics(SwitchArgumentInvalid); return; } memcpy_n2h(mbreq, gQueueAck[mbap.TxID].Buffer); switch (state) { default: writeDbg(MbError, "I did not expect state %d in OnModbusReadWriteRegistersFailed()!", state); break; } runError(1001, 0); } void OnModbusWriteBitsFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) { char reason[100]; struct ModbusReqWriteBits mbreq; switch (error) { case Timeout: break; case FinalTimeout: strncpy(reason, "Timeout", elCount(reason)); break; case Exception: snprintf(reason, elCount(reason), "Exception: %s", ModbusExceptions[ex-1]); break; case NotSent: strncpy(reason, "Impossible to send", elCount(reason)); break; default: writeDbg(MbError, "OnModbusWriteBitsFailed: Unkown error: %d", error); OnModbusClientPanics(SwitchArgumentInvalid); return; } memcpy_n2h(mbreq, gQueueAck[mbap.TxID].Buffer); switch (state) { case 40: case 160: case 170: writeDbg(MbError, "State %d. Writing %d bits at 0x%04X did not work! Reason: %s", state, mbreq.Count, mbreq.Address, reason); break; default: writeDbg(MbError, "I did not expect state %d in OnModbusWriteBitsFailed()!", state); break; } runError(1001, 0); } void OnModbusWriteBitsSuccess(struct ModbusResConfirmMultiple mbres) { word s17[200]; switch (state) { case 40: writeDbg(MbDebug, "State %d. Successfully set %d bits at 0x%04X", state, mbres.Count, mbres.Address); ModbusWriteBit(thisDev.Addr.Write.OutputBits+2, 0); state = 50; break; case 160: state = 170; break; case 170: writeDbg(MbDebug, "State %d. Successfully received two(?) Write-Bits telegrams (as expected)", state); ModbusWriteRegisters(thisDev.Addr.Write.OutputRegisters, 200, s17); state = 180; break; default: writeDbg(MbError, "I did not expect state %d in OnModbusWriteBitsSuccess()!", state); runError(1001, 0); break; } } void OnModbusWriteRegistersFailed(enum ModbusRequestError error, enum ModbusException ex, struct ModbusApHeader mbap) { char reason[100]; struct ModbusReqWriteRegisters mbreq; switch (error) { case Timeout: break; case FinalTimeout: strncpy(reason, "Timeout", elCount(reason)); break; case Exception: snprintf(reason, elCount(reason), "Exception: %s", ModbusExceptions[ex-1]); break; case NotSent: strncpy(reason, "Impossible to send", elCount(reason)); break; default: writeDbg(MbError, "OnModbusWriteRegistersFailed: Unkown error: %d", error); OnModbusClientPanics(SwitchArgumentInvalid); return; } memcpy_n2h(mbreq, gQueueAck[mbap.TxID].Buffer); switch (state) { case 70: case 180: case 190: writeDbg(MbError, "State %d. Writing %d registers at 0x%04X did not work! Reason: %s", state, mbreq.Count, mbreq.Address, reason); break; default: writeDbg(MbError, "I did not expect state %d in OnModbusWriteRegistersFailed()!", state); break; } runError(1001, 0); } void OnModbusWriteRegistersSuccess(struct ModbusResConfirmMultiple mbres) { switch (state) { case 70: writeDbg(MbDebug, "State %d. Successfully set %d output registers at 0x%04X", state, mbres.Count, mbres.Address); timr.Set(1); break; case 180: state = 190; break; case 190: writeDbg(MbDebug, "State %d. Successfully received two Write-Registers telegrams (as expected)", state); timr.Set(1); break; default: writeDbg(MbError, "I did not expect state %d in OnModbusWriteRegistersSuccess()!", state); runError(1001, 0); break; } } void OnModbusClientPanics(enum FatalErrors reason) { switch (reason) { case ParsingBuffer: writeDbg(MbError, "State %d. FATAL ERROR while parsing received buffer", state); break; case ModbusPackageWasSplit: writeDbg(MbError, "State %d. FATAL ERROR: Modbus package was split", state); break; case DeviceCodeUnknown: writeDbg(MbError, "State %d. FATAL ERROR: Device code unknown", state); break; case VendorIdUnknown: writeDbg(MbError, "State %d. FATAL ERROR: Vendor Id unknown", state); break; case ConnectionError: writeDbg(MbError, "State %d. FATAL ERROR: Connection Error", state); break; case FuncCodeIncorrect: writeDbg(MbError, "State %d. FATAL ERROR: FuncCode Incorrect", state); break; case AddressFailure: writeDbg(MbError, "State %d. FATAL ERROR: Some Modbus Address Failure", state); break; case SwitchArgumentInvalid: writeDbg(MbError, "State %d. FATAL ERROR: A argument of a switch statement is incorrect"); break; } stop(); runError(1001, 0); } on timer timr { byte s3[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; byte s15[3000]; switch(state) { case 30: ModbusWriteBitsB(thisDev.Addr.Write.OutputBits, 10, s3); state = 40; break; case 50: ModbusReadOutBits(thisDev.Addr.Read.OutputBits, 10); state = 60; break; case 70: ModbusReadOutRegisters(thisDev.Addr.Read.OutputRegisters, 1); state = 75; break; case 80: ModbusReadOutRegisters(thisDev.Addr.Read.OutputRegisters, 1); state = 90; break; case 100: ModbusReadOutRegisters(thisDev.Addr.Read.OutputRegisters, 1); state = 110; break; case 130: ModbusReadRegisters(thisDev.Addr.Read.InputRegisters, 300); state = 140; break; case 160: ModbusWriteBitsB(thisDev.Addr.Write.OutputBits, 3000, s15); break; case 170: ModbusWriteBitsB(thisDev.Addr.Write.OutputBits, 1000, s15); break; case 190: ModbusReadBits(thisDev.Addr.Read.InputBits, 1); thisDev.ReceiveWindow = 0xFF; // Set receive window to maximum to test the limit state = 900; break; default: writeDbg(MbError, "I did not expect state %d in timer timr!", state); break; } }