Newer
Older
/*=====================================================================
======================================================================*/
/**
* @file
* @brief Cross-platform support for serial ports
*
* @author Lorenz Meier <mavteam@student.ethz.ch>
*
*/
#include <QTimer>
#include <QDebug>
#include <QMutexLocker>
#include "SerialLink.h"
#include "LinkManager.h"
//#define USE_QEXTSERIAL // this allows us to revert to old serial library during transition
SerialLink::SerialLink(QString portname, int baudRate, bool hardwareFlowControl, bool parity,
int dataBits, int stopBits) :
{
// Setup settings
this->porthandle = portname.trimmed();
#ifdef _WIN32
// Port names above 20 need the network path format - if the port name is not already in this format
// catch this special case
if (this->porthandle.size() > 0 && !this->porthandle.startsWith("\\")) {
// Append \\.\ before the port handle. Additional backslashes are used for escaping.
this->porthandle = "\\\\.\\" + this->porthandle;
}
#endif
// Set unique ID and add link to the list of links
this->id = getNextLinkId();
setBaudRate(baudRate);
if (hardwareFlowControl)
{
portSettings.setFlowControl(QPortSettings::FLOW_HARDWARE);
}
else
{
portSettings.setFlowControl(QPortSettings::FLOW_OFF);
}
if (parity)
{
portSettings.setParity(QPortSettings::PAR_EVEN);
}
else
{
portSettings.setParity(QPortSettings::PAR_NONE);
}
setDataBits(dataBits);
setStopBits(stopBits);
name = tr("Serial Link ") + QString::number(getId());
else
{
name = portname.trimmed();
void SerialLink::loadSettings()
{
// Load defaults from settings
QSettings settings(QGC::COMPANYNAME, QGC::APPNAME);
settings.sync();
if (settings.contains("SERIALLINK_COMM_PORT")) {
if (porthandle == "") setPortName(settings.value("SERIALLINK_COMM_PORT").toString());
setBaudRateType(settings.value("SERIALLINK_COMM_BAUD").toInt());
setParityType(settings.value("SERIALLINK_COMM_PARITY").toInt());
setStopBits(settings.value("SERIALLINK_COMM_STOPBITS").toInt());
setDataBits(settings.value("SERIALLINK_COMM_DATABITS").toInt());
setFlowType(settings.value("SERIALLINK_COMM_FLOW_CONTROL").toInt());
}
}
void SerialLink::writeSettings()
{
// Store settings
QSettings settings(QGC::COMPANYNAME, QGC::APPNAME);
settings.setValue("SERIALLINK_COMM_PORT", this->porthandle);
settings.setValue("SERIALLINK_COMM_BAUD", getBaudRateType());
settings.setValue("SERIALLINK_COMM_PARITY", getParityType());
settings.setValue("SERIALLINK_COMM_STOPBITS", getStopBits());
settings.setValue("SERIALLINK_COMM_DATABITS", getDataBits());
settings.setValue("SERIALLINK_COMM_FLOW_CONTROL", getFlowType());
settings.sync();
}
void SerialLink::run()
{
// Initialize the connection
hardwareConnect();
// Qt way to make clear what a while(1) loop does
// Check if new bytes have arrived, if yes, emit the notification signal
checkForBytes();
/* Serial data isn't arriving that fast normally, this saves the thread
* from consuming too much processing time
*/
MG::SLEEP::msleep(SerialLink::poll_interval);
}
}
void SerialLink::checkForBytes()
{
if(port && port->isOpen() && port->isWritable()) {
dataMutex.lock();
qint64 available = port->bytesAvailable();
dataMutex.unlock();
readBytes();
void SerialLink::writeBytes(const char* data, qint64 size)
{
// qDebug() << "Serial link " << this->getName() << "transmitted" << b << "bytes:";
// Increase write counter
bitsSentTotal += size * 8;
// int i;
// for (i=0; i<size; i++)
// {
// unsigned char v =data[i];
// qDebug("%02x ", v);
// }
// qDebug("\n");
disconnect();
// Error occured
emit communicationError(this->getName(), tr("Could not send data - link %1 is disconnected!").arg(this->getName()));
}
}
/**
* @brief Read a number of bytes from the interface.
*
* @param data Pointer to the data byte array to write the bytes to
* @param maxLength The maximum number of bytes to write
**/
void SerialLink::readBytes()
{
const qint64 maxLength = 2048;
char data[maxLength];
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/* Read as much data in buffer as possible without overflow */
if(maxLength < numBytes) numBytes = maxLength;
port->read(data, numBytes);
QByteArray b(data, numBytes);
emit bytesReceived(this, b);
//qDebug() << "SerialLink::readBytes()" << std::hex << data;
// int i;
// for (i=0; i<numBytes; i++){
// unsigned int v=data[i];
//
// fprintf(stderr,"%02x ", v);
// }
// fprintf(stderr,"\n");
bitsReceivedTotal += numBytes * 8;
}
}
dataMutex.unlock();
}
/**
* @brief Get the number of bytes to read.
*
* @return The number of bytes to read
**/
qint64 SerialLink::bytesAvailable()
{
return port->bytesAvailable();
return 0;
}
}
/**
* @brief Disconnect the connection.
*
* @return True if connection has been disconnected, false if connection couldn't be disconnected.
**/
bool SerialLink::disconnect()
{
//#if !defined _WIN32 || !defined _WIN64
/* Block the thread until it returns from run() */
//#endif
// dataMutex.lock();
port->flushInBuffer();
port->flushOutBuffer();
port->close();
delete port;
port = NULL;
// dataMutex.unlock();
if(this->isRunning()) this->terminate(); //stop running the thread, restart it upon connect
bool closed = true;
//port->isOpen();
emit disconnected();
emit connected(false);
// No port, so we're disconnected
return true;
}
}
/**
* @brief Connect the connection.
*
* @return True if connection has been established, false if connection couldn't be established.
**/
bool SerialLink::connect()
{
if (this->isRunning()) this->disconnect();
this->start(LowPriority);
}
/**
* @brief This function is called indirectly by the connect() call.
*
* The connect() function starts the thread and indirectly calls this method.
*
* @return True if the connection could be established, false otherwise
* @see connect() For the right function to establish the connection.
**/
bool SerialLink::hardwareConnect()
{
port->close();
delete port;
}
port = new QSerialPort(porthandle, portSettings);
QObject::connect(port,SIGNAL(aboutToClose()),this,SIGNAL(disconnected()));
port->setCommTimeouts(QSerialPort::CtScheme_NonBlockingRead);
qDebug() << "CONNECTING LINK: " << __FILE__ << __LINE__ << "with settings" << port->portName() << getBaudRate() << getDataBits() << getParityType() << getStopBits();
writeSettings();
/**
* @brief Check if connection is active.
*
* @return True if link is connected, false otherwise.
**/
bool SerialLink::isConnected()
{
}
int SerialLink::getId()
{
return id;
}
QString SerialLink::getName()
{
return name;
}
void SerialLink::setName(QString name)
{
this->name = name;
emit nameChanged(this->name);
}
qint64 SerialLink::getNominalDataRate()
{
switch (portSettings.baudRate()) {
case QPortSettings::BAUDR_50:
case QPortSettings::BAUDR_75:
case QPortSettings::BAUDR_110:
case QPortSettings::BAUDR_134:
case QPortSettings::BAUDR_150:
case QPortSettings::BAUDR_200:
case QPortSettings::BAUDR_300:
case QPortSettings::BAUDR_600:
case QPortSettings::BAUDR_1200:
case QPortSettings::BAUDR_1800:
case QPortSettings::BAUDR_2400:
case QPortSettings::BAUDR_4800:
case QPortSettings::BAUDR_9600:
#ifdef Q_OS_WIN
case QPortSettings::BAUDR_14400:
#endif
case QPortSettings::BAUDR_19200:
case QPortSettings::BAUDR_38400:
#ifdef Q_OS_WIN
case QPortSettings::BAUDR_56000:
#endif
case QPortSettings::BAUDR_57600:
#ifdef Q_OS_WIN
case QPortSettings::BAUDR_76800:
#endif
case QPortSettings::BAUDR_115200:
#ifdef Q_OS_WIN
// Windows-specific high-end baudrates
case QPortSettings::BAUDR_128000:
case QPortSettings::BAUDR_256000:
case QPortSettings::BAUDR_230400:
dataRate = 230400;
case QPortSettings::BAUDR_460800:
dataRate = 460800;
#endif
// All-OS high-speed
case QPortSettings::BAUDR_921600:
dataRate = 921600;
case QPortSettings::BAUDR_UNKNOWN:
default:
// Do nothing
break;
qint64 SerialLink::getTotalUpstream()
{
statisticsMutex.lock();
return bitsSentTotal / ((MG::TIME::getGroundTimeNow() - connectionStartTime) / 1000);
statisticsMutex.unlock();
}
qint64 SerialLink::getCurrentUpstream()
{
qint64 SerialLink::getMaxUpstream()
{
qint64 SerialLink::getBitsSent()
{
qint64 SerialLink::getBitsReceived()
{
qint64 SerialLink::getTotalDownstream()
{
statisticsMutex.lock();
return bitsReceivedTotal / ((MG::TIME::getGroundTimeNow() - connectionStartTime) / 1000);
statisticsMutex.unlock();
}
qint64 SerialLink::getCurrentDownstream()
{
qint64 SerialLink::getMaxDownstream()
{
bool SerialLink::isFullDuplex()
{
/* Serial connections are always half duplex */
return false;
}
int SerialLink::getLinkQuality()
{
/* This feature is not supported with this interface */
return -1;
}
QString SerialLink::getPortName()
{
int SerialLink::getBaudRate()
{
int SerialLink::getBaudRateType()
{
return portSettings.baudRate();
int SerialLink::getFlowType()
{
return portSettings.flowControl();
int SerialLink::getParityType()
{
return portSettings.parity();
int SerialLink::getDataBitsType()
{
return portSettings.dataBits();
int SerialLink::getStopBitsType()
{
return portSettings.stopBits();
int SerialLink::getDataBits()
{
int ret = -1;
switch (portSettings.dataBits()) {
case QPortSettings::DB_5:
ret = 5;
break;
case QPortSettings::DB_6:
ret = 6;
break;
case QPortSettings::DB_7:
ret = 7;
break;
case QPortSettings::DB_8:
ret = 8;
break;
default:
break;
}
return ret;
}
int SerialLink::getStopBits()
{
int ret = -1;
switch (portSettings.stopBits()) {
case QPortSettings::STOP_1:
ret = 1;
break;
case QPortSettings::STOP_2:
ret = 2;
break;
default:
ret = -1;
break;
}
return ret;
}
if(portName.trimmed().length() > 0) {
if (isConnected()) reconnect = true;
disconnect();
this->porthandle = portName.trimmed();
setName(tr("serial port ") + portName.trimmed());
#ifdef _WIN32
// Port names above 20 need the network path format - if the port name is not already in this format
// catch this special case
if (!this->porthandle.startsWith("\\")) {
// Append \\.\ before the port handle. Additional backslashes are used for escaping.
this->porthandle = "\\\\.\\" + this->porthandle;
}
#endif
return false;
}
}
bool SerialLink::setBaudRateType(int rateIndex)
{
bool reconnect = false;
bool accepted = true; // This is changed if none of the data rates matches
if(isConnected()) reconnect = true;
disconnect();
if (rateIndex >= (int)QPortSettings::BAUDR_50 && rateIndex <= (int)QPortSettings::BAUDR_921600)
{
portSettings.setBaudRate((QPortSettings::BaudRate)rateIndex);
}
if(reconnect) connect();
return accepted;
}
bool SerialLink::setBaudRateString(const QString& rate)
{
bool ok;
int intrate = rate.toInt(&ok);
if (!ok) return false;
return setBaudRate(intrate);
}
qDebug() << "BAUD RATE:" << rate;
bool reconnect = false;
bool accepted = true; // This is changed if none of the data rates matches
portSettings.setBaudRate(QPortSettings::BAUDR_50);
portSettings.setBaudRate(QPortSettings::BAUDR_75);
portSettings.setBaudRate(QPortSettings::BAUDR_110);
portSettings.setBaudRate(QPortSettings::BAUDR_134);
portSettings.setBaudRate(QPortSettings::BAUDR_150);
portSettings.setBaudRate(QPortSettings::BAUDR_200);
portSettings.setBaudRate(QPortSettings::BAUDR_300);
portSettings.setBaudRate(QPortSettings::BAUDR_600);
portSettings.setBaudRate(QPortSettings::BAUDR_1200);
portSettings.setBaudRate(QPortSettings::BAUDR_1800);
portSettings.setBaudRate(QPortSettings::BAUDR_2400);
portSettings.setBaudRate(QPortSettings::BAUDR_4800);
portSettings.setBaudRate(QPortSettings::BAUDR_9600);
portSettings.setBaudRate(QPortSettings::BAUDR_14400);
portSettings.setBaudRate(QPortSettings::BAUDR_19200);
portSettings.setBaudRate(QPortSettings::BAUDR_38400);
portSettings.setBaudRate(QPortSettings::BAUDR_56000);
portSettings.setBaudRate(QPortSettings::BAUDR_57600);
portSettings.setBaudRate(QPortSettings::BAUDR_76800);
portSettings.setBaudRate(QPortSettings::BAUDR_115200);
portSettings.setBaudRate(QPortSettings::BAUDR_128000);
case 230400:
portSettings.setBaudRate(QPortSettings::BAUDR_230400);
break;
portSettings.setBaudRate(QPortSettings::BAUDR_256000);
case 460800:
portSettings.setBaudRate(QPortSettings::BAUDR_460800);
break;
case 921600:
portSettings.setBaudRate(QPortSettings::BAUDR_921600);
break;
default:
// If none of the above cases matches, there must be an error
accepted = false;
break;
}
if(reconnect) connect();
return accepted;
bool SerialLink::setFlowType(int flow)
{
if(isConnected()) reconnect = true;
disconnect();
case (int)QPortSettings::FLOW_OFF:
portSettings.setFlowControl(QPortSettings::FLOW_OFF);
case (int)QPortSettings::FLOW_HARDWARE:
portSettings.setFlowControl(QPortSettings::FLOW_HARDWARE);
case (int)QPortSettings::FLOW_XONXOFF:
portSettings.setFlowControl(QPortSettings::FLOW_XONXOFF);
break;
default:
// If none of the above cases matches, there must be an error
accepted = false;
break;
}
bool SerialLink::setParityType(int parity)
{
if (isConnected()) reconnect = true;
disconnect();
case (int)QPortSettings::PAR_NONE:
portSettings.setParity(QPortSettings::PAR_NONE);
case (int)QPortSettings::PAR_ODD:
portSettings.setParity(QPortSettings::PAR_ODD);
case (int)QPortSettings::PAR_EVEN:
portSettings.setParity(QPortSettings::PAR_EVEN);
case (int)QPortSettings::PAR_SPACE:
portSettings.setParity(QPortSettings::PAR_SPACE);
break;
default:
// If none of the above cases matches, there must be an error
accepted = false;
break;
}
if (reconnect) connect();
bool SerialLink::setDataBits(int dataBits)
{
qDebug() << "Setting" << dataBits << "data bits";
bool reconnect = false;
if (isConnected()) reconnect = true;
portSettings.setDataBits(QPortSettings::DB_5);
portSettings.setDataBits(QPortSettings::DB_6);
portSettings.setDataBits(QPortSettings::DB_7);
portSettings.setDataBits(QPortSettings::DB_8);
break;
default:
// If none of the above cases matches, there must be an error
accepted = false;
break;
}
if(reconnect) connect();
bool SerialLink::setStopBits(int stopBits)
{
if(isConnected()) reconnect = true;
disconnect();
portSettings.setStopBits(QPortSettings::STOP_1);
portSettings.setStopBits(QPortSettings::STOP_2);
break;
default:
// If none of the above cases matches, there must be an error
accepted = false;
break;
}
if(reconnect) connect();
return accepted;
}
bool SerialLink::setDataBitsType(int dataBits)
{
bool reconnect = false;
bool accepted = false;
if (isConnected()) reconnect = true;
disconnect();
if (dataBits >= (int)QPortSettings::DB_5 && dataBits <= (int)QPortSettings::DB_8) {
portSettings.setDataBits((QPortSettings::DataBits) dataBits);
if(reconnect) connect();
accepted = true;
}
return accepted;
}
bool SerialLink::setStopBitsType(int stopBits)
{
bool reconnect = false;
bool accepted = false;
if(isConnected()) reconnect = true;
disconnect();
if (stopBits >= (int)QPortSettings::STOP_1 && stopBits <= (int)QPortSettings::STOP_2) {
portSettings.setStopBits((QPortSettings::StopBits) stopBits);
if(reconnect) connect();