Program Listing for File devicecontroller.cpp

Return to documentation for file (src/controller/devicecontroller.cpp)

/*
 * SerialDevice - Utility class for managing devices connected via USB/Bluetooth.
 */

#include <QRegularExpression>
#include <QSerialPort>
#include <QSettings>
#include <QTcpSocket>
#include "dialog/preferencesdialog.h"
#include "devicecontroller.h"
#include "widget/maestrocontrolwidget.h"

namespace PixelMaestroStudio {
    DeviceController::DeviceController(const QString& port_name) {
        this->port_name_ = port_name;

        /*
         * Check whether the port name is an IP address.
         * If so, initialize a TCP socket.
         * Otherwise, assume a serial device.
         */
        QRegularExpression exp("^(?:[0-9]{1,3}.){3}[0-9]{1,3}");
        bool is_network_device = exp.match(port_name).hasMatch();
        if (is_network_device) {
            this->device_ = QSharedPointer<QTcpSocket>(new QTcpSocket());
            this->device_type_ = DeviceType::TCP;
        }
        else {
            this->device_ = QSharedPointer<QSerialPort>(new QSerialPort());
            this->device_type_ = DeviceType::Serial;
        }

        // Look up the device in settings
        QSettings settings;
        int num_devices = settings.beginReadArray(PreferencesDialog::devices);
        for (int device = 0; device < num_devices; device++) {
            settings.setArrayIndex(device);
            QString comp_name = settings.value(PreferencesDialog::device_port).toString();
            if (port_name == comp_name) {
                set_real_time_update(settings.value(PreferencesDialog::device_real_time_refresh).toBool());
                set_autoconnect(settings.value(PreferencesDialog::device_autoconnect).toBool());

                // Load Section Map model (if it exists)
                int num_maps = settings.beginReadArray(PreferencesDialog::section_map);
                if (num_maps > 0) {
                    section_map_model = new SectionMapModel();
                    for (int row = 0; row < num_maps; row++) {
                        settings.setArrayIndex(row);
                        section_map_model->add_section();

                        QString remote_section = settings.value(PreferencesDialog::section_map_remote).toString();
                        section_map_model->item(row, 1)->setText(remote_section);
                    }
                }

                settings.endArray();
                break;
            }
        }
        settings.endArray();
    }

    bool DeviceController::connect() {
        if (device_type_ == DeviceType::Serial) {
            QSerialPort* serial_device = dynamic_cast<QSerialPort*>(device_.data());

            if (!serial_device) return false;

            serial_device->setPortName(port_name_);
            serial_device->setBaudRate(baud_rate_);

            // Set comm settings
            serial_device->setFlowControl(QSerialPort::FlowControl::NoFlowControl);
            serial_device->setParity(QSerialPort::Parity::NoParity);
            serial_device->setDataBits(QSerialPort::DataBits::Data8);
            serial_device->setStopBits(QSerialPort::StopBits::OneStop);

            return (serial_device->open(QIODevice::WriteOnly));
        }
        else if (device_type_ == DeviceType::TCP) {
            // Extract the IP address and port number
            QRegularExpression address_re("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}");
            QRegularExpression port_re("[^:][0-9]+$");

            QString address = address_re.match(port_name_).captured(0);
            QString port = port_re.match(port_name_).captured(0);

            // If no port number is found, use the default
            uint16_t port_num = static_cast<uint16_t>(port.toUInt());
            if (port_num == 0) {
                QSettings settings;
                port_num = PORT_NUM;
            }

            QTcpSocket* tcp_device = dynamic_cast<QTcpSocket*>(device_.data());

            if (!tcp_device) return false;

            tcp_device->connectToHost(address, port_num);

            // Wait for a connection
            // TODO: Make non-blocking
            return tcp_device->waitForConnected(TIMEOUT);
        }

        return false;
    }

    bool DeviceController::disconnect() {
        if (device_type_ == DeviceType::Serial && device_) {
            bool flushed = dynamic_cast<QSerialPort*>(device_.data())->flush();
            device_->close();
            return flushed;
        }
        else if (device_type_ == DeviceType::TCP && device_) {
            QTcpSocket* tcp_device = dynamic_cast<QTcpSocket*>(device_.data());
            tcp_device->flush();
            tcp_device->disconnectFromHost();
            return true;
        }

        return false;
    }

    bool DeviceController::get_autoconnect() const {
        return autoconnect_;
    }

    QIODevice* DeviceController::get_device() const {
        return device_.data();
    }

    bool DeviceController::get_open() const {
        if (device_) {
            switch (device_type_) {
                case DeviceType::Serial:
                    return dynamic_cast<QSerialPort*>(device_.data())->isOpen();
                case DeviceType::TCP:
                    return dynamic_cast<QTcpSocket*>(device_.data())->state() == QAbstractSocket::ConnectedState;
            }
        }

        return false;
    }

    QString DeviceController::get_port_name() const {
        return port_name_;
    }

    bool DeviceController::get_real_time_refresh_enabled() const {
        return real_time_updates_;
    }

    void DeviceController::flush() {
        switch (device_type_) {
            case DeviceType::Serial:
                dynamic_cast<QSerialPort*>(device_.data())->flush();
                break;
            case DeviceType::TCP:
                dynamic_cast<QTcpSocket*>(device_.data())->flush();
                break;
        }
    }

    void DeviceController::set_autoconnect(bool autoconnect) {
        this->autoconnect_ = autoconnect;
    }

    void DeviceController::set_port_name(const QString &port_name) {
        this->port_name_ = port_name;
    }

    void DeviceController::set_real_time_update(bool enabled) {
        this->real_time_updates_ = enabled;
    }
}