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