Ver código fonte

Improve Modbus RTU robustness. Fixes #126

Neal Wilson 9 anos atrás
pai
commit
7f7e09a87f
1 arquivos alterados com 34 adições e 3 exclusões
  1. 34
    3
      src/typica.w

+ 34
- 3
src/typica.w Ver arquivo

17409
 		void svuResponse(QByteArray response);
17409
 		void svuResponse(QByteArray response);
17410
 		void requestMeasurement();
17410
 		void requestMeasurement();
17411
 		void mResponse(QByteArray response);
17411
 		void mResponse(QByteArray response);
17412
-		void ignore(QByteArray response);@/
17412
+		void ignore(QByteArray response);
17413
+		void timeout();@/
17413
 	private:@/
17414
 	private:@/
17414
 		QextSerialPort *port;
17415
 		QextSerialPort *port;
17415
 		QByteArray responseBuffer;
17416
 		QByteArray responseBuffer;
17418
 		QList<char *> callbackQueue;
17419
 		QList<char *> callbackQueue;
17419
 		quint16 calculateCRC(QByteArray data);
17420
 		quint16 calculateCRC(QByteArray data);
17420
 		QTimer *messageDelayTimer;
17421
 		QTimer *messageDelayTimer;
17422
+		QTimer *commTimeout;
17421
 		int delayTime;
17423
 		int delayTime;
17422
 		char station;
17424
 		char station;
17423
 		int decimalPosition;
17425
 		int decimalPosition;
17449
 
17451
 
17450
 @<ModbusRTUDevice implementation@>=
17452
 @<ModbusRTUDevice implementation@>=
17451
 ModbusRTUDevice::ModbusRTUDevice(DeviceTreeModel *model,@| const QModelIndex &index)
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
 	waiting(@[false@])@/
17455
 	waiting(@[false@])@/
17454
 {@/
17456
 {@/
17457
+qDebug() << "Initializing Modbus RTU Device";
17455
 	QDomElement portReferenceElement = model->referenceElement(model->data(index,
17458
 	QDomElement portReferenceElement = model->referenceElement(model->data(index,
17456
 		Qt::UserRole).toString());
17459
 		Qt::UserRole).toString());
17457
 	QDomNodeList portConfigData = portReferenceElement.elementsByTagName("attribute");
17460
 	QDomNodeList portConfigData = portReferenceElement.elementsByTagName("attribute");
17469
 	double temp = ((double)(1) / (double)(baudRate)) * 48;
17472
 	double temp = ((double)(1) / (double)(baudRate)) * 48;
17470
 	delayTime = (int)(temp * 3000);
17473
 	delayTime = (int)(temp * 3000);
17471
 	messageDelayTimer->setSingleShot(true);
17474
 	messageDelayTimer->setSingleShot(true);
17475
+	commTimeout->setSingleShot(true);
17472
 	connect(messageDelayTimer, SIGNAL(timeout()), this, SLOT(sendNextMessage()));
17476
 	connect(messageDelayTimer, SIGNAL(timeout()), this, SLOT(sendNextMessage()));
17477
+	connect(commTimeout, SIGNAL(timeout()), this, SLOT(timeout()));
17473
 	port->setDataBits(DATA_8);
17478
 	port->setDataBits(DATA_8);
17474
 	port->setParity((ParityType)attributes.value("parity").toInt());
17479
 	port->setParity((ParityType)attributes.value("parity").toInt());
17475
 	port->setStopBits((StopBitsType)attributes.value("stop").toInt());
17480
 	port->setStopBits((StopBitsType)attributes.value("stop").toInt());
17632
 	{
17637
 	{
17633
 		unitIsF = @[false@];
17638
 		unitIsF = @[false@];
17634
 	}
17639
 	}
17640
+	qDebug() << "Received unit response";
17635
 }
17641
 }
17636
 
17642
 
17637
 void ModbusRTUDevice::svlResponse(QByteArray response)
17643
 void ModbusRTUDevice::svlResponse(QByteArray response)
17646
 		outputSVLower /= 10;
17652
 		outputSVLower /= 10;
17647
 	}
17653
 	}
17648
 	emit SVLowerChanged(outputSVLower);
17654
 	emit SVLowerChanged(outputSVLower);
17655
+	qDebug() << "Received set value lower bound response";
17649
 }
17656
 }
17650
 
17657
 
17651
 void ModbusRTUDevice::svuResponse(QByteArray response)
17658
 void ModbusRTUDevice::svuResponse(QByteArray response)
17660
 		outputSVUpper /= 10;
17667
 		outputSVUpper /= 10;
17661
 	}
17668
 	}
17662
 	emit SVUpperChanged(outputSVUpper);
17669
 	emit SVUpperChanged(outputSVUpper);
17670
+	qDebug() << "Received set value upper bound response";
17663
 }
17671
 }
17664
 
17672
 
17665
 void ModbusRTUDevice::requestMeasurement()
17673
 void ModbusRTUDevice::requestMeasurement()
17767
 @<ModbusRTUDevice implementation@>=
17775
 @<ModbusRTUDevice implementation@>=
17768
 ModbusRTUDevice::~ModbusRTUDevice()
17776
 ModbusRTUDevice::~ModbusRTUDevice()
17769
 {
17777
 {
17778
+	commTimeout->stop();
17770
 	messageDelayTimer->stop();
17779
 	messageDelayTimer->stop();
17771
 	port->close();
17780
 	port->close();
17772
 }
17781
 }
17786
 a timer which will trigger sending the next message after a safe amount of
17795
 a timer which will trigger sending the next message after a safe amount of
17787
 time has passed.
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
 @<ModbusRTUDevice implementation@>=
17802
 @<ModbusRTUDevice implementation@>=
17790
 void ModbusRTUDevice::dataAvailable()
17803
 void ModbusRTUDevice::dataAvailable()
17791
 {
17804
 {
17795
 	}
17808
 	}
17796
 	responseBuffer.append(port->readAll());
17809
 	responseBuffer.append(port->readAll());
17797
 	@<Check Modbus RTU message size@>@;
17810
 	@<Check Modbus RTU message size@>@;
17811
+	commTimeout->stop();
17798
 	if(calculateCRC(responseBuffer) == 0)
17812
 	if(calculateCRC(responseBuffer) == 0)
17799
 	{
17813
 	{
17800
 		QObject *object = retObjQueue.at(0);
17814
 		QObject *object = retObjQueue.at(0);
17807
 		messageQueue.removeAt(0);
17821
 		messageQueue.removeAt(0);
17808
 		retObjQueue.removeAt(0);
17822
 		retObjQueue.removeAt(0);
17809
 		callbackQueue.removeAt(0);
17823
 		callbackQueue.removeAt(0);
17810
-		messageDelayTimer->start(delayTime);
17811
 	}
17824
 	}
17812
 	else
17825
 	else
17813
 	{
17826
 	{
17814
 		qDebug() << "CRC failed";
17827
 		qDebug() << "CRC failed";
17815
 	}
17828
 	}
17829
+	messageDelayTimer->start(delayTime);
17816
 	waiting = @[false@];
17830
 	waiting = @[false@];
17817
 	responseBuffer.clear();
17831
 	responseBuffer.clear();
17818
 }
17832
 }
17927
 		message.append(check[0]);
17941
 		message.append(check[0]);
17928
 		message.append(check[1]);
17942
 		message.append(check[1]);
17929
 		port->write(message);
17943
 		port->write(message);
17944
+		commTimeout->start(2000);
17930
 		messageDelayTimer->start(delayTime);
17945
 		messageDelayTimer->start(delayTime);
17931
 		waiting = @[true@];
17946
 		waiting = @[true@];
17932
 	}
17947
 	}
17958
 	return;
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
 @ This class must be exposed to the host environment.
17992
 @ This class must be exposed to the host environment.
17962
 
17993
 
17963
 @<Function prototypes for scripting@>=
17994
 @<Function prototypes for scripting@>=

Carregando…
Cancelar
Salvar