From defcc7cac2b8e2ef3782b8284d622f87e27ceac1 Mon Sep 17 00:00:00 2001 From: Georgi Tushev Date: Thu, 3 Jan 2019 17:09:07 +0100 Subject: [PATCH] add serialpackage --- BrainATUMtome/BrainATUMtome.pro | 10 ++- BrainATUMtome/mainwindow.cpp | 24 +++++- BrainATUMtome/mainwindow.ui | 5 +- BrainATUMtome/microtome.cpp | 21 ++++- BrainATUMtome/microtome.h | 9 ++ BrainATUMtome/serialpackage.cpp | 133 ++++++++++++++++++++++++++++++ BrainATUMtome/serialpackage.h | 53 ++++++++++++ BrainATUMtome/serialport.cpp | 141 ++++++++++++++++++++++++++++++++ BrainATUMtome/serialport.h | 48 +++++++++++ BrainATUMtome/syringe.cpp | 14 +++- BrainATUMtome/syringe.h | 4 + config/settings.ini | 13 ++- 12 files changed, 463 insertions(+), 12 deletions(-) create mode 100644 BrainATUMtome/serialpackage.cpp create mode 100644 BrainATUMtome/serialpackage.h create mode 100644 BrainATUMtome/serialport.cpp create mode 100644 BrainATUMtome/serialport.h diff --git a/BrainATUMtome/BrainATUMtome.pro b/BrainATUMtome/BrainATUMtome.pro index 3088193..5066ebb 100644 --- a/BrainATUMtome/BrainATUMtome.pro +++ b/BrainATUMtome/BrainATUMtome.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += core gui +QT += core gui serialport greaterThan(QT_MAJOR_VERSION, 4): QT += widgets @@ -30,14 +30,18 @@ SOURCES += \ eventlog.cpp \ epos.cpp \ microtome.cpp \ - syringe.cpp + syringe.cpp \ + serialport.cpp \ + serialpackage.cpp HEADERS += \ mainwindow.h \ eventlog.h \ epos.h \ microtome.h \ - syringe.h + syringe.h \ + serialport.h \ + serialpackage.h FORMS += \ mainwindow.ui diff --git a/BrainATUMtome/mainwindow.cpp b/BrainATUMtome/mainwindow.cpp index a1c8ccf..bebf294 100644 --- a/BrainATUMtome/mainwindow.cpp +++ b/BrainATUMtome/mainwindow.cpp @@ -41,7 +41,7 @@ MainWindow::~MainWindow() void MainWindow::configure_epos() { if (m_settings->value("epos/deviceName").isNull()) { - ui->widget_board->on_log(EventLogType::Warn, "Settings :: failed to parse INI file " + m_settings->fileName()); + ui->widget_board->on_log(EventLogType::Warn, "Settings :: failed to parse Epos settings in configuration file " + m_settings->fileName()); return; } QString portLower = m_settings->value("epos/portLower", "USB0").toString(); @@ -51,10 +51,10 @@ void MainWindow::configure_epos() QString interfaceName = m_settings->value("epos/interfaceName", "USB").toString(); m_driverEpos = new Epos(this); + connect(m_driverEpos, &Epos::log, ui->widget_board, &EventLog::on_log); m_driverEpos->open(EPOS_MOTORID_LOWER, deviceName, protocolStackName, interfaceName, portLower); m_driverEpos->open(EPOS_MOTORID_UPPER, deviceName, protocolStackName, interfaceName, portUpper); - connect(m_driverEpos, &Epos::log, ui->widget_board, &EventLog::on_log); connect(this, &MainWindow::uirequest_epos_velocity, m_driverEpos, &Epos::on_uirequest_velocity); connect(this, &MainWindow::uirequest_epos_tension, m_driverEpos, &Epos::on_uirequest_tension); @@ -91,8 +91,18 @@ void MainWindow::configure_epos() void MainWindow::configure_microtome() { + if (m_settings->value("microtome/deviceName").isNull()) { + ui->widget_board->on_log(EventLogType::Warn, "Settings :: failed to parse microtome settings in configuration file " + m_settings->fileName()); + return; + } + + QString portName = m_settings->value("microtome/port", "COM2").toString(); + int baudRate = m_settings->value("microtome/baudRate", 19200).toInt(); + m_driverMicrotome = new Microtome(this); connect(m_driverMicrotome, &Microtome::log, ui->widget_board, &EventLog::on_log); + m_driverMicrotome->open(portName, baudRate); + connect(this, &MainWindow::uirequest_microtome_cuttingMotor, m_driverMicrotome, &Microtome::on_uirequest_cuttingMotor); connect(this, &MainWindow::uirequest_microtome_cuttingSpeed, m_driverMicrotome, &Microtome::on_uirequest_cuttingSpeed); connect(this, &MainWindow::uirequest_microtome_returnSpeed, m_driverMicrotome, &Microtome::on_uirequest_returnSpeed); @@ -104,8 +114,18 @@ void MainWindow::configure_microtome() void MainWindow::configure_syringe() { + if (m_settings->value("syringe/deviceName").isNull()) { + ui->widget_board->on_log(EventLogType::Warn, "Settings :: failed to parse syringe settings in configuration file " + m_settings->fileName()); + return; + } + + QString portName = m_settings->value("syringe/port", "COM6").toString(); + int baudRate = m_settings->value("syringe/baudRate", 9600).toInt(); + m_driverSyringe = new Syringe(this); connect(m_driverSyringe, &Syringe::log, ui->widget_board, &EventLog::on_log); + m_driverSyringe->open(portName, baudRate); + connect(this, &MainWindow::uirequest_syringe_pump, m_driverSyringe, &Syringe::on_uirequest_pump); connect(this, &MainWindow::uirequest_syringe_auto, m_driverSyringe, &Syringe::on_uirequest_auto); } diff --git a/BrainATUMtome/mainwindow.ui b/BrainATUMtome/mainwindow.ui index a2297b2..fba2225 100644 --- a/BrainATUMtome/mainwindow.ui +++ b/BrainATUMtome/mainwindow.ui @@ -448,7 +448,10 @@ - FRAME + CAMERA + + + Qt::AlignCenter diff --git a/BrainATUMtome/microtome.cpp b/BrainATUMtome/microtome.cpp index 13acfc7..2b121ea 100644 --- a/BrainATUMtome/microtome.cpp +++ b/BrainATUMtome/microtome.cpp @@ -1,11 +1,30 @@ #include "microtome.h" -Microtome::Microtome(QObject *parent) : QObject(parent) +Microtome::Microtome(QObject *parent) : QObject(parent), + m_serialPort(nullptr) +{ + m_serialPackage = new SerialPackage(this); + m_serialPackage->test(); +} + + +Microtome::~Microtome() { } +void Microtome::open(const QString &portName, int baudRate) +{ + m_serialPort = new SerialPort(this); + connect(m_serialPort, &SerialPort::log, + [this](EventLogType type, const QString &message) { + emit log(type, "Microtome :: " + message); + } + ); + m_serialPort->open(portName, baudRate); +} + void Microtome::on_uirequest_cuttingMotor(bool state) { QString textState = (state) ? "on" : "off"; diff --git a/BrainATUMtome/microtome.h b/BrainATUMtome/microtome.h index c4d7d2f..7ca8bab 100644 --- a/BrainATUMtome/microtome.h +++ b/BrainATUMtome/microtome.h @@ -4,6 +4,8 @@ #include #include "eventlog.h" +#include "serialport.h" +#include "serialpackage.h" class Microtome : public QObject { @@ -11,6 +13,13 @@ class Microtome : public QObject public: explicit Microtome(QObject *parent = nullptr); + ~Microtome(); + + void open(const QString &portName, int baudRate); + +private: + SerialPort *m_serialPort; + SerialPackage *m_serialPackage; public slots: void on_uirequest_cuttingMotor(bool state); diff --git a/BrainATUMtome/serialpackage.cpp b/BrainATUMtome/serialpackage.cpp new file mode 100644 index 0000000..571acee --- /dev/null +++ b/BrainATUMtome/serialpackage.cpp @@ -0,0 +1,133 @@ +#include "serialpackage.h" + +SerialPackage::SerialPackage(QObject *parent) : QObject(parent) +{ + +} + +SerialPackage::~SerialPackage() +{ + +} + + +void SerialPackage::test() +{ + setAddress(0xCA); + setCommand(0xFC); + + qDebug() << m_buffer; + qDebug() << address(); + qDebug() << command(); + qDebug() << checksum(); +} + + +quint8 SerialPackage::address() +{ + return unpack(0); +} + +quint8 SerialPackage::command() +{ + return unpack(2); +} + +quint8 SerialPackage::checksum() +{ + return unpack(m_buffer.size() - 2); +} + + + +void SerialPackage::setAddress(quint8 value) +{ + pack(value, 0); +} + + +void SerialPackage::setCommand(quint8 value) +{ + pack(value, 2); +} + + + +void SerialPackage::unpack(quint8 &value, int index) +{ + value = static_cast(QString("%1%2").arg(m_buffer.mid(index, 1), m_buffer.mid(index + 1, 1)).toUInt(nullptr, 16)); +} + + + + +void SerialPackage::pack(quint8 value, int index) +{ + QString textValue = QString("%1").arg(value, 2, 16, QChar('0')).toUpper(); + m_buffer.replace(index, 2, textValue); +} + + +void SerialPackage::pack(quint16 value, int index) +{ + QString textValue = QString("%1").arg(value, 4, 16, QChar('0')).toUpper(); + m_buffer.replace(index, 4, textValue); +} + + +void SerialPackage::pack(quint32 value, int index) +{ + QString textValue = QString("%1").arg(value, 8, 16, QChar('0')).toUpper(); + m_buffer.replace(index, 8, textValue); +} + + + + +bool SerialPackage::parse(const QByteArray &package) +{ + // clean message + QString message(package); + + message = message.simplified(); + + if (message.at(0) == '!') + message.remove(0, 1); + + + + // check package size + if ((message.size() < SIZE_PACKAGE_MINIMUM) || (message.size() >= SIZE_PACKAGE_BUFFER)) + { + emit log(EventLogType::Warn, + "SerialPackage:: package size " + + QString::number(message.size()) + + " is outside package range [ " + + QString::number(SIZE_PACKAGE_MINIMUM) + + ", " + + QString::number(SIZE_PACKAGE_BUFFER) + + ") bytes."); + return false; + } + + // check for even size + if ((message.size() % 2) != 0) + { + emit log(EventLogType::Warn, + "SerialPackage:: package size " + + QString::number(message.size()) + + " is not even. Package is truncated."); + return false; + } + + // get received checksum + quint8 checksumReceived = static_cast(QString("%1%2").arg(message.mid(message.size() - 2, 1), m_buffer.mid(message.size() - 1, 1)).toUInt(nullptr, 16)); + message.remove(message.size() - 2, 2); + m_buffer = message; + + return (checksumReceived == checksum()); +} + + + + diff --git a/BrainATUMtome/serialpackage.h b/BrainATUMtome/serialpackage.h new file mode 100644 index 0000000..8695338 --- /dev/null +++ b/BrainATUMtome/serialpackage.h @@ -0,0 +1,53 @@ +#ifndef SERIALPACKAGE_H +#define SERIALPACKAGE_H + +#include +#include + +#include "eventlog.h" + +class SerialPackage : public QObject +{ + Q_OBJECT + +public: + explicit SerialPackage(QObject *parent = nullptr); + ~SerialPackage(); + + bool parse(const QByteArray &package); + + void test(); + quint8 address(); + quint8 command(); + quint8 checksum(); + + void setAddress(quint8 value); + void setCommand(quint8 value); + + void push(quint8 value); + void push(quint16 value); + void push(quint32 value); + + void pop(quint8 &value); + void pop(quint16 &value); + void pop(quint32 &value); + +private: + QString m_buffer; + + void unpack(quint8 &value, int index); + void unpack(quint16 &value, int index); + void unpack(quint32 &value, int index); + void pack(quint8 value, int index); + void pack(quint16 value, int index); + void pack(quint32 value, int index); + + static const qint8 SIZE_PACKAGE_MINIMUM = 6; + static const qint8 SIZE_PACKAGE_BUFFER = 64; + +signals: + void log(EventLogType type, const QString &message); + +}; + +#endif // SERIALPACKAGE_H diff --git a/BrainATUMtome/serialport.cpp b/BrainATUMtome/serialport.cpp new file mode 100644 index 0000000..502eabf --- /dev/null +++ b/BrainATUMtome/serialport.cpp @@ -0,0 +1,141 @@ +#include "serialport.h" + +SerialPort::SerialPort(QObject *parent) : QObject(parent), + m_serialPort(new QSerialPort(this)), + m_dispatcher(new QStateMachine(this)), + m_timerRespond(new QTimer(this)), + m_bufferRead(new QByteArray()), + m_queue(new QQueue()) +{ + // configure dispatcher + QState *stateListener = new QState(m_dispatcher); + QState *stateSender = new QState(m_dispatcher); + QState *stateReceiver = new QState(m_dispatcher); + stateListener->addTransition(this, &SerialPort::schedule, stateSender); + stateSender->addTransition(this, &SerialPort::sent, stateReceiver); + stateSender->addTransition(this, &SerialPort::listen, stateListener); + stateReceiver->addTransition(this, &SerialPort::readyRespond, stateSender); + m_dispatcher->setInitialState(stateListener); + m_dispatcher->start(); + + // configure callbacks + connect(m_serialPort, &QSerialPort::readyRead, this, &SerialPort::on_bufferRead); + connect(stateSender, &QState::entered, this, &SerialPort::on_bufferWrite); + connect(m_timerRespond, &QTimer::timeout, [this](){emit log(EventLogType::Warn, "SerialPort :: respond timeout.");}); + + + connect(stateListener, &QState::entered, [this](){emit log(EventLogType::Notify, "SerialPort ::Dispatcher::Listener");}); + connect(stateSender, &QState::entered, [this](){emit log(EventLogType::Notify, "SerialPort ::Dispatcher::Sender");}); + connect(stateReceiver, &QState::entered, [this](){emit log(EventLogType::Notify, "SerialPort ::Dispatcher::Receiver");}); + + +} + +SerialPort::~SerialPort() +{ + if (m_dispatcher->isRunning()) + m_dispatcher->stop(); + + if (m_timerRespond->isActive()) + m_timerRespond->isActive(); + + if (m_serialPort->isOpen()) + m_serialPort->close(); + + if (m_bufferRead != nullptr) + delete m_bufferRead; + + if (m_queue != nullptr) + delete m_queue; +} + + +void SerialPort::open(const QString &portName, qint32 baudRate) +{ + // configure port + m_serialPort->setPortName(portName); + m_serialPort->setBaudRate(baudRate); + m_serialPort->setDataBits(QSerialPort::Data8); + m_serialPort->setParity(QSerialPort::NoParity); + m_serialPort->setFlowControl(QSerialPort::NoFlowControl); + m_serialPort->setStopBits(QSerialPort::OneStop); + m_serialPort->open(QIODevice::ReadWrite); + if (!m_serialPort->isOpen()) + { + emit log(EventLogType::Warn, "SerialPort :: failed to open serial port " + m_serialPort->portName()); + return; + } + + emit log(EventLogType::Notify, + "SerialPort :: device opened @ port=" + m_serialPort->portName() + ", baudRate=" + QString::number(m_serialPort->baudRate())); + + // configure respond timer + m_timerRespond->setSingleShot(true); + m_timerRespond->setInterval(TIMER_INTERVAL_RESPOND); +} + + +void SerialPort::on_scheduleMessage(const QByteArray &message) +{ + m_queue->enqueue(message); + emit schedule(); +} + + +void SerialPort::on_bufferRead() +{ + // add new bytes to buffer + m_bufferRead->append(m_serialPort->readAll()); + + + + // check for message terminator + if (!(m_bufferRead->contains('\r') || + m_bufferRead->contains('\n') || + m_bufferRead->contains(0x03))) + return; + + // extract respond message + m_bufferRead->replace('\r', 0x03); + m_bufferRead->replace('\n', 0x03); + int packageEnd = m_bufferRead->indexOf(0x03); + QByteArray packageBuffer = m_bufferRead->mid(0, packageEnd); + m_bufferRead->remove(0, packageEnd + 1); + m_timerRespond->stop(); + + // emit respond message + emit readyRespond(packageBuffer); +} + + +void SerialPort::on_bufferWrite() +{ + // check queue + if (m_queue->isEmpty()) + { + emit listen(); + return; + } + + // send first command in queue + qint64 bytesWritten = -1; + QByteArray packageBuffer = m_queue->dequeue(); + if (m_serialPort->isOpen()) + bytesWritten = m_serialPort->write(packageBuffer); + + // check transfer + if (bytesWritten == -1) + { + emit log(EventLogType::Warn, "SerialPort :: failed to write data to serial port."); + } + else if (bytesWritten != packageBuffer.size()) + { + emit log(EventLogType::Warn, "SerialPort :: serial port sent a truncated message."); + } + else + { + m_timerRespond->start(); + emit log(EventLogType::Notify, "SerialPort :: message " + QString::fromUtf8(packageBuffer)); + emit sent(); + } +} diff --git a/BrainATUMtome/serialport.h b/BrainATUMtome/serialport.h new file mode 100644 index 0000000..adba5e6 --- /dev/null +++ b/BrainATUMtome/serialport.h @@ -0,0 +1,48 @@ +#ifndef SERIALPORT_H +#define SERIALPORT_H + +#include +#include +#include +#include +#include + +#include "eventlog.h" + +class SerialPort : public QObject +{ + Q_OBJECT + +public: + explicit SerialPort(QObject *parent = nullptr); + ~SerialPort(); + + void open(const QString &portName, qint32 baudRate); + +private: + static const qint16 TIMER_INTERVAL_RESPOND = 2000; + + QSerialPort *m_serialPort; + QStateMachine *m_dispatcher; + QTimer *m_timerRespond; + QByteArray *m_bufferRead; + QQueue *m_queue; + +public slots: + void on_scheduleMessage(const QByteArray &message); + +private slots: + void on_bufferRead(); + void on_bufferWrite(); + +signals: + void log(EventLogType type, const QString &message); + + void schedule(); + void sent(); + void listen(); + void readyRespond(const QByteArray &respond); + +}; + +#endif // SERIALPORT_H diff --git a/BrainATUMtome/syringe.cpp b/BrainATUMtome/syringe.cpp index ec0343d..e6408ab 100644 --- a/BrainATUMtome/syringe.cpp +++ b/BrainATUMtome/syringe.cpp @@ -1,12 +1,24 @@ #include "syringe.h" Syringe::Syringe(QObject *parent) : QObject(parent), - m_auto(false) + m_auto(false), + m_serialPort(nullptr) { } +void Syringe::open(const QString &portName, int baudRate) +{ + m_serialPort = new SerialPort(this); + connect(m_serialPort, &SerialPort::log, + [this](EventLogType type, const QString &message) { + emit log(type, "Syringe :: " + message); + } + ); + m_serialPort->open(portName, baudRate); +} + void Syringe::on_uirequest_pump() { emit log(EventLogType::Notify, "Syringe :: pump"); diff --git a/BrainATUMtome/syringe.h b/BrainATUMtome/syringe.h index 5707379..d147131 100644 --- a/BrainATUMtome/syringe.h +++ b/BrainATUMtome/syringe.h @@ -4,6 +4,7 @@ #include #include "eventlog.h" +#include "serialport.h" class Syringe : public QObject { @@ -12,8 +13,11 @@ class Syringe : public QObject public: explicit Syringe(QObject *parent = nullptr); + void open(const QString &portName, int baudRate); + private: bool m_auto; + SerialPort *m_serialPort; signals: void log(EventLogType type, const QString &message); diff --git a/config/settings.ini b/config/settings.ini index f3fb73a..5574d8c 100644 --- a/config/settings.ini +++ b/config/settings.ini @@ -1,15 +1,20 @@ [microtome] -port=COM2 -baudRate=19200 +#deviceName=Leica +#port=COM2 +#baudRate=19200 +deviceName=Arduino +port=/dev/cu.usbmodem14311 +baudRate=9600 [epos] -portLower=USB0 -portUpper=USB1 deviceName=EPOS2 protocolStackName=MAXON SERIAL V2 interfaceName=USB +portLower=USB0 +portUpper=USB1 [syringe] +deviceName=Syringe port=COM6 baudRate=9600