diff --git a/unipi/neuron.cpp b/unipi/neuron.cpp index 65bdfb1..72547c2 100644 --- a/unipi/neuron.cpp +++ b/unipi/neuron.cpp @@ -35,12 +35,12 @@ #include #include -Neuron::Neuron(NeuronTypes neuronType, QModbusTcpClient *modbusInterface, QObject *parent) : - NeuronCommon(parent), - m_modbusInterface(modbusInterface), +Neuron::Neuron(NeuronTypes neuronType, QModbusClient *modbusInterface, QObject *parent) : + NeuronCommon(modbusInterface, 0, parent), m_neuronType(neuronType) { qCDebug(dcUniPi()) << "Neuron: Creating Neuron connection" << neuronType; + } Neuron::~Neuron() @@ -48,25 +48,6 @@ Neuron::~Neuron() qCDebug(dcUniPi()) << "Neuron: Deleting Neuron connection" << m_neuronType; } -bool Neuron::init() -{ - qCDebug(dcUniPi()) << "Neuron: Init"; - if (!loadModbusMap()) { - return false; - } - - if (!m_modbusInterface) { - qWarning(dcUniPi()) << "Neuron: Modbus TCP interface not available"; - return false; - } - - if (m_modbusInterface->connectDevice()) { - qWarning(dcUniPi()) << "Neuron: Could not connect to modbus TCP device"; - return false; - } - return true; -} - QString Neuron::type() { switch (m_neuronType) { @@ -312,483 +293,3 @@ bool Neuron::loadModbusMap() } return true; } - - -bool Neuron::modbusWriteRequest(const Request &request) -{ - if (!m_modbusInterface) - return false; - - if (QModbusReply *reply = m_modbusInterface->sendWriteRequest(request.data, m_slaveAddress)) { - if (!reply->isFinished()) { - connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); - connect(reply, &QModbusReply::finished, this, [reply, request, this] { - - if (!m_writeRequestQueue.isEmpty()) { - modbusWriteRequest(m_writeRequestQueue.takeFirst()); - } - - if (reply->error() == QModbusDevice::NoError) { - requestExecuted(request.id, true); - const QModbusDataUnit unit = reply->result(); - int modbusAddress = unit.startAddress(); - if(m_modbusDigitalOutputRegisters.values().contains(modbusAddress)){ - QString circuit = m_modbusDigitalOutputRegisters.key(modbusAddress); - emit digitalOutputStatusChanged(circuit, unit.value(0)); - } 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); - emit userLEDStatusChanged(circuit, unit.value(0)); - } - } else { - requestExecuted(request.id, false); - qCWarning(dcUniPi()) << "Neuron: Write response error:" << reply->error(); - emit requestError(request.id, reply->errorString()); - } - }); - QTimer::singleShot(m_responseTimeoutTime, reply, &QModbusReply::deleteLater); - } else { - delete reply; // broadcast replies return immediately - return false; - } - } else { - qCWarning(dcUniPi()) << "Neuron: Read error: " << m_modbusInterface->errorString(); - return false; - } - return true; -} - - -bool Neuron::modbusReadRequest(const QModbusDataUnit &request) -{ - if (!m_modbusInterface) - return false; - - if (QModbusReply *reply = m_modbusInterface->sendReadRequest(request, m_slaveAddress)) { - if (!reply->isFinished()) { - connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); - connect(reply, &QModbusReply::finished, this, [reply, this] { - - int modbusAddress = 0; - - if (reply->error() == QModbusDevice::NoError) { - const QModbusDataUnit unit = reply->result(); - - for (int i = 0; i < static_cast(unit.valueCount()); i++) { - //qCDebug(dcUniPi()) << "Start Address:" << unit.startAddress() << "Register Type:" << unit.registerType() << "Value:" << unit.value(i); - modbusAddress = unit.startAddress() + i; - - QString circuit; - switch (unit.registerType()) { - case QModbusDataUnit::RegisterType::Coils: - if(m_modbusDigitalInputRegisters.values().contains(modbusAddress)){ - circuit = m_modbusDigitalInputRegisters.key(modbusAddress); - if (circuitValueChanged(circuit, unit.value(i))) - emit digitalInputStatusChanged(circuit, unit.value(i)); - } else if(m_modbusDigitalOutputRegisters.values().contains(modbusAddress)){ - circuit = m_modbusDigitalOutputRegisters.key(modbusAddress); - if (circuitValueChanged(circuit, unit.value(i))) - emit digitalOutputStatusChanged(circuit, unit.value(i)); - } else if(m_modbusUserLEDRegisters.values().contains(modbusAddress)){ - circuit = m_modbusUserLEDRegisters.key(modbusAddress); - if (circuitValueChanged(circuit, unit.value(i))) - emit userLEDStatusChanged(circuit, unit.value(i)); - } else { - qCWarning(dcUniPi()) << "Neuron: Received unrecorgnised coil register" << modbusAddress; - } - break; - - case QModbusDataUnit::RegisterType::HoldingRegisters: - 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 { - qCWarning(dcUniPi()) << "Neuron: Received unrecognised holding register" << modbusAddress; - } - break; - case QModbusDataUnit::RegisterType::InputRegisters: - 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); - i++; - } else { - qCWarning(dcUniPi()) << "Neuron: Received unrecognised input register" << modbusAddress; - } - break; - case QModbusDataUnit::RegisterType::DiscreteInputs: - case QModbusDataUnit::RegisterType::Invalid: - qCWarning(dcUniPi()) << "Neuron: Invalide register type"; - break; - } - } - } else if (reply->error() == QModbusDevice::ProtocolError) { - qCWarning(dcUniPi()) << "Neuron: Read response error:" << reply->errorString() << reply->rawResult().exceptionCode(); - } else { - qCWarning(dcUniPi()) << "Neuron: Read response error:" << reply->error() << reply->errorString(); - } - }); - QTimer::singleShot(m_responseTimeoutTime, reply, &QModbusReply::deleteLater); - } else { - delete reply; // broadcast replies return immediately - return false; - } - } else { - qCWarning(dcUniPi()) << "Neuron: Read error: " << m_modbusInterface->errorString(); - return false; - } - return true; -} - - -bool Neuron::getInputRegisters(QList registerList) -{ - if (registerList.isEmpty()) { - return true; - } - - std::sort(registerList.begin(), registerList.end()); - int previousReg = registerList.first(); //first register to read and starting point to get the following registers - int startAddress; - - QHash registerGroups; - - foreach (int reg, registerList) { - //qDebug(dcUniPi()) << "Register" << reg << "previous Register" << previousReg; - if (reg == previousReg) { //first register - startAddress = reg; - registerGroups.insert(startAddress, 1); - } else if (reg == (previousReg + 1)) { //next register in block - previousReg = reg; - registerGroups.insert(startAddress, (registerGroups.value(startAddress) + 1)); //update block length - } else { // new block - startAddress = reg; - previousReg = reg; - registerGroups.insert(startAddress, 1); - } - } - - foreach (int startAddress, registerGroups.keys()) { - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, startAddress, registerGroups.value(startAddress)); - if (m_readRequestQueue.isEmpty()) { - modbusReadRequest(request); - } else if (m_readRequestQueue.length() > 100) { - qCWarning(dcUniPi()) << "Neuron: Too many pending read requests"; - } else { - m_readRequestQueue.append(request); - } - } - return true; -} - -bool Neuron::getHoldingRegisters(QList registerList) -{ - if (registerList.isEmpty()) { - return true; - } - - std::sort(registerList.begin(), registerList.end()); - int previousReg = registerList.first(); //first register to read and starting point to get the following registers - int startAddress; - - QHash registerGroups; - - foreach (int reg, registerList) { - //qDebug(dcUniPi()) << "Register" << reg << "previous Register" << previousReg; - if (reg == previousReg) { //first register - startAddress = reg; - registerGroups.insert(startAddress, 1); - } else if (reg == (previousReg + 1)) { //next register in block - previousReg = reg; - registerGroups.insert(startAddress, (registerGroups.value(startAddress) + 1)); //update block length - } else { // new block - startAddress = reg; - previousReg = reg; - registerGroups.insert(startAddress, 1); - } - } - - foreach (int startAddress, registerGroups.keys()) { - qDebug(dcUniPi()) << "Register" << startAddress << "length" << registerGroups.value(startAddress); - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, startAddress, registerGroups.value(startAddress)); - modbusReadRequest(request); - } - return true; -} - -bool Neuron::getCoils(QList registerList) -{ - if (registerList.isEmpty()) { - return true; - } - - std::sort(registerList.begin(), registerList.end()); - int previousReg = registerList.first(); //first register to read and starting point to get the following registers - int startAddress; - - QHash registerGroups; - - foreach (int reg, registerList) { - //qDebug(dcUniPi()) << "Register" << reg << "previous Register" << previousReg; - if (reg == previousReg) { //first register - startAddress = reg; - registerGroups.insert(startAddress, 1); - } else if (reg == (previousReg + 1)) { //next register in block - previousReg = reg; - registerGroups.insert(startAddress, (registerGroups.value(startAddress) + 1)); //update block length - } else { // new block - startAddress = reg; - previousReg = reg; - registerGroups.insert(startAddress, 1); - } - } - - foreach (int startAddress, registerGroups.keys()) { - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, startAddress, registerGroups.value(startAddress)); - if (m_readRequestQueue.isEmpty()) { - modbusReadRequest(request); - } else if (m_readRequestQueue.length() > 100) { - qCWarning(dcUniPi()) << "Neuron: Too many pending read requests"; - } else { - m_readRequestQueue.append(request); - } - } - return true; -} - -bool Neuron::getAnalogIO(const RegisterDescriptor &descriptor) -{ - 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_readRequestQueue.append(request); - } - return true; -} - -bool Neuron::getAllDigitalInputs() -{ - if (!m_modbusInterface) { - qCWarning(dcUniPi()) << "Neuron: Ḿodbus interface not initialized"; - return false; - } - return getCoils(m_modbusDigitalInputRegisters.values()); -} - -bool Neuron::getAllDigitalOutputs() -{ - if (!m_modbusInterface) { - qCWarning(dcUniPi()) << "Neuron: Modbus interface not initialized"; - return false; - } - return getCoils(m_modbusDigitalOutputRegisters.values()); -} - -bool Neuron::getAllAnalogInputs() -{ - if (!m_modbusInterface) { - qCWarning(dcUniPi()) << "Neuron: Modbus interface not initialized"; - return false; - } - Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogInputRegisters.values()) { - getAnalogIO(descriptor); - } - return true; -} - -bool Neuron::getAllAnalogOutputs() -{ - if (!m_modbusInterface) { - qCWarning(dcUniPi()) << "Neuron: Modbus interface not initialized"; - return false; - } - Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) { - getAnalogIO(descriptor); - } - return true; -} - -bool Neuron::getDigitalInput(const QString &circuit) -{ - int modbusAddress = m_modbusDigitalInputRegisters.value(circuit); - //qDebug(dcUniPi()) << "Neuron: Reading digital Input" << circuit << modbusAddress; - - if (!m_modbusInterface) - return false; - - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, 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); - } - return true; -} - -bool Neuron::getAnalogOutput(const QString &circuit) -{ - //qDebug(dcUniPi()) << "Neuron: Get analog output" << circuit; - Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) { - if (descriptor.circuit == circuit) { - return getAnalogIO(descriptor); - } - } - return false; -} - - -QUuid Neuron::setDigitalOutput(const QString &circuit, bool value) -{ - int modbusAddress = m_modbusDigitalOutputRegisters.value(circuit); - //qDebug(dcUniPi()) << "Neuron: Setting digital ouput" << circuit << modbusAddress << value; - - Request request; - request.data = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, modbusAddress, 1); - request.data.setValue(0, static_cast(value)); - request.id = QUuid::createUuid(); - - if (m_writeRequestQueue.isEmpty()) { - modbusWriteRequest(request); - } else if (m_writeRequestQueue.length() > 100) { - return ""; - } else { - m_writeRequestQueue.append(request); - } - return request.id; -} - - -bool Neuron::getDigitalOutput(const QString &circuit) -{ - int modbusAddress = m_modbusDigitalOutputRegisters.value(circuit); - //qDebug(dcUniPi()) << "Reading digital 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); - } - return true; -} - - -QUuid Neuron::setAnalogOutput(const QString &circuit, double value) -{ - qDebug(dcUniPi()) << "Neuron: Set analog output" << circuit << value; - if (!m_modbusInterface) - return ""; - - 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(value*400))); // 0 to 4000 = 0 to 10.0 V - } else if (descriptor.count == 2) { - request.data.setValue(0, (static_cast(value) >> 16)); - request.data.setValue(1, (static_cast(value) & 0xffff)); - } - - if (m_writeRequestQueue.isEmpty()) { - modbusWriteRequest(request); - } else if (m_writeRequestQueue.length() > 100) { - return ""; - } else { - m_writeRequestQueue.append(request); - } - return request.id; - } - } - return ""; -} - - -bool Neuron::getAnalogInput(const QString &circuit) -{ - //qDebug(dcUniPi()) << "Neuron: Get analog input" << circuit; - - Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) { - if (descriptor.circuit == circuit) { - return getAnalogIO(descriptor); - } - } - return false; -} - -QUuid Neuron::setUserLED(const QString &circuit, bool value) -{ - int modbusAddress = m_modbusUserLEDRegisters.value(circuit); - //qDebug(dcUniPi()) << "Neuron: Setting digital ouput" << circuit << modbusAddress << value; - - if (!m_modbusInterface) - return ""; - - Request request; - request.id = QUuid::createUuid(); - - request.data = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, modbusAddress, 1); - request.data.setValue(0, static_cast(value)); - - if (m_writeRequestQueue.isEmpty()) { - modbusWriteRequest(request); - } else if (m_writeRequestQueue.length() > 100) { - return ""; - } else { - m_writeRequestQueue.append(request); - } - - return request.id; -} - - -bool Neuron::getUserLED(const QString &circuit) -{ - int modbusAddress = m_modbusUserLEDRegisters.value(circuit); - //qDebug(dcUniPi()) << "Neuron: Get user LED" << circuit << modbusAddress; - - if (!m_modbusInterface) - return false; - - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, 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); - } - return true; -} - - -void Neuron::onOutputPollingTimer() -{ - getAllDigitalOutputs(); - getAllAnalogOutputs(); -} - -void Neuron::onInputPollingTimer() -{ - getAllDigitalInputs(); - getAllAnalogInputs(); -} diff --git a/unipi/neuron.h b/unipi/neuron.h index 490d24a..a728632 100644 --- a/unipi/neuron.h +++ b/unipi/neuron.h @@ -38,7 +38,6 @@ #include #include - class Neuron : public NeuronCommon { Q_OBJECT @@ -62,46 +61,12 @@ public: }; Q_ENUM(NeuronTypes) - explicit Neuron(NeuronTypes neuronType, QModbusTcpClient *modbusInterface, QObject *parent = nullptr); + explicit Neuron(NeuronTypes neuronType, QModbusClient *modbusInterface, QObject *parent = nullptr); ~Neuron(); - - bool init(); QString type(); - QUuid setDigitalOutput(const QString &circuit, bool value); - QUuid setAnalogOutput(const QString &circuit, double value); - QUuid setUserLED(const QString &circuit, bool value); - - bool getDigitalOutput(const QString &circuit); - bool getDigitalInput(const QString &circuit); - - bool getAnalogOutput(const QString &circuit); - bool getAnalogInput(const QString &circuit); - - bool getAllDigitalOutputs(); - bool getAllDigitalInputs(); - bool getAllAnalogInputs(); - bool getAllAnalogOutputs(); - - bool getUserLED(const QString &circuit); - private: - QModbusTcpClient *m_modbusInterface = nullptr; NeuronTypes m_neuronType = NeuronTypes::S103; - - bool loadModbusMap(); - bool modbusReadRequest(const QModbusDataUnit &request); - bool modbusWriteRequest(const Request &request); - - bool getInputRegisters(QList registers); - bool getHoldingRegisters(QList registers); - bool getCoils(QList registers); - - bool getAnalogIO(const RegisterDescriptor &descriptor); - -public slots: - void onOutputPollingTimer(); - void onInputPollingTimer(); + bool loadModbusMap() override; }; - #endif // NEURON_H diff --git a/unipi/neuroncommon.cpp b/unipi/neuroncommon.cpp index a33ef8e..e8ce0dc 100644 --- a/unipi/neuroncommon.cpp +++ b/unipi/neuroncommon.cpp @@ -29,10 +29,73 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "neuroncommon.h" +#include "extern-plugininfo.h" -NeuronCommon::NeuronCommon(QObject *parent) : QObject(parent) +NeuronCommon::NeuronCommon(QModbusClient *modbusInterface, int slaveAddress, QObject *parent) : + QObject(parent), + m_slaveAddress(slaveAddress), + m_modbusInterface(modbusInterface) { + m_inputPollingTimer = new QTimer(this); + connect(m_inputPollingTimer, &QTimer::timeout, this, &NeuronCommon::onInputPollingTimer); + m_inputPollingTimer->setTimerType(Qt::TimerType::PreciseTimer); + m_inputPollingTimer->setInterval(200); + m_outputPollingTimer = new QTimer(this); + connect(m_outputPollingTimer, &QTimer::timeout, this, &NeuronCommon::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) { + if (m_inputPollingTimer) + m_inputPollingTimer->start(); + if (m_outputPollingTimer) + m_outputPollingTimer->start(); + emit connectionStateChanged(true); + } else { + if (m_inputPollingTimer) + m_inputPollingTimer->stop(); + if (m_outputPollingTimer) + m_outputPollingTimer->stop(); + emit connectionStateChanged(false); + } + }); +} + +bool NeuronCommon::init() +{ + qCDebug(dcUniPi()) << "Neuron: Init"; + if (!loadModbusMap()) { + return false; + } + + if (!m_modbusInterface) { + qWarning(dcUniPi()) << "Neuron: Modbus interface not available"; + return false; + } + + if (m_modbusInterface->connectDevice()) { + qWarning(dcUniPi()) << "Neuron: Could not connect to modbus device"; + return false; + } + return true; +} + +int NeuronCommon::slaveAddress() +{ + return m_slaveAddress; +} + +void NeuronCommon::setSlaveAddress(int slaveAddress) +{ + qCDebug(dcUniPi()) << "Neuron: Set slave address" << slaveAddress; + m_slaveAddress = slaveAddress; } QList NeuronCommon::digitalInputs() @@ -109,3 +172,432 @@ bool NeuronCommon::circuitValueChanged(const QString &circuit, quint32 value) return true; } } + +void NeuronCommon::getAllDigitalInputs() +{ + getCoils(m_modbusDigitalInputRegisters.values()); +} + +void NeuronCommon::getAllDigitalOutputs() +{ + getCoils(m_modbusDigitalOutputRegisters.values()); +} + +void NeuronCommon::getAllAnalogInputs() +{ + Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogInputRegisters.values()) { + getAnalogIO(descriptor); + } +} + +void NeuronCommon::getAllAnalogOutputs() +{ + Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) { + getAnalogIO(descriptor); + } +} + +bool NeuronCommon::getDigitalInput(const QString &circuit) +{ + if (m_modbusDigitalInputRegisters.contains(circuit)) { + qCWarning(dcUniPi()) << "Neuron: Digital input circuit not found" << circuit; + return ""; + } + int modbusAddress = m_modbusDigitalInputRegisters.value(circuit); + //qDebug(dcUniPi()) << "Neuron: Reading digital Input" << circuit << modbusAddress; + + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, 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); + } + return true; +} + +bool NeuronCommon::getAnalogOutput(const QString &circuit) +{ + //qDebug(dcUniPi()) << "Neuron: Get analog output" << circuit; + Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) { + if (descriptor.circuit == circuit) { + return getAnalogIO(descriptor); + } + } + qCWarning(dcUniPi()) << "Neuron: Analog output circuit not found" << circuit; + return false; +} + + +QUuid NeuronCommon::setDigitalOutput(const QString &circuit, bool value) +{ + if (m_modbusDigitalOutputRegisters.contains(circuit)) { + qCWarning(dcUniPi()) << "Neuron: Digital output circuit not found" << circuit; + return ""; + } + int modbusAddress = m_modbusDigitalOutputRegisters.value(circuit); + //qDebug(dcUniPi()) << "Neuron: Setting digital ouput" << circuit << modbusAddress << value; + + Request request; + request.data = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, modbusAddress, 1); + request.data.setValue(0, static_cast(value)); + request.id = QUuid::createUuid(); + + if (m_writeRequestQueue.isEmpty()) { + modbusWriteRequest(request); + } else if (m_writeRequestQueue.length() > 100) { + return ""; + } else { + m_writeRequestQueue.append(request); + } + return request.id; +} + + +bool NeuronCommon::getDigitalOutput(const QString &circuit) +{ + if (m_modbusDigitalOutputRegisters.contains(circuit)) { + qCWarning(dcUniPi()) << "Neuron: Digital output circuit not found" << circuit; + return false; + } + int modbusAddress = m_modbusDigitalOutputRegisters.value(circuit); + //qDebug(dcUniPi()) << "Reading digital Output" << circuit << modbusAddress; + + 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); + } + return true; +} + + +QUuid NeuronCommon::setAnalogOutput(const QString &circuit, double value) +{ + qDebug(dcUniPi()) << "Neuron: Set analog output" << circuit << 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(value*400))); // 0 to 4000 = 0 to 10.0 V + } else if (descriptor.count == 2) { + request.data.setValue(0, (static_cast(value) >> 16)); + request.data.setValue(1, (static_cast(value) & 0xffff)); + } + + if (m_writeRequestQueue.isEmpty()) { + modbusWriteRequest(request); + } else if (m_writeRequestQueue.length() > 100) { + return ""; + } else { + m_writeRequestQueue.append(request); + } + return request.id; + } + } + qCWarning(dcUniPi()) << "Neuron: Analog output circuit not found" << circuit; + return ""; +} + + +bool NeuronCommon::getAnalogInput(const QString &circuit) +{ + //qDebug(dcUniPi()) << "Neuron: Get analog input" << circuit; + + Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) { + if (descriptor.circuit == circuit) { + return getAnalogIO(descriptor); + } + } + return false; +} + +QUuid NeuronCommon::setUserLED(const QString &circuit, bool value) +{ + int modbusAddress = m_modbusUserLEDRegisters.value(circuit); + //qDebug(dcUniPi()) << "Neuron: Setting user led" << circuit << modbusAddress << value; + + if (!m_modbusInterface) + return ""; + + Request request; + request.id = QUuid::createUuid(); + + request.data = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, modbusAddress, 1); + request.data.setValue(0, static_cast(value)); + + if (m_writeRequestQueue.isEmpty()) { + if (!modbusWriteRequest(request)) { + return ""; + } + } else if (m_writeRequestQueue.length() > 100) { + return ""; + } else { + m_writeRequestQueue.append(request); + } + + return request.id; +} + + +bool NeuronCommon::getUserLED(const QString &circuit) +{ + int modbusAddress = m_modbusUserLEDRegisters.value(circuit); + //qDebug(dcUniPi()) << "Neuron: Get user LED" << circuit << modbusAddress; + + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, 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); + } + return true; +} + +bool NeuronCommon::getAnalogIO(const RegisterDescriptor &descriptor) +{ + if (!m_modbusInterface) + return false; + + if (m_modbusInterface->state() != QModbusDevice::State::ConnectedState) + return false; + + 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_readRequestQueue.append(request); + } + return true; +} + +bool NeuronCommon::modbusWriteRequest(const Request &request) +{ + if (!m_modbusInterface) { + emit requestExecuted(request.id, false); + emit requestError(request.id, "Modbus interface not available"); + return false; + } + if (m_modbusInterface->state() != QModbusDevice::State::ConnectedState) { + emit requestExecuted(request.id, false); + emit requestError(request.id, "Device not connected"); + return false; + }; + + if (QModbusReply *reply = m_modbusInterface->sendWriteRequest(request.data, m_slaveAddress)) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [reply, request, this] { + + if (!m_writeRequestQueue.isEmpty()) { + modbusWriteRequest(m_writeRequestQueue.takeFirst()); + } + + if (reply->error() == QModbusDevice::NoError) { + emit requestExecuted(request.id, true); + const QModbusDataUnit unit = reply->result(); + int modbusAddress = unit.startAddress(); + if(m_modbusDigitalOutputRegisters.values().contains(modbusAddress)){ + QString circuit = m_modbusDigitalOutputRegisters.key(modbusAddress); + emit digitalOutputStatusChanged(circuit, unit.value(0)); + } 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); + emit userLEDStatusChanged(circuit, unit.value(0)); + } + } else { + emit requestExecuted(request.id, false); + qCWarning(dcUniPi()) << "Neuron: Write response error:" << reply->error(); + emit requestError(request.id, reply->errorString()); + } + }); + QTimer::singleShot(m_responseTimeoutTime, reply, &QModbusReply::deleteLater); + } else { + delete reply; // broadcast replies return immediately + return false; + } + } else { + qCWarning(dcUniPi()) << "Neuron: Read error: " << m_modbusInterface->errorString(); + return false; + } + return true; +} + + +bool NeuronCommon::modbusReadRequest(const QModbusDataUnit &request) +{ + if (!m_modbusInterface) { + return false; + } + if (m_modbusInterface->state() != QModbusDevice::State::ConnectedState) + return false; + + if (QModbusReply *reply = m_modbusInterface->sendReadRequest(request, m_slaveAddress)) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [reply, this] { + + int modbusAddress = 0; + + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + + for (uint i = 0; i < unit.valueCount(); i++) { + //qCDebug(dcUniPi()) << "Start Address:" << unit.startAddress() << "Register Type:" << unit.registerType() << "Value:" << unit.value(i); + modbusAddress = unit.startAddress() + i; + + QString circuit; + switch (unit.registerType()) { + case QModbusDataUnit::RegisterType::Coils: + if(m_modbusDigitalInputRegisters.values().contains(modbusAddress)){ + circuit = m_modbusDigitalInputRegisters.key(modbusAddress); + if (circuitValueChanged(circuit, unit.value(i))) + emit digitalInputStatusChanged(circuit, unit.value(i)); + } else if(m_modbusDigitalOutputRegisters.values().contains(modbusAddress)){ + circuit = m_modbusDigitalOutputRegisters.key(modbusAddress); + if (circuitValueChanged(circuit, unit.value(i))) + emit digitalOutputStatusChanged(circuit, unit.value(i)); + } else if(m_modbusUserLEDRegisters.values().contains(modbusAddress)){ + circuit = m_modbusUserLEDRegisters.key(modbusAddress); + if (circuitValueChanged(circuit, unit.value(i))) + emit userLEDStatusChanged(circuit, unit.value(i)); + } else { + qCWarning(dcUniPi()) << "Neuron: Received unrecognised coil register" << modbusAddress; + } + break; + + case QModbusDataUnit::RegisterType::HoldingRegisters: { + if (m_modbusAnalogOutputRegisters.keys().contains(modbusAddress)) { + RegisterDescriptor descriptor = m_modbusAnalogOutputRegisters.value(modbusAddress); + circuit = descriptor.circuit; + quint32 value = 0; + if (descriptor.count == 1) { + value = unit.value(i); + } else if (descriptor.count == 2) { + if (unit.valueCount() >= (i+1)) { + value = (unit.value(i) << 16 | unit.value(i+1)); + i++; + } else { + qCWarning(dcUniPi()) << "Neuron: Received analog output, but value count is too short"; + } + } + if (circuitValueChanged(circuit, value)) + emit analogOutputStatusChanged(circuit, value); + + } else { + qCWarning(dcUniPi()) << "Neuron: Received unrecognised holding register" << modbusAddress; + } + } break; + case QModbusDataUnit::RegisterType::InputRegisters: + if(m_modbusAnalogInputRegisters.keys().contains(modbusAddress)){ + RegisterDescriptor descriptor = m_modbusAnalogInputRegisters.value(modbusAddress); + circuit = descriptor.circuit; + quint32 value = 0; + if (descriptor.count == 1) { + value = unit.value(i); + } else if (descriptor.count == 2) { + if (unit.valueCount() >= (i+1)) { + value = (unit.value(i) << 16 | unit.value(i+1)); + i++; + } else { + qCWarning(dcUniPi()) << "Neuron: Received analog input, but value count is too short"; + } + } + if (circuitValueChanged(circuit, value)) + emit analogInputStatusChanged(circuit, value); + + } else { + qCWarning(dcUniPi()) << "Neuron: Received unrecognised input register" << modbusAddress; + } + break; + case QModbusDataUnit::RegisterType::DiscreteInputs: + case QModbusDataUnit::RegisterType::Invalid: + qCWarning(dcUniPi()) << "Neuron: Invalide register type"; + break; + } + } + } else if (reply->error() == QModbusDevice::ProtocolError) { + qCWarning(dcUniPi()) << "Neuron: Read response error:" << reply->errorString() << reply->rawResult().exceptionCode(); + } else { + qCWarning(dcUniPi()) << "Neuron: Read response error:" << reply->error() << reply->errorString(); + } + }); + QTimer::singleShot(m_responseTimeoutTime, reply, &QModbusReply::deleteLater); + } else { + delete reply; // broadcast replies return immediately + return false; + } + } else { + qCWarning(dcUniPi()) << "Neuron: Read error: " << m_modbusInterface->errorString(); + return false; + } + return true; +} + +void NeuronCommon::getCoils(QList registerList) +{ + if (registerList.isEmpty()) { + return; + } + + std::sort(registerList.begin(), registerList.end()); + int previousReg = registerList.first(); //first register to read and starting point to get the following registers + int startAddress; + + QHash registerGroups; + + foreach (int reg, registerList) { + //qDebug(dcUniPi()) << "Register" << reg << "previous Register" << previousReg; + if (reg == previousReg) { //first register + startAddress = reg; + registerGroups.insert(startAddress, 1); + } else if (reg == (previousReg + 1)) { //next register in block + previousReg = reg; + registerGroups.insert(startAddress, (registerGroups.value(startAddress) + 1)); //update block length + } else { // new block + startAddress = reg; + previousReg = reg; + registerGroups.insert(startAddress, 1); + } + } + + foreach (int startAddress, registerGroups.keys()) { + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, startAddress, registerGroups.value(startAddress)); + if (m_readRequestQueue.isEmpty()) { + modbusReadRequest(request); + } else if (m_readRequestQueue.length() > 100) { + qCWarning(dcUniPi()) << "Neuron: Too many pending read requests"; + } else { + m_readRequestQueue.append(request); + } + } +} + +void NeuronCommon::onOutputPollingTimer() +{ + getAllDigitalOutputs(); + getAllAnalogOutputs(); +} + +void NeuronCommon::onInputPollingTimer() +{ + getAllDigitalInputs(); + getAllAnalogInputs(); +} diff --git a/unipi/neuroncommon.h b/unipi/neuroncommon.h index 32f1317..2220138 100644 --- a/unipi/neuroncommon.h +++ b/unipi/neuroncommon.h @@ -38,7 +38,10 @@ class NeuronCommon : public QObject { Q_OBJECT public: - explicit NeuronCommon(QObject *parent = nullptr); + explicit NeuronCommon(QModbusClient *modbusInterface, int slaveAddress, QObject *parent = nullptr); + bool init(); + int slaveAddress(); + void setSlaveAddress(int slaveAddress); QList digitalInputs(); QList digitalOutputs(); @@ -46,6 +49,23 @@ public: QList analogOutputs(); QList userLEDs(); + QUuid setDigitalOutput(const QString &circuit, bool value); + QUuid setAnalogOutput(const QString &circuit, double value); + QUuid setUserLED(const QString &circuit, bool value); + + bool getDigitalOutput(const QString &circuit); + bool getDigitalInput(const QString &circuit); + + bool getAnalogOutput(const QString &circuit); + bool getAnalogInput(const QString &circuit); + + void getAllDigitalOutputs(); + void getAllDigitalInputs(); + void getAllAnalogInputs(); + void getAllAnalogOutputs(); + + bool getUserLED(const QString &circuit); + protected: enum RWPermission { RWPermissionNone, @@ -63,19 +83,8 @@ protected: 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 m_writeRequestQueue; - QList m_readRequestQueue; + virtual bool loadModbusMap() = 0; + RegisterDescriptor registerDescriptorFromStringList(const QStringList &data); QHash m_modbusDigitalOutputRegisters; QHash m_modbusDigitalInputRegisters; @@ -83,10 +92,29 @@ protected: QHash m_modbusAnalogInputRegisters; QHash m_modbusAnalogOutputRegisters; +private: + struct Request { + QUuid id; + QModbusDataUnit data; + }; + + int m_slaveAddress = 0; + uint m_responseTimeoutTime = 2000; + QModbusClient *m_modbusInterface = nullptr; + + QTimer *m_inputPollingTimer = nullptr; + QTimer *m_outputPollingTimer = nullptr; + + QList m_writeRequestQueue; + QList m_readRequestQueue; + QHash m_previousCircuitValue; - RegisterDescriptor registerDescriptorFromStringList(const QStringList &data); bool circuitValueChanged(const QString &circuit, quint32 value); + bool getAnalogIO(const RegisterDescriptor &descriptor); + bool modbusReadRequest(const QModbusDataUnit &request); + bool modbusWriteRequest(const Request &request); + void getCoils(QList registers); signals: void requestExecuted(const QUuid &requestId, bool success); @@ -101,6 +129,9 @@ signals: void connectionStateChanged(bool state); +private slots: + void onOutputPollingTimer(); + void onInputPollingTimer(); }; #endif // NEURONCOMMON_H diff --git a/unipi/neuronextension.cpp b/unipi/neuronextension.cpp index 3291c4a..e50bad7 100644 --- a/unipi/neuronextension.cpp +++ b/unipi/neuronextension.cpp @@ -36,68 +36,19 @@ #include #include -NeuronExtension::NeuronExtension(ExtensionTypes extensionType, QModbusRtuSerialMaster *modbusInterface, int slaveAddress, QObject *parent) : - NeuronCommon(parent), - m_modbusInterface(modbusInterface), - m_slaveAddress(slaveAddress), +NeuronExtension::NeuronExtension(ExtensionTypes extensionType, QModbusClient *modbusInterface, int slaveAddress, QObject *parent) : + NeuronCommon(modbusInterface, slaveAddress, parent), m_extensionType(extensionType) { - qCDebug(dcUniPi()) << "NeuronExtension: Creating extension" << extensionType; - m_inputPollingTimer = new QTimer(this); - connect(m_inputPollingTimer, &QTimer::timeout, this, &NeuronExtension::onInputPollingTimer); - m_inputPollingTimer->setTimerType(Qt::TimerType::PreciseTimer); - m_inputPollingTimer->setInterval(200); - - m_outputPollingTimer = new QTimer(this); - connect(m_outputPollingTimer, &QTimer::timeout, this, &NeuronExtension::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) { - if (m_inputPollingTimer) - m_inputPollingTimer->start(); - if (m_outputPollingTimer) - m_outputPollingTimer->start(); - emit connectionStateChanged(true); - } else { - if (m_inputPollingTimer) - m_inputPollingTimer->stop(); - if (m_outputPollingTimer) - m_outputPollingTimer->stop(); - emit connectionStateChanged(false); - } - }); + qCDebug(dcUniPi()) << "Neuron: Creating extension" << extensionType; } NeuronExtension::~NeuronExtension() { - qCDebug(dcUniPi()) << "Neuron Extension: Deleting extension" << m_extensionType; + qCDebug(dcUniPi()) << "Neuron: Deleting extension" << m_extensionType; } -bool NeuronExtension::init() -{ - qCDebug(dcUniPi()) << "Neuron Extension: Init"; - if (!loadModbusMap()) { - return false; - } - if (!m_modbusInterface) { - qWarning(dcUniPi()) << "Neuron Extension: Modbus RTU interface not available"; - return false; - } - - if (m_modbusInterface->connectDevice()) { - qWarning(dcUniPi()) << "Neuron Extension: Could not connect to RTU device"; - return false; - } - return true; -} QString NeuronExtension::type() { @@ -121,20 +72,9 @@ QString NeuronExtension::type() } } -int NeuronExtension::slaveAddress() -{ - return m_slaveAddress; -} - -void NeuronExtension::setSlaveAddress(int slaveAddress) -{ - qCDebug(dcUniPi()) << "Neuron Extension: Set slave address" << slaveAddress; - m_slaveAddress = slaveAddress; -} - bool NeuronExtension::loadModbusMap() { - qCDebug(dcUniPi()) << "Neuron Extension: Load modbus map"; + qCDebug(dcUniPi()) << "Neuron: Load modbus map"; QStringList fileCoilList; QStringList fileRegisterList; @@ -165,7 +105,7 @@ bool NeuronExtension::loadModbusMap() foreach (QString relativeFilePath, fileCoilList) { QString absoluteFilePath = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).last() + "/nymea/modbus" + relativeFilePath; - qDebug(dcUniPi()) << "Neuron Extension: Open CSV File:" << absoluteFilePath; + qDebug(dcUniPi()) << "Neuron: Open CSV File:" << absoluteFilePath; QFile *csvFile = new QFile(absoluteFilePath); if (!csvFile->open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(dcUniPi()) << csvFile->errorString() << absoluteFilePath; @@ -228,7 +168,7 @@ bool NeuronExtension::loadModbusMap() foreach (QString relativeFilePath, fileRegisterList) { QString absoluteFilePath = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).last() + "/nymea/modbus" + relativeFilePath; - qDebug(dcUniPi()) << "Neuron Extension: Open CSV File:" << absoluteFilePath; + qDebug(dcUniPi()) << "Neuron: Open CSV File:" << absoluteFilePath; QFile *csvFile = new QFile(absoluteFilePath); if (!csvFile->open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(dcUniPi()) << csvFile->errorString() << absoluteFilePath; @@ -240,23 +180,23 @@ bool NeuronExtension::loadModbusMap() QString line = textStream->readLine(); QStringList list = line.split(','); if (list.length() <= 5) { - qCWarning(dcUniPi()) << "Neuron Extension: Currupted CSV file:" << csvFile->fileName(); + qCWarning(dcUniPi()) << "Neuron: Currupted CSV file:" << csvFile->fileName(); csvFile->deleteLater(); return false; } if (list.last() == "Basic" && list[5].split(" ").length() > 3) { if (list[5].split(" ").length() <= 3) { - qCWarning(dcUniPi()) << "Neuron Extension: Currupted CSV file:" << csvFile->fileName(); + qCWarning(dcUniPi()) << "Neuron: Currupted CSV file:" << csvFile->fileName(); csvFile->deleteLater(); return false; } int modbusAddress = list[0].toInt(); if (list[5].contains("Analog Input Value", Qt::CaseSensitivity::CaseInsensitive)) { m_modbusAnalogInputRegisters.insert(modbusAddress, registerDescriptorFromStringList(list)); - qDebug(dcUniPi()) << "Neuron Extension: Found analog input register" << modbusAddress; + qDebug(dcUniPi()) << "Neuron: Found analog input register" << modbusAddress; } else if (list[5].contains("Analog Output Value", Qt::CaseSensitivity::CaseInsensitive)) { m_modbusAnalogOutputRegisters.insert(modbusAddress, registerDescriptorFromStringList(list)); - qDebug(dcUniPi()) << "Neuron Extension: Found analog output register" << modbusAddress; + qDebug(dcUniPi()) << "Neuron: Found analog output register" << modbusAddress; } } } @@ -265,448 +205,3 @@ bool NeuronExtension::loadModbusMap() } return true; } - -bool NeuronExtension::modbusReadRequest(const QModbusDataUnit &request) -{ - if (!m_modbusInterface) - return false; - - if (QModbusReply *reply = m_modbusInterface->sendReadRequest(request, m_slaveAddress)) { - if (!reply->isFinished()) { - connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); - connect(reply, &QModbusReply::finished, this, [reply, this] { - - int modbusAddress = 0; - - if (!m_readRequestQueue.isEmpty()) { - modbusReadRequest(m_readRequestQueue.takeFirst()); - } - - if (reply->error() == QModbusDevice::NoError) { - const QModbusDataUnit unit = reply->result(); - - for (int i = 0; i < static_cast(unit.valueCount()); i++) { - //qCDebug(dcUniPi()) << "Neuron Extension: Start Address:" << unit.startAddress() << "Register Type:" << unit.registerType() << "Value:" << unit.value(i); - modbusAddress = unit.startAddress() + i; - - QString circuit; - switch (unit.registerType()) { - case QModbusDataUnit::RegisterType::Coils: - if(m_modbusDigitalInputRegisters.values().contains(modbusAddress)){ - circuit = m_modbusDigitalInputRegisters.key(modbusAddress); - if (circuitValueChanged(circuit, unit.value(i))) - emit digitalInputStatusChanged(circuit, unit.value(i)); - } else if(m_modbusDigitalOutputRegisters.values().contains(modbusAddress)){ - circuit = m_modbusDigitalOutputRegisters.key(modbusAddress); - if (circuitValueChanged(circuit, unit.value(i))) - emit digitalOutputStatusChanged(circuit, unit.value(i)); - } else if(m_modbusUserLEDRegisters.values().contains(modbusAddress)){ - circuit = m_modbusUserLEDRegisters.key(modbusAddress); - if (circuitValueChanged(circuit, unit.value(i))) - emit userLEDStatusChanged(circuit, unit.value(i)); - } else { - qCWarning(dcUniPi()) << "Neuron Extension: Received unrecognised coil register" << modbusAddress; - } - break; - - case QModbusDataUnit::RegisterType::InputRegisters: - 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 unrecognised input register" << modbusAddress; - } - break; - case QModbusDataUnit::RegisterType::HoldingRegisters: - 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 unrecognised holding register" << modbusAddress; - } - break; - case QModbusDataUnit::RegisterType::DiscreteInputs: - case QModbusDataUnit::RegisterType::Invalid: - qCWarning(dcUniPi()) << "Neuron Extension: Invalide register type"; - break; - } - } - - } else if (reply->error() == QModbusDevice::ProtocolError) { - qCWarning(dcUniPi()) << "Neuron Extension: Read response error:" << reply->errorString() << reply->rawResult().exceptionCode(); - } else { - qCWarning(dcUniPi()) << "Neuron Extension: Read response error:" << reply->error(); - } - }); - QTimer::singleShot(m_responseTimeoutTime, reply, &QModbusReply::deleteLater); - } else { - delete reply; // broadcast replies return immediately - return false; - } - } else { - qCWarning(dcUniPi()) << "Neuron Extension: Read error: " << m_modbusInterface->errorString(); - return false; - } - return true; -} - - -bool NeuronExtension::modbusWriteRequest(const Request &request) -{ - if (!m_modbusInterface) - return false; - - if (QModbusReply *reply = m_modbusInterface->sendWriteRequest(request.data, m_slaveAddress)) { - if (!reply->isFinished()) { - connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); - connect(reply, &QModbusReply::finished, this, [reply, request, this] { - - if (!m_writeRequestQueue.isEmpty()) { - modbusWriteRequest(m_writeRequestQueue.takeFirst()); - } - - if (reply->error() == QModbusDevice::NoError) { - requestExecuted(request.id, true); - const QModbusDataUnit unit = reply->result(); - int modbusAddress = unit.startAddress(); - if(m_modbusDigitalOutputRegisters.values().contains(modbusAddress)){ - QString circuit = m_modbusDigitalOutputRegisters.key(modbusAddress); - emit digitalOutputStatusChanged(circuit, unit.value(0)); - } 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); - emit userLEDStatusChanged(circuit, unit.value(0)); - } - } else { - requestExecuted(request.id, false); - qCWarning(dcUniPi()) << "Neuron Extension: Read response error:" << reply->error(); - emit requestError(request.id, reply->errorString()); - } - }); - QTimer::singleShot(m_responseTimeoutTime, reply, &QModbusReply::deleteLater); - } else { - delete reply; // broadcast replies return immediately - return false; - } - } else { - qCWarning(dcUniPi()) << "Neuron Extension: Read error: " << m_modbusInterface->errorString(); - return false; - } - return true; -} - -bool NeuronExtension::getAnalogIO(const RegisterDescriptor &descriptor) -{ - 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_readRequestQueue.append(request); - } - return true; -} - -bool NeuronExtension::getDigitalInput(const QString &circuit) -{ - int modbusAddress = m_modbusDigitalInputRegisters.value(circuit); - //qDebug(dcUniPi()) << "Reading digital input" << circuit << modbusAddress; - - if (!m_modbusInterface) - return false; - - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, modbusAddress, 1); - if (m_readRequestQueue.isEmpty()) { - return modbusReadRequest(request); - } else if (m_readRequestQueue.length() > 100) { - return ""; - } else { - m_readRequestQueue.append(request); - } - return true; -} - - -QUuid NeuronExtension::setDigitalOutput(const QString &circuit, bool value) -{ - int modbusAddress = m_modbusDigitalOutputRegisters.value(circuit); - //qDebug(dcUniPi()) << "Setting digital ouput" << circuit << modbusAddress; - - if (!m_modbusInterface) - return ""; - - Request request; - request.id = QUuid::createUuid(); - - request.data = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, modbusAddress, 1); - request.data.setValue(0, static_cast(value)); - - if (m_writeRequestQueue.isEmpty()) { - modbusWriteRequest(request); - } else if (m_writeRequestQueue.length() > 100) { - return ""; - } else { - m_writeRequestQueue.append(request); - } - - return request.id; -} - -bool NeuronExtension::getDigitalOutput(const QString &circuit) -{ - int modbusAddress = m_modbusDigitalOutputRegisters.value(circuit); - //qDebug(dcUniPi()) << "Reading digital output" << circuit << modbusAddress; - - if (!m_modbusInterface) - return false; - - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, 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); - } - return true; -} - - -bool NeuronExtension::getAllDigitalInputs() -{ - if (!m_modbusInterface) - return false; - - QList requests; - QList registerList = m_modbusDigitalInputRegisters.values(); - - if (registerList.isEmpty()) { - return true; //device has no digital inputs - } - - std::sort(registerList.begin(), registerList.end()); - int previousReg = registerList.first(); //first register to read and starting point to get the following registers - int startAddress; - - QHash registerGroups; - - foreach (int reg, registerList) { - //qDebug(dcUniPi()) << "Register" << reg << "previous Register" << previousReg; - if (reg == previousReg) { //first register - startAddress = reg; - registerGroups.insert(startAddress, 1); - } else if (reg == (previousReg + 1)) { //next register in block - previousReg = reg; - registerGroups.insert(startAddress, (registerGroups.value(startAddress) + 1)); //update block length - } else { // new block - startAddress = reg; - previousReg = reg; - registerGroups.insert(startAddress, 1); - } - } - - foreach (int startAddress, registerGroups.keys()) { - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, startAddress, registerGroups.value(startAddress)); - if (m_readRequestQueue.isEmpty()) { - modbusReadRequest(request); - } else if (m_readRequestQueue.length() > 100) { - qCWarning(dcUniPi()) << "Neuron extension: Too many pending read requests"; - } else { - m_readRequestQueue.append(request); - } - } - return true; -} - -bool NeuronExtension::getAllAnalogOutputs() -{ - if (!m_modbusInterface) { - qCWarning(dcUniPi()) << "Neuron: Modbus interface not initialized"; - return false; - } - Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) { - getAnalogIO(descriptor); - } - return true; -} - -bool NeuronExtension::getAllAnalogInputs() -{ - if (!m_modbusInterface) { - qCWarning(dcUniPi()) << "Neuron: Modbus interface not initialized"; - return false; - } - Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogInputRegisters.values()) { - getAnalogIO(descriptor); - } - return true; -} - -bool NeuronExtension::getAllDigitalOutputs() -{ - if (!m_modbusInterface) - return false; - - QList requests; - QList registerList = m_modbusDigitalOutputRegisters.values(); - - if (registerList.isEmpty()) { - return true; //device has no digital outputs - } - - std::sort(registerList.begin(), registerList.end()); - int previousReg = registerList.first(); //first register to read and starting point to get the following registers - int startAddress; - - QHash registerGroups; - - foreach (int reg, registerList) { - //qDebug(dcUniPi()) << "Register" << reg << "previous Register" << previousReg; - if (reg == previousReg) { //first register - startAddress = reg; - registerGroups.insert(startAddress, 1); - } else if (reg == (previousReg + 1)) { //next register in block - previousReg = reg; - registerGroups.insert(startAddress, (registerGroups.value(startAddress) + 1)); //update block length - } else { // new block - startAddress = reg; - previousReg = reg; - registerGroups.insert(startAddress, 1); - } - } - - foreach (int startAddress, registerGroups.keys()) { - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, startAddress, registerGroups.value(startAddress)); - if (m_readRequestQueue.isEmpty()) { - modbusReadRequest(request); - } else if (m_readRequestQueue.length() > 100) { - qCWarning(dcUniPi()) << "Neuron extension: Too many pending read requests"; - } else { - m_readRequestQueue.append(request); - } - } - return true; -} - -QUuid NeuronExtension::setAnalogOutput(const QString &circuit, double value) -{ - qDebug(dcUniPi()) << "Neuron Extension: Set analog output" << circuit << value; - if (!m_modbusInterface) - return ""; - - 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(value*400))); // 0 to 4000 = 0 to 10.0 V - } else if (descriptor.count == 2) { - request.data.setValue(0, (static_cast(value) >> 16)); - request.data.setValue(1, (static_cast(value) & 0xffff)); - } - - if (m_writeRequestQueue.isEmpty()) { - modbusWriteRequest(request); - } else if (m_writeRequestQueue.length() > 100) { - return ""; - } else { - m_writeRequestQueue.append(request); - } - return request.id; - } - } - return ""; -} - - -bool NeuronExtension::getAnalogOutput(const QString &circuit) -{ - //qDebug(dcUniPi()) << "Neuron Extension: Get analog output" << circuit; - Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogOutputRegisters.values()) { - if (descriptor.circuit == circuit) { - return getAnalogIO(descriptor); - } - } - return false; -} - - -bool NeuronExtension::getAnalogInput(const QString &circuit) -{ - //qDebug(dcUniPi()) << "Neuron Extension: Get analog input" << circuit; - Q_FOREACH(RegisterDescriptor descriptor, m_modbusAnalogInputRegisters.values()) { - if (descriptor.circuit == circuit) { - return getAnalogIO(descriptor); - } - } - return false; -} - -QUuid NeuronExtension::setUserLED(const QString &circuit, bool value) -{ - int modbusAddress = m_modbusUserLEDRegisters.value(circuit); - //qDebug(dcUniPi()) << "Neuron Extension: Setting user LED" << circuit << modbusAddress << value; - - if (!m_modbusInterface) - return ""; - - Request request; - request.id = QUuid::createUuid(); - - request.data = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, modbusAddress, 1); - request.data.setValue(0, static_cast(value)); - - if (m_writeRequestQueue.isEmpty()) { - modbusWriteRequest(request); - } else if (m_writeRequestQueue.length() > 100) { - return ""; - } else { - m_writeRequestQueue.append(request); - } - - return request.id; -} - - -bool NeuronExtension::getUserLED(const QString &circuit) -{ - int modbusAddress = m_modbusUserLEDRegisters.value(circuit); - //qDebug(dcUniPi()) << "Neuron Extension: Get user LED" << circuit << modbusAddress; - - if (!m_modbusInterface) - return false; - - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, 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); - } - return true; -} - - -void NeuronExtension::onOutputPollingTimer() -{ - getAllDigitalOutputs(); - getAllAnalogOutputs(); -} - -void NeuronExtension::onInputPollingTimer() -{ - getAllDigitalInputs(); - getAllAnalogInputs(); -} diff --git a/unipi/neuronextension.h b/unipi/neuronextension.h index 0867957..709af05 100644 --- a/unipi/neuronextension.h +++ b/unipi/neuronextension.h @@ -54,46 +54,12 @@ public: }; Q_ENUM(ExtensionTypes) - explicit NeuronExtension(ExtensionTypes extensionType, QModbusRtuSerialMaster *modbusInterface, int slaveAddress, QObject *parent = nullptr); + explicit NeuronExtension(ExtensionTypes extensionType, QModbusClient *modbusInterface, int slaveAddress, QObject *parent = nullptr); ~NeuronExtension(); - - bool init(); QString type(); - int slaveAddress(); - void setSlaveAddress(int slaveAddress); - - QUuid setDigitalOutput(const QString &circuit, bool value); - bool getDigitalOutput(const QString &circuit); - bool getDigitalInput(const QString &circuit); - - QUuid setAnalogOutput(const QString &circuit, double value); - bool getAnalogOutput(const QString &circuit); - bool getAnalogInput(const QString &circuit); - - bool getAllDigitalOutputs(); - bool getAllDigitalInputs(); - bool getAllAnalogOutputs(); - bool getAllAnalogInputs(); - - QUuid setUserLED(const QString &circuit, bool value); - bool getUserLED(const QString &circuit); private: - - QModbusRtuSerialMaster *m_modbusInterface = nullptr; - int m_slaveAddress = 0; ExtensionTypes m_extensionType = ExtensionTypes::xS10; - QHash m_previousCircuitValue; - - bool loadModbusMap(); - bool modbusWriteRequest(const Request &request); - bool modbusReadRequest(const QModbusDataUnit &request); - - bool getAnalogIO(const RegisterDescriptor &descriptor); - -private slots: - void onOutputPollingTimer(); - void onInputPollingTimer(); + bool loadModbusMap() override; }; - #endif // NEURONEXTENSION_H