finished neuron refactoring

pull/8/head
Boernsman 2021-02-05 10:59:57 +01:00
parent 571af9c85a
commit 3ff3494053
6 changed files with 557 additions and 1107 deletions

View File

@ -35,12 +35,12 @@
#include <QTextStream>
#include <QStandardPaths>
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<int>(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<int> 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<int, int> 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<int> 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<int, int> 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<int> 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<int, int> 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<uint16_t>(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<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);
}
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<uint16_t>(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();
}

View File

@ -38,7 +38,6 @@
#include <QTimer>
#include <QUuid>
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<int> registers);
bool getHoldingRegisters(QList<int> registers);
bool getCoils(QList<int> registers);
bool getAnalogIO(const RegisterDescriptor &descriptor);
public slots:
void onOutputPollingTimer();
void onInputPollingTimer();
bool loadModbusMap() override;
};
#endif // NEURON_H

View File

@ -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<QString> 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<uint16_t>(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<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);
}
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<uint16_t>(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<int> 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<int, int> 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();
}

View File

@ -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<QString> digitalInputs();
QList<QString> digitalOutputs();
@ -46,6 +49,23 @@ public:
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);
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<Request> m_writeRequestQueue;
QList<QModbusDataUnit> m_readRequestQueue;
virtual bool loadModbusMap() = 0;
RegisterDescriptor registerDescriptorFromStringList(const QStringList &data);
QHash<QString, int> m_modbusDigitalOutputRegisters;
QHash<QString, int> m_modbusDigitalInputRegisters;
@ -83,10 +92,29 @@ protected:
QHash<int, RegisterDescriptor> m_modbusAnalogInputRegisters;
QHash<int, RegisterDescriptor> 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<Request> m_writeRequestQueue;
QList<QModbusDataUnit> m_readRequestQueue;
QHash<QString, uint16_t> 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<int> 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

View File

@ -36,68 +36,19 @@
#include <QModbusDataUnit>
#include <QStandardPaths>
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<int>(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<uint16_t>(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<QModbusDataUnit> requests;
QList<int> 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<int, int> 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<QModbusDataUnit> requests;
QList<int> 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<int, int> 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<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);
}
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<uint16_t>(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();
}

View File

@ -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<QString, uint16_t> 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