Bladeren bron

Improve Modbus RTU robustness. Fixes #126

Neal Wilson 9 jaren geleden
bovenliggende
commit
7f7e09a87f
1 gewijzigde bestanden met toevoegingen van 34 en 3 verwijderingen
  1. 34
    3
      src/typica.w

+ 34
- 3
src/typica.w Bestand weergeven

@@ -17409,7 +17409,8 @@ class ModbusRTUDevice : public QObject
17409 17409
 		void svuResponse(QByteArray response);
17410 17410
 		void requestMeasurement();
17411 17411
 		void mResponse(QByteArray response);
17412
-		void ignore(QByteArray response);@/
17412
+		void ignore(QByteArray response);
17413
+		void timeout();@/
17413 17414
 	private:@/
17414 17415
 		QextSerialPort *port;
17415 17416
 		QByteArray responseBuffer;
@@ -17418,6 +17419,7 @@ class ModbusRTUDevice : public QObject
17418 17419
 		QList<char *> callbackQueue;
17419 17420
 		quint16 calculateCRC(QByteArray data);
17420 17421
 		QTimer *messageDelayTimer;
17422
+		QTimer *commTimeout;
17421 17423
 		int delayTime;
17422 17424
 		char station;
17423 17425
 		int decimalPosition;
@@ -17449,9 +17451,10 @@ immediately upon construction.
17449 17451
 
17450 17452
 @<ModbusRTUDevice implementation@>=
17451 17453
 ModbusRTUDevice::ModbusRTUDevice(DeviceTreeModel *model,@| const QModelIndex &index)
17452
-: QObject(NULL), messageDelayTimer(new QTimer), unitIsF(@[true@]), readingsv(@[false@]),
17454
+: QObject(NULL), messageDelayTimer(new QTimer), commTimeout(new QTimer), unitIsF(@[true@]), readingsv(@[false@]),
17453 17455
 	waiting(@[false@])@/
17454 17456
 {@/
17457
+qDebug() << "Initializing Modbus RTU Device";
17455 17458
 	QDomElement portReferenceElement = model->referenceElement(model->data(index,
17456 17459
 		Qt::UserRole).toString());
17457 17460
 	QDomNodeList portConfigData = portReferenceElement.elementsByTagName("attribute");
@@ -17469,7 +17472,9 @@ ModbusRTUDevice::ModbusRTUDevice(DeviceTreeModel *model,@| const QModelIndex &in
17469 17472
 	double temp = ((double)(1) / (double)(baudRate)) * 48;
17470 17473
 	delayTime = (int)(temp * 3000);
17471 17474
 	messageDelayTimer->setSingleShot(true);
17475
+	commTimeout->setSingleShot(true);
17472 17476
 	connect(messageDelayTimer, SIGNAL(timeout()), this, SLOT(sendNextMessage()));
17477
+	connect(commTimeout, SIGNAL(timeout()), this, SLOT(timeout()));
17473 17478
 	port->setDataBits(DATA_8);
17474 17479
 	port->setParity((ParityType)attributes.value("parity").toInt());
17475 17480
 	port->setStopBits((StopBitsType)attributes.value("stop").toInt());
@@ -17632,6 +17637,7 @@ void ModbusRTUDevice::unitResponse(QByteArray response)
17632 17637
 	{
17633 17638
 		unitIsF = @[false@];
17634 17639
 	}
17640
+	qDebug() << "Received unit response";
17635 17641
 }
17636 17642
 
17637 17643
 void ModbusRTUDevice::svlResponse(QByteArray response)
@@ -17646,6 +17652,7 @@ void ModbusRTUDevice::svlResponse(QByteArray response)
17646 17652
 		outputSVLower /= 10;
17647 17653
 	}
17648 17654
 	emit SVLowerChanged(outputSVLower);
17655
+	qDebug() << "Received set value lower bound response";
17649 17656
 }
17650 17657
 
17651 17658
 void ModbusRTUDevice::svuResponse(QByteArray response)
@@ -17660,6 +17667,7 @@ void ModbusRTUDevice::svuResponse(QByteArray response)
17660 17667
 		outputSVUpper /= 10;
17661 17668
 	}
17662 17669
 	emit SVUpperChanged(outputSVUpper);
17670
+	qDebug() << "Received set value upper bound response";
17663 17671
 }
17664 17672
 
17665 17673
 void ModbusRTUDevice::requestMeasurement()
@@ -17767,6 +17775,7 @@ else
17767 17775
 @<ModbusRTUDevice implementation@>=
17768 17776
 ModbusRTUDevice::~ModbusRTUDevice()
17769 17777
 {
17778
+	commTimeout->stop();
17770 17779
 	messageDelayTimer->stop();
17771 17780
 	port->close();
17772 17781
 }
@@ -17786,6 +17795,10 @@ remove the message and callback information from the message queue, and start
17786 17795
 a timer which will trigger sending the next message after a safe amount of
17787 17796
 time has passed.
17788 17797
 
17798
+If a response is received with an invalid CRC, we do not pass that message
17799
+out. Instead, the message handling queues are kept as they are and the previous
17800
+command will be sent again once the message delay timer is finished.
17801
+
17789 17802
 @<ModbusRTUDevice implementation@>=
17790 17803
 void ModbusRTUDevice::dataAvailable()
17791 17804
 {
@@ -17795,6 +17808,7 @@ void ModbusRTUDevice::dataAvailable()
17795 17808
 	}
17796 17809
 	responseBuffer.append(port->readAll());
17797 17810
 	@<Check Modbus RTU message size@>@;
17811
+	commTimeout->stop();
17798 17812
 	if(calculateCRC(responseBuffer) == 0)
17799 17813
 	{
17800 17814
 		QObject *object = retObjQueue.at(0);
@@ -17807,12 +17821,12 @@ void ModbusRTUDevice::dataAvailable()
17807 17821
 		messageQueue.removeAt(0);
17808 17822
 		retObjQueue.removeAt(0);
17809 17823
 		callbackQueue.removeAt(0);
17810
-		messageDelayTimer->start(delayTime);
17811 17824
 	}
17812 17825
 	else
17813 17826
 	{
17814 17827
 		qDebug() << "CRC failed";
17815 17828
 	}
17829
+	messageDelayTimer->start(delayTime);
17816 17830
 	waiting = @[false@];
17817 17831
 	responseBuffer.clear();
17818 17832
 }
@@ -17927,6 +17941,7 @@ void ModbusRTUDevice::sendNextMessage()
17927 17941
 		message.append(check[0]);
17928 17942
 		message.append(check[1]);
17929 17943
 		port->write(message);
17944
+		commTimeout->start(2000);
17930 17945
 		messageDelayTimer->start(delayTime);
17931 17946
 		waiting = @[true@];
17932 17947
 	}
@@ -17958,6 +17973,22 @@ void ModbusRTUDevice::ignore(QByteArray)
17958 17973
 	return;
17959 17974
 }
17960 17975
 
17976
+@ Sometimes a communications failure will occur in which a response to a
17977
+command is never received. To reset communications we set a timer whenever a
17978
+command is sent and stop that once a full response is received. If the timer
17979
+times out, we should clear the response buffer and attempt to re-establish
17980
+communications. Currently this timeout is hard coded at 2 seconds, however
17981
+this should be configurable and smaller values may well be acceptable.
17982
+
17983
+@<ModbusRTUDevice implementation@>=
17984
+void ModbusRTUDevice::timeout()
17985
+{
17986
+	qDebug() << "Communications timeout.";
17987
+	responseBuffer.clear();
17988
+	waiting = false;
17989
+	messageDelayTimer->start();
17990
+}
17991
+
17961 17992
 @ This class must be exposed to the host environment.
17962 17993
 
17963 17994
 @<Function prototypes for scripting@>=

Laden…
Annuleren
Opslaan