Git Product home page Git Product logo

qmodbus's People

Contributors

ghorwin avatar kiug avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

qmodbus's Issues

Error handling concept

Hi, reviewing the code base of qmodbus, I miss a consistent error handling concept.

Normally, we have to distinguish between:

  • programming errors, i.e. resizing a register and accessing an index out of range -> this should be an assert or exception, as it is clearly programmers fault
  • connection/libmodbus errors resulting from communication problems: these errors can occur at any time in a valid software and have to be checked for via return code. -1 return codes from libmodbus could be converted into exceptions, hereby forcing QModbus users to handle the errors, or they could be stored and accessed via lastError() (current code)
  • utility function errors, not related to (possible breaking) connections should also be classified to be either programmer's errors or (anticipated) runtime errors

Some examples:

void QModbusBits::setValue (unsigned int index, bool value)
{
	operator[](index) = value; // no error handling at all, in release mode SEGFAULT when using invalid index
}


qint8 QModbusRegisters::getInteger8 (int index, bool low)
{
	if (size () <= (int)index) {
		modbusError.set (OUT_OF_RANGE_ENO);
		// error flag is set, but function continues to run until SEGFAULT further below
		// instead, should throw an exception or raise and assertion
	}
	else {
		modbusError.clear();
	}

	qint16 value = 0;
	((qint16 *) &value)[0] = at (index);

	if ( low )
		return getLowByte( value );
	else
		return getHighByte( value );
}

void QModbusMaster::setResponseTimeout (uint32_t sec, uint32_t usec)
{
//    struct timeval timeout;
//    timeout.tv_sec = sec;
//    timeout.tv_usec = usec;
//    modbus_set_response_timeout ((modbus_t *) ctx, &timeout);

	modbus_set_response_timeout ((modbus_t *) ctx, sec, usec);
	// Error handling, i.e. check for return code is missing; user code has no
	// way to know that setting the time out may have failed
}

I suggest the following error handling concept:

  • range checks or other checks that prevent code to SEGFAULT/raise access violations should use Q_ASSERT to guard the relevant code -> setting lastError is not necessary in such cases, as they can only occur due to faulty client code
  • all libmodbus calls should check for return value:
    • setter functions (i.e. functions that change the state of the modbus slave/master connection) return bool - false on error with lastError containing the information on the error
    • getter functions return the retrieved value if successful, and only store lastError value

Example code:

// setter functions - return value for convenience, same as 
// manually checking for modbusMaster->lastError().isValid()
if (!modbusMaster->setSomeValue(x,y))
    qCritical() << modbusMaster->lastError().message();

// getter functions - user code is responsible for checking return value,
// as errors are anticipated
int hl = modbusMaster->getHeaderLength();
if (modbusMaster->lastError().isValid())
    qCritical() << modbusMaster->lastError().message();


// Option A)
// checking for invalid arguments via ASSERTS - may be problematic, as asserts are not
// being used in release code and if index is provided by user input, software could SEGFAULT
void QModbusRegisters::setValue (int index, quint16 value) {
	// assert should deliver context information on programmers error
	Q_ASSERT_X(index >= 0 && index < size(), "QModbusRegisters::setValue", "index out of range");
	operator [] (index) = value;
}


// Option B)
// check errors via Exception class (as QModbus is C++ code, exceptions are ok)

class QModbusException : public std::exception {
public:
	QModbusException();
	QModbusException(const QModbusException&);
	QModbusException(const QString & context, const QString & msg);
};

void QModbusRegisters::setValue (int index, quint16 value) {
	// assert should deliver context information on programmers error
	if (index >= 0 && index < size())
		throw QModbusException("QModbusRegisters::setValue", QString("Index %1 out of range (size = %2)").arg(index).arg(size());
	operator [] (index) = value;
}

If we can decide error handling of QModbus I would go through the code and provide a patch via pull request.

What do you think about it?

Encoding bug on little endian systems -> consistently wrong byteswap when writing modbus float32

Hi,

just wanted to let you know that you have a consistent bug when writing Float32 and Int32 encoded values into Modbus on little endian systems (linux etc.). This effectively causes Float32_abcd to be written as cdab encoding into modbus stream.

Here is the problem:

void QModbusRegisters::setFloat32 (unsigned int index, float value)
{
    if (size () <= (int)index + 1) {
        modbusError.set (OUT_OF_RANGE_ENO);
    }
    else {
        modbusError.clear ();
    }
    twoRegistersRepresentation valueRep;
    valueRep.float32 = value;
    operator [] (index) = valueRep.component[0];
    operator [] (index + 1) = valueRep.component[1];
}

The problem is the use of the union twoRegistersRepresentation.

Example: Float value 20.1 has binary expression in big endian as:

0x41a0147b

(see https://www.h-schmidt.net/FloatConverter/IEEE754.html)

where 0x41a0 encodes 20, and 0x137b encodes .1

This is abcd notation. The setFloat32() code, however, writes as cdab (i.e. results in 0x147b41a0 in the Modbus byte stream). The Modbus standard, however, requires abcd (big endian) encoding to be used in the byte stream.

This should be fixed, or clarified by adding a suffix setFloat32_cdab

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.