refactored neuron code

pull/8/head
Boernsman 2021-02-04 22:20:38 +01:00
parent fadb3a1649
commit 571af9c85a
8 changed files with 359 additions and 313 deletions

View File

@ -893,10 +893,9 @@ void IntegrationPluginUniPi::onNeuronExtensionConnectionStateChanged(bool state)
void IntegrationPluginUniPi::onRequestExecuted(const QUuid &requestId, bool success)
{
qCDebug(dcUniPi()) << "Request executed, pending requests:" << m_asyncActions.size();
if (m_asyncActions.contains(requestId)){
ThingActionInfo *info = m_asyncActions.take(requestId);
qCDebug(dcUniPi()) << "Request executed, pending requests:" << m_asyncActions.count();
if (success){
info->finish(Thing::ThingErrorNoError);
} else {

View File

@ -36,43 +36,11 @@
#include <QStandardPaths>
Neuron::Neuron(NeuronTypes neuronType, QModbusTcpClient *modbusInterface, QObject *parent) :
QObject(parent),
NeuronCommon(parent),
m_modbusInterface(modbusInterface),
m_neuronType(neuronType)
{
qCDebug(dcUniPi()) << "Neuron: Creating Neuron connection" << neuronType;
m_inputPollingTimer = new QTimer(this);
connect(m_inputPollingTimer, &QTimer::timeout, this, &Neuron::onInputPollingTimer);
m_inputPollingTimer->setTimerType(Qt::TimerType::PreciseTimer);
m_inputPollingTimer->setInterval(200);
m_outputPollingTimer = new QTimer(this);
connect(m_outputPollingTimer, &QTimer::timeout, this, &Neuron::onOutputPollingTimer);
m_outputPollingTimer->setTimerType(Qt::TimerType::PreciseTimer);
m_outputPollingTimer->setInterval(1000);
if (m_modbusInterface->state() == QModbusDevice::State::ConnectedState) {
m_inputPollingTimer->start();
m_outputPollingTimer->start();
}
connect(m_modbusInterface, &QModbusDevice::stateChanged, this, [this] (QModbusDevice::State state) {
if (state == QModbusDevice::State::ConnectedState) {
qCDebug(dcUniPi()) << "Neuron: Starting polling timer";
if (m_inputPollingTimer)
m_inputPollingTimer->start();
if (m_outputPollingTimer)
m_outputPollingTimer->start();
emit connectionStateChanged(true);
} else {
qCDebug(dcUniPi()) << "Neuron: Stopping polling timer";
if (m_inputPollingTimer)
m_inputPollingTimer->stop();
if (m_outputPollingTimer)
m_outputPollingTimer->stop();
emit connectionStateChanged(false);
}
});
}
Neuron::~Neuron()
@ -134,31 +102,6 @@ QString Neuron::type()
return "Unknown";
}
QList<QString> Neuron::digitalInputs()
{
return m_modbusDigitalInputRegisters.keys();
}
QList<QString> Neuron::digitalOutputs()
{
return m_modbusDigitalOutputRegisters.keys();
}
QList<QString> Neuron::analogInputs()
{
return m_modbusAnalogInputRegisters.keys();
}
QList<QString> Neuron::analogOutputs()
{
return m_modbusAnalogOutputRegisters.keys();
}
QList<QString> Neuron::userLEDs()
{
return m_modbusUserLEDRegisters.keys();
}
bool Neuron::loadModbusMap()
{
@ -354,13 +297,13 @@ bool Neuron::loadModbusMap()
return false;
}
if (list.last() == "Basic") {
QString circuit = list[5].split(" ").last();
int modbusAddress = list[0].toInt();
if (list[5].contains("Analog Input Value", Qt::CaseSensitivity::CaseInsensitive)) {
m_modbusAnalogInputRegisters.insert(circuit, list[0].toInt());
qDebug(dcUniPi()) << "Neuron: Found analog input register" << circuit << list[0].toInt();
m_modbusAnalogInputRegisters.insert(modbusAddress, registerDescriptorFromStringList(list));
qDebug(dcUniPi()) << "Neuron: Found analog input register" << modbusAddress;
} else if (list[5].contains("Analog Output Value", Qt::CaseSensitivity::CaseInsensitive)) {
m_modbusAnalogOutputRegisters.insert(circuit, list[0].toInt());
qDebug(dcUniPi()) << "Neuron: Found analog output register" << circuit << list[0].toInt();
m_modbusAnalogOutputRegisters.insert(modbusAddress, registerDescriptorFromStringList(list));
qDebug(dcUniPi()) << "Neuron: Found analog output register" << modbusAddress;
}
}
}
@ -392,8 +335,8 @@ bool Neuron::modbusWriteRequest(const Request &request)
if(m_modbusDigitalOutputRegisters.values().contains(modbusAddress)){
QString circuit = m_modbusDigitalOutputRegisters.key(modbusAddress);
emit digitalOutputStatusChanged(circuit, unit.value(0));
} else if(m_modbusAnalogOutputRegisters.values().contains(modbusAddress)){
QString circuit = m_modbusAnalogOutputRegisters.key(modbusAddress);
} else if(m_modbusAnalogOutputRegisters.contains(modbusAddress)){
QString circuit = m_modbusAnalogOutputRegisters.value(modbusAddress).circuit;
emit analogOutputStatusChanged(circuit, unit.value(0));
} else if(m_modbusUserLEDRegisters.values().contains(modbusAddress)){
QString circuit = m_modbusUserLEDRegisters.key(modbusAddress);
@ -458,8 +401,8 @@ bool Neuron::modbusReadRequest(const QModbusDataUnit &request)
break;
case QModbusDataUnit::RegisterType::HoldingRegisters:
if(m_modbusAnalogOutputRegisters.values().contains(modbusAddress)){
circuit = m_modbusAnalogOutputRegisters.key(modbusAddress);
if(m_modbusAnalogOutputRegisters.keys().contains(modbusAddress)){
circuit = m_modbusAnalogOutputRegisters.value(modbusAddress).circuit;
if (circuitValueChanged(circuit, unit.value(i)))
emit analogOutputStatusChanged(circuit, unit.value(i));
} else {
@ -467,8 +410,8 @@ bool Neuron::modbusReadRequest(const QModbusDataUnit &request)
}
break;
case QModbusDataUnit::RegisterType::InputRegisters:
if(m_modbusAnalogInputRegisters.values().contains(modbusAddress)){
circuit = m_modbusAnalogInputRegisters.key(modbusAddress);
if(m_modbusAnalogInputRegisters.keys().contains(modbusAddress)){
circuit = m_modbusAnalogInputRegisters.value(modbusAddress).circuit;
quint32 value = (unit.value(i) << 16 | unit.value(i+1));
if (circuitValueChanged(circuit, value))
emit analogInputStatusChanged(circuit, value);
@ -617,23 +560,20 @@ bool Neuron::getCoils(QList<int> registerList)
return true;
}
bool Neuron::circuitValueChanged(const QString &circuit, quint32 value)
bool Neuron::getAnalogIO(const RegisterDescriptor &descriptor)
{
if (m_previousCircuitValue.contains(circuit)) {
if (m_previousCircuitValue.value(circuit) == value) {
// Only emit status change of the circuit value has changed
return false;
} else {
m_previousCircuitValue.insert(circuit, value); //update existing value
return true;
}
QModbusDataUnit request = QModbusDataUnit(descriptor.registerType, descriptor.address, descriptor.count);
if (m_readRequestQueue.isEmpty()) {
return modbusReadRequest(request);
} else if (m_readRequestQueue.length() > 100) {
qCWarning(dcUniPi()) << "Neuron: Too many pending read requests";
return false;
} else {
m_previousCircuitValue.insert(circuit, value);
return true;
m_readRequestQueue.append(request);
}
return true;
}
bool Neuron::getAllDigitalInputs()
{
if (!m_modbusInterface) {
@ -658,8 +598,8 @@ bool Neuron::getAllAnalogInputs()
qCWarning(dcUniPi()) << "Neuron: Modbus interface not initialized";
return false;
}
foreach(QString circuit, m_modbusAnalogInputRegisters.keys()) {
getAnalogInput(circuit);
Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogInputRegisters.values()) {
getAnalogIO(descriptor);
}
return true;
}
@ -670,8 +610,8 @@ bool Neuron::getAllAnalogOutputs()
qCWarning(dcUniPi()) << "Neuron: Modbus interface not initialized";
return false;
}
foreach(QString circuit, m_modbusAnalogOutputRegisters.keys()) {
getAnalogOutput(circuit);
Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) {
getAnalogIO(descriptor);
}
return true;
}
@ -698,22 +638,13 @@ bool Neuron::getDigitalInput(const QString &circuit)
bool Neuron::getAnalogOutput(const QString &circuit)
{
int modbusAddress = m_modbusAnalogOutputRegisters.value(circuit);
//qDebug(dcUniPi()) << "Neuron: Reading analog output" << circuit << modbusAddress;
if (!m_modbusInterface)
return false;
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, modbusAddress, 1);
if (m_readRequestQueue.isEmpty()) {
return modbusReadRequest(request);
} else if (m_readRequestQueue.length() > 100) {
qCWarning(dcUniPi()) << "Neuron: Too many pending read requests";
return false;
} else {
m_readRequestQueue.append(request);
//qDebug(dcUniPi()) << "Neuron: Get analog output" << circuit;
Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) {
if (descriptor.circuit == circuit) {
return getAnalogIO(descriptor);
}
}
return true;
return false;
}
@ -761,48 +692,46 @@ bool Neuron::getDigitalOutput(const QString &circuit)
QUuid Neuron::setAnalogOutput(const QString &circuit, double value)
{
int modbusAddress = m_modbusAnalogOutputRegisters.value(circuit);
qDebug(dcUniPi()) << "Neuron: Writing analog Output" << circuit << modbusAddress;
qDebug(dcUniPi()) << "Neuron: Set analog output" << circuit << value;
if (!m_modbusInterface)
return "";
Request request;
request.id = QUuid::createUuid();
request.data = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, modbusAddress, 2);
request.data.setValue(0, (static_cast<uint32_t>(value) >> 16)); //FIXME
request.data.setValue(0, (static_cast<uint32_t>(value) & 0xffff)); //FIXME
Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters) {
if (descriptor.circuit == circuit) {
Request request;
request.id = QUuid::createUuid();
request.data = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, descriptor.address, descriptor.count);
if (descriptor.count == 1) {
request.data.setValue(0, (static_cast<uint>(value*400))); // 0 to 4000 = 0 to 10.0 V
} else if (descriptor.count == 2) {
request.data.setValue(0, (static_cast<uint32_t>(value) >> 16));
request.data.setValue(1, (static_cast<uint32_t>(value) & 0xffff));
}
if (m_writeRequestQueue.isEmpty()) {
modbusWriteRequest(request);
} else if (m_writeRequestQueue.length() > 100) {
return "";
} else {
m_writeRequestQueue.append(request);
if (m_writeRequestQueue.isEmpty()) {
modbusWriteRequest(request);
} else if (m_writeRequestQueue.length() > 100) {
return "";
} else {
m_writeRequestQueue.append(request);
}
return request.id;
}
}
return request.id;
return "";
}
bool Neuron::getAnalogInput(const QString &circuit)
{
int modbusAddress = m_modbusAnalogInputRegisters.value(circuit);
//qDebug(dcUniPi()) << "Neuron: Reading analog Input" << circuit << modbusAddress;
//qDebug(dcUniPi()) << "Neuron: Get analog input" << circuit;
if (!m_modbusInterface)
return false;
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, modbusAddress, 2);
if (m_readRequestQueue.isEmpty()) {
return modbusReadRequest(request);
} else if (m_readRequestQueue.length() > 100) {
qCWarning(dcUniPi()) << "Neuron: Too many pending read requests";
return false;
} else {
m_readRequestQueue.append(request);
Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) {
if (descriptor.circuit == circuit) {
return getAnalogIO(descriptor);
}
}
return true;
return false;
}
QUuid Neuron::setUserLED(const QString &circuit, bool value)
@ -834,7 +763,7 @@ QUuid Neuron::setUserLED(const QString &circuit, bool value)
bool Neuron::getUserLED(const QString &circuit)
{
int modbusAddress = m_modbusUserLEDRegisters.value(circuit);
//qDebug(dcUniPi()) << "Neuron: Reading digital Output" << circuit << modbusAddress;
//qDebug(dcUniPi()) << "Neuron: Get user LED" << circuit << modbusAddress;
if (!m_modbusInterface)
return false;

View File

@ -31,22 +31,19 @@
#ifndef NEURON_H
#define NEURON_H
#include "neuroncommon.h"
#include <QObject>
#include <QHash>
#include <QTimer>
#include <QtSerialBus>
#include <QUuid>
class Neuron : public QObject
class Neuron : public NeuronCommon
{
Q_OBJECT
public:
struct Request {
QUuid id;
QModbusDataUnit data;
};
enum NeuronTypes {
S103,
M103,
@ -71,12 +68,6 @@ public:
bool init();
QString type();
QList<QString> digitalInputs();
QList<QString> digitalOutputs();
QList<QString> analogInputs();
QList<QString> analogOutputs();
QList<QString> userLEDs();
QUuid setDigitalOutput(const QString &circuit, bool value);
QUuid setAnalogOutput(const QString &circuit, double value);
QUuid setUserLED(const QString &circuit, bool value);
@ -93,27 +84,11 @@ public:
bool getAllAnalogOutputs();
bool getUserLED(const QString &circuit);
private:
int m_slaveAddress = 0;
uint m_responseTimeoutTime = 2000;
QTimer *m_inputPollingTimer = nullptr;
QTimer *m_outputPollingTimer = nullptr;
QModbusTcpClient *m_modbusInterface = nullptr;
QHash<QString, int> m_modbusDigitalOutputRegisters;
QHash<QString, int> m_modbusDigitalInputRegisters;
QHash<QString, int> m_modbusAnalogInputRegisters;
QHash<QString, int> m_modbusAnalogOutputRegisters;
QHash<QString, int> m_modbusUserLEDRegisters;
QList<Request> m_writeRequestQueue;
QList<QModbusDataUnit> m_readRequestQueue;
NeuronTypes m_neuronType = NeuronTypes::S103;
QHash<QString, uint16_t> m_previousCircuitValue;
bool loadModbusMap();
bool modbusReadRequest(const QModbusDataUnit &request);
bool modbusWriteRequest(const Request &request);
@ -122,17 +97,7 @@ private:
bool getHoldingRegisters(QList<int> registers);
bool getCoils(QList<int> registers);
bool circuitValueChanged(const QString &circuit, quint32 value);
signals:
void requestExecuted(const QUuid &requestId, bool success);
void requestError(const QUuid &requestId, const QString &error);
void digitalInputStatusChanged(const QString &circuit, bool value);
void digitalOutputStatusChanged(const QString &circuit, bool value);
void analogInputStatusChanged(const QString &circuit, double value);
void analogOutputStatusChanged(const QString &circuit, double value);
void userLEDStatusChanged(const QString &circuit, bool value);
void connectionStateChanged(bool state);
bool getAnalogIO(const RegisterDescriptor &descriptor);
public slots:
void onOutputPollingTimer();

111
unipi/neuroncommon.cpp Normal file
View File

@ -0,0 +1,111 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "neuroncommon.h"
NeuronCommon::NeuronCommon(QObject *parent) : QObject(parent)
{
}
QList<QString> NeuronCommon::digitalInputs()
{
return m_modbusDigitalInputRegisters.keys();
}
QList<QString> NeuronCommon::digitalOutputs()
{
return m_modbusDigitalOutputRegisters.keys();
}
QList<QString> NeuronCommon::analogInputs()
{
QList<QString> circuits;
Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogInputRegisters.values()) {
circuits.append(descriptor.circuit);
}
return circuits;
}
QList<QString> NeuronCommon::analogOutputs()
{
QList<QString> circuits;
Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) {
circuits.append(descriptor.circuit);
}
return circuits;
}
QList<QString> NeuronCommon::userLEDs()
{
return m_modbusUserLEDRegisters.keys();
}
NeuronCommon::RegisterDescriptor NeuronCommon::registerDescriptorFromStringList(const QStringList &data)
{
RegisterDescriptor descriptor;
if (data.count() < 7) {
return descriptor;
}
descriptor.address = data[0].toInt();
descriptor.count = data[2].toInt();
if (data[3] == "RW") {
descriptor.readWrite = RWPermissionReadWrite;
} else if (data[3] == "W") {
descriptor.readWrite = RWPermissionWrite;
} else if (data[3] == "R") {
descriptor.readWrite = RWPermissionRead;
}
descriptor.circuit = data[5].split(" ").last();
descriptor.category = data.last();
if (data[5].contains("Analog Input Value", Qt::CaseSensitivity::CaseInsensitive)) {
descriptor.registerType = QModbusDataUnit::RegisterType::InputRegisters;
} else if (data[5].contains("Analog Output Value", Qt::CaseSensitivity::CaseInsensitive)) {
descriptor.registerType = QModbusDataUnit::RegisterType::HoldingRegisters;
}
return descriptor;
}
bool NeuronCommon::circuitValueChanged(const QString &circuit, quint32 value)
{
if (m_previousCircuitValue.contains(circuit)) {
if (m_previousCircuitValue.value(circuit) == value) {
// Only emit status change of the circuit value has changed
return false;
} else {
m_previousCircuitValue.insert(circuit, value); //update existing value
return true;
}
} else {
m_previousCircuitValue.insert(circuit, value);
return true;
}
}

106
unipi/neuroncommon.h Normal file
View File

@ -0,0 +1,106 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef NEURONCOMMON_H
#define NEURONCOMMON_H
#include <QObject>
#include <QtSerialBus>
class NeuronCommon : public QObject
{
Q_OBJECT
public:
explicit NeuronCommon(QObject *parent = nullptr);
QList<QString> digitalInputs();
QList<QString> digitalOutputs();
QList<QString> analogInputs();
QList<QString> analogOutputs();
QList<QString> userLEDs();
protected:
enum RWPermission {
RWPermissionNone,
RWPermissionRead,
RWPermissionReadWrite,
RWPermissionWrite
};
struct RegisterDescriptor {
int address;
uint count;
QString circuit;
RWPermission readWrite;
QString category;
QModbusDataUnit::RegisterType registerType;
};
struct Request {
QUuid id;
QModbusDataUnit data;
};
int m_slaveAddress = 0;
uint m_responseTimeoutTime = 2000;
QTimer *m_inputPollingTimer = nullptr;
QTimer *m_outputPollingTimer = nullptr;
QList<Request> m_writeRequestQueue;
QList<QModbusDataUnit> m_readRequestQueue;
QHash<QString, int> m_modbusDigitalOutputRegisters;
QHash<QString, int> m_modbusDigitalInputRegisters;
QHash<QString, int> m_modbusUserLEDRegisters;
QHash<int, RegisterDescriptor> m_modbusAnalogInputRegisters;
QHash<int, RegisterDescriptor> m_modbusAnalogOutputRegisters;
QHash<QString, uint16_t> m_previousCircuitValue;
RegisterDescriptor registerDescriptorFromStringList(const QStringList &data);
bool circuitValueChanged(const QString &circuit, quint32 value);
signals:
void requestExecuted(const QUuid &requestId, bool success);
void requestError(const QUuid &requestId, const QString &error);
void digitalInputStatusChanged(const QString &circuit, bool value);
void digitalOutputStatusChanged(const QString &circuit, bool value);
void analogInputStatusChanged(const QString &circuit, double value);
void analogOutputStatusChanged(const QString &circuit, double value);
void userLEDStatusChanged(const QString &circuit, bool value);
void connectionStateChanged(bool state);
};
#endif // NEURONCOMMON_H

View File

@ -37,7 +37,7 @@
#include <QStandardPaths>
NeuronExtension::NeuronExtension(ExtensionTypes extensionType, QModbusRtuSerialMaster *modbusInterface, int slaveAddress, QObject *parent) :
QObject(parent),
NeuronCommon(parent),
m_modbusInterface(modbusInterface),
m_slaveAddress(slaveAddress),
m_extensionType(extensionType)
@ -132,31 +132,6 @@ void NeuronExtension::setSlaveAddress(int slaveAddress)
m_slaveAddress = slaveAddress;
}
QList<QString> NeuronExtension::digitalInputs()
{
return m_modbusDigitalInputRegisters.keys();
}
QList<QString> NeuronExtension::digitalOutputs()
{
return m_modbusDigitalOutputRegisters.keys();
}
QList<QString> NeuronExtension::analogInputs()
{
return m_modbusAnalogInputRegisters.keys();
}
QList<QString> NeuronExtension::analogOutputs()
{
return m_modbusAnalogOutputRegisters.keys();
}
QList<QString> NeuronExtension::userLEDs()
{
return m_modbusUserLEDRegisters.keys();
}
bool NeuronExtension::loadModbusMap()
{
qCDebug(dcUniPi()) << "Neuron Extension: Load modbus map";
@ -275,13 +250,13 @@ bool NeuronExtension::loadModbusMap()
csvFile->deleteLater();
return false;
}
QString circuit = list[5].split(" ").at(3);
int modbusAddress = list[0].toInt();
if (list[5].contains("Analog Input Value", Qt::CaseSensitivity::CaseInsensitive)) {
m_modbusAnalogInputRegisters.insert(circuit, list[0].toInt());
qDebug(dcUniPi()) << "Neuron Extension: Found analog input register" << circuit << list[0].toInt();
m_modbusAnalogInputRegisters.insert(modbusAddress, registerDescriptorFromStringList(list));
qDebug(dcUniPi()) << "Neuron Extension: Found analog input register" << modbusAddress;
} else if (list[5].contains("Analog Output Value", Qt::CaseSensitivity::CaseInsensitive)) {
m_modbusAnalogOutputRegisters.insert(circuit, list[0].toInt());
qDebug(dcUniPi()) << "Neuron Extension: Found analog output register" << circuit << list[0].toInt();
m_modbusAnalogOutputRegisters.insert(modbusAddress, registerDescriptorFromStringList(list));
qDebug(dcUniPi()) << "Neuron Extension: Found analog output register" << modbusAddress;
}
}
}
@ -330,28 +305,28 @@ bool NeuronExtension::modbusReadRequest(const QModbusDataUnit &request)
if (circuitValueChanged(circuit, unit.value(i)))
emit userLEDStatusChanged(circuit, unit.value(i));
} else {
qCWarning(dcUniPi()) << "Neuron Extension: Received unrecorgnised coil register" << modbusAddress;
qCWarning(dcUniPi()) << "Neuron Extension: Received unrecognised coil register" << modbusAddress;
}
break;
case QModbusDataUnit::RegisterType::InputRegisters:
if(m_modbusAnalogInputRegisters.values().contains(modbusAddress)){
circuit = m_modbusAnalogInputRegisters.key(modbusAddress);
if(m_modbusAnalogInputRegisters.contains(modbusAddress)){
circuit = m_modbusAnalogInputRegisters.value(modbusAddress).circuit;
quint32 value = ((unit.value(i) << 16) | unit.value(i+1));
if (circuitValueChanged(circuit, value))
emit analogInputStatusChanged(circuit, value);
i++;
} else {
qCWarning(dcUniPi()) << "Neuron Extension: Received unrecorgnised input register" << modbusAddress;
qCWarning(dcUniPi()) << "Neuron Extension: Received unrecognised input register" << modbusAddress;
}
break;
case QModbusDataUnit::RegisterType::HoldingRegisters:
if(m_modbusAnalogOutputRegisters.values().contains(modbusAddress)){
circuit = m_modbusAnalogOutputRegisters.key(modbusAddress);
if(m_modbusAnalogOutputRegisters.contains(modbusAddress)){
circuit = m_modbusAnalogOutputRegisters.value(modbusAddress).circuit;
if (circuitValueChanged(circuit, unit.value(i)))
emit analogOutputStatusChanged(circuit, unit.value(i));
} else {
qCWarning(dcUniPi()) << "Neuron Extension: Received unrecorgnised holding register" << modbusAddress;
qCWarning(dcUniPi()) << "Neuron Extension: Received unrecognised holding register" << modbusAddress;
}
break;
case QModbusDataUnit::RegisterType::DiscreteInputs:
@ -401,8 +376,8 @@ bool NeuronExtension::modbusWriteRequest(const Request &request)
if(m_modbusDigitalOutputRegisters.values().contains(modbusAddress)){
QString circuit = m_modbusDigitalOutputRegisters.key(modbusAddress);
emit digitalOutputStatusChanged(circuit, unit.value(0));
} else if(m_modbusAnalogOutputRegisters.values().contains(modbusAddress)){
QString circuit = m_modbusAnalogOutputRegisters.key(modbusAddress);
} else if(m_modbusAnalogOutputRegisters.contains(modbusAddress)){
QString circuit = m_modbusAnalogOutputRegisters.value(modbusAddress).circuit;
emit analogOutputStatusChanged(circuit, unit.value(0));
} else if(m_modbusUserLEDRegisters.values().contains(modbusAddress)){
QString circuit = m_modbusUserLEDRegisters.key(modbusAddress);
@ -426,20 +401,18 @@ bool NeuronExtension::modbusWriteRequest(const Request &request)
return true;
}
bool NeuronExtension::circuitValueChanged(const QString &circuit, quint32 value)
bool NeuronExtension::getAnalogIO(const RegisterDescriptor &descriptor)
{
if (m_previousCircuitValue.contains(circuit)) {
if (m_previousCircuitValue.value(circuit) == value) {
// Only emit status change of the circuit value has changed
return false;
} else {
m_previousCircuitValue.insert(circuit, value); //update existing value
return true;
}
QModbusDataUnit request = QModbusDataUnit(descriptor.registerType, descriptor.address, descriptor.count);
if (m_readRequestQueue.isEmpty()) {
return modbusReadRequest(request);
} else if (m_readRequestQueue.length() > 100) {
qCWarning(dcUniPi()) << "Neuron: Too many pending read requests";
return false;
} else {
m_previousCircuitValue.insert(circuit, value);
return true;
m_readRequestQueue.append(request);
}
return true;
}
bool NeuronExtension::getDigitalInput(const QString &circuit)
@ -556,22 +529,24 @@ bool NeuronExtension::getAllDigitalInputs()
bool NeuronExtension::getAllAnalogOutputs()
{
if (!m_modbusInterface)
if (!m_modbusInterface) {
qCWarning(dcUniPi()) << "Neuron: Modbus interface not initialized";
return false;
foreach (QString circuit, m_modbusAnalogOutputRegisters.keys()) {
getAnalogOutput(circuit);
}
Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) {
getAnalogIO(descriptor);
}
return true;
}
bool NeuronExtension::getAllAnalogInputs()
{
if (!m_modbusInterface)
if (!m_modbusInterface) {
qCWarning(dcUniPi()) << "Neuron: Modbus interface not initialized";
return false;
foreach (QString circuit, m_modbusAnalogInputRegisters.keys()) {
getAnalogInput(circuit);
}
Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogInputRegisters.values()) {
getAnalogIO(descriptor);
}
return true;
}
@ -624,64 +599,57 @@ bool NeuronExtension::getAllDigitalOutputs()
QUuid NeuronExtension::setAnalogOutput(const QString &circuit, double value)
{
int modbusAddress = m_modbusAnalogOutputRegisters.value(circuit);
qDebug(dcUniPi()) << "Neuron Extension: Set analog output" << circuit << value;
if (!m_modbusInterface)
return "";
Request request;
request.id = QUuid::createUuid();
request.data = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, modbusAddress, 1);
request.data.setValue(0, static_cast<uint16_t>(value));
Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters) {
if (descriptor.circuit == circuit) {
Request request;
request.id = QUuid::createUuid();
request.data = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, descriptor.address, descriptor.count);
if (descriptor.count == 1) {
request.data.setValue(0, (static_cast<uint>(value*400))); // 0 to 4000 = 0 to 10.0 V
} else if (descriptor.count == 2) {
request.data.setValue(0, (static_cast<uint32_t>(value) >> 16));
request.data.setValue(1, (static_cast<uint32_t>(value) & 0xffff));
}
if (m_writeRequestQueue.isEmpty()) {
modbusWriteRequest(request);
} else if (m_writeRequestQueue.length() > 100) {
return "";
} else {
m_writeRequestQueue.append(request);
if (m_writeRequestQueue.isEmpty()) {
modbusWriteRequest(request);
} else if (m_writeRequestQueue.length() > 100) {
return "";
} else {
m_writeRequestQueue.append(request);
}
return request.id;
}
}
return request.id;
return "";
}
bool NeuronExtension::getAnalogOutput(const QString &circuit)
{
int modbusAddress = m_modbusAnalogOutputRegisters.value(circuit);
if (!m_modbusInterface)
return false;
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, modbusAddress, 1);
if (m_readRequestQueue.isEmpty()) {
return modbusReadRequest(request);
} else if (m_readRequestQueue.length() > 100) {
qCWarning(dcUniPi()) << "Neuron extension: Too many pending read requests";
return false;
} else {
m_readRequestQueue.append(request);
//qDebug(dcUniPi()) << "Neuron Extension: Get analog output" << circuit;
Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) {
if (descriptor.circuit == circuit) {
return getAnalogIO(descriptor);
}
}
return true;
return false;
}
bool NeuronExtension::getAnalogInput(const QString &circuit)
{
int modbusAddress = m_modbusAnalogInputRegisters.value(circuit);
if (!m_modbusInterface)
return false;
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, modbusAddress, 2);
if (m_readRequestQueue.isEmpty()) {
return modbusReadRequest(request);
} else if (m_readRequestQueue.length() > 100) {
qCWarning(dcUniPi()) << "Neuron extension: Too many pending read requests";
return false;
} else {
m_readRequestQueue.append(request);
//qDebug(dcUniPi()) << "Neuron Extension: Get analog input" << circuit;
Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogInputRegisters.values()) {
if (descriptor.circuit == circuit) {
return getAnalogIO(descriptor);
}
}
return true;
return false;
}
QUuid NeuronExtension::setUserLED(const QString &circuit, bool value)

View File

@ -31,22 +31,18 @@
#ifndef NEURONEXTENSION_H
#define NEURONEXTENSION_H
#include "neuroncommon.h"
#include <QObject>
#include <QHash>
#include <QTimer>
#include <QtSerialBus>
#include <QUuid>
class NeuronExtension : public QObject
class NeuronExtension : public NeuronCommon
{
Q_OBJECT
public:
struct Request {
QUuid id;
QModbusDataUnit data;
};
enum ExtensionTypes {
xS10,
xS20,
@ -66,12 +62,6 @@ public:
int slaveAddress();
void setSlaveAddress(int slaveAddress);
QList<QString> digitalInputs();
QList<QString> digitalOutputs();
QList<QString> analogInputs();
QList<QString> analogOutputs();
QList<QString> userLEDs();
QUuid setDigitalOutput(const QString &circuit, bool value);
bool getDigitalOutput(const QString &circuit);
bool getDigitalInput(const QString &circuit);
@ -87,19 +77,8 @@ public:
QUuid setUserLED(const QString &circuit, bool value);
bool getUserLED(const QString &circuit);
private:
uint m_responseTimeoutTime = 2000;
QTimer *m_inputPollingTimer = nullptr;
QTimer *m_outputPollingTimer = nullptr;
QHash<QString, int> m_modbusDigitalOutputRegisters;
QHash<QString, int> m_modbusDigitalInputRegisters;
QHash<QString, int> m_modbusAnalogInputRegisters;
QHash<QString, int> m_modbusAnalogOutputRegisters;
QHash<QString, int> m_modbusUserLEDRegisters;
QList<Request> m_writeRequestQueue;
QList<QModbusDataUnit> m_readRequestQueue;
QModbusRtuSerialMaster *m_modbusInterface = nullptr;
int m_slaveAddress = 0;
@ -110,20 +89,7 @@ private:
bool modbusWriteRequest(const Request &request);
bool modbusReadRequest(const QModbusDataUnit &request);
bool circuitValueChanged(const QString &circuit, quint32 value);
signals:
void requestExecuted(const QUuid &requestId, bool success);
void requestError(const QUuid &requestId, const QString &error);
void digitalInputStatusChanged(const QString &circuit, bool value);
void digitalOutputStatusChanged(const QString &circuit, bool value);
void analogInputStatusChanged(const QString &circuit, double value);
void analogOutputStatusChanged(const QString &circuit, double value);
void userLEDStatusChanged(const QString &circuit, bool value);
void connectionStateChanged(bool state);
bool getAnalogIO(const RegisterDescriptor &descriptor);
private slots:
void onOutputPollingTimer();

View File

@ -11,6 +11,7 @@ QT += \
SOURCES += \
integrationpluginunipi.cpp \
neuron.cpp \
neuroncommon.cpp \
neuronextension.cpp \
mcp23008.cpp \
i2cport.cpp \
@ -21,6 +22,7 @@ SOURCES += \
HEADERS += \
integrationpluginunipi.h \
neuron.h \
neuroncommon.h \
neuronextension.h \
mcp23008.h \
i2cport.h \