Newer
Older
/*=====================================================================
======================================================================*/
/**
* @file
* @brief Cross-platform support for serial ports
*
* @author Lorenz Meier <mavteam@student.ethz.ch>
*
*/
#include <QTimer>
#include <QDebug>
#include <qserialport.h>
#include <qserialportinfo.h>
SerialLink::SerialLink(QString portname, int baudRate, bool hardwareFlowControl, bool parity,
int dataBits, int stopBits) :
Michael Carpenter
committed
m_stopp(false),
Michael Carpenter
committed
m_reqReset(false)
qDebug() << "create SerialLink " << portname << baudRate << hardwareFlowControl
<< parity << dataBits << stopBits;
if (m_portName == "" && getCurrentPorts().size() > 0)
m_portName = m_ports.first().trimmed();
qDebug() << "m_portName " << m_portName;
if (hardwareFlowControl)
{
Michael Carpenter
committed
void SerialLink::requestReset()
{
QMutexLocker locker(&this->m_stoppMutex);
m_reqReset = true;
}
QList<QString> SerialLink::getCurrentPorts()
QList<QSerialPortInfo> portList = QSerialPortInfo::availablePorts();
if( portList.count() == 0){
qDebug() << "No Ports Found" << m_ports;
}
foreach (const QSerialPortInfo &info, portList)
// qDebug() << "PortName : " << info.portName()
// << "Description : " << info.description();
// qDebug() << "Manufacturer: " << info.manufacturer();
m_ports.append(info.portName());
void SerialLink::loadSettings()
{
// Load defaults from settings
QSettings settings(QGC::COMPANYNAME, QGC::APPNAME);
settings.sync();
if (settings.contains("SERIALLINK_COMM_PORT"))
{
m_portName = settings.value("SERIALLINK_COMM_PORT").toString();
m_baud = settings.value("SERIALLINK_COMM_BAUD").toInt();
m_parity = settings.value("SERIALLINK_COMM_PARITY").toInt();
m_stopBits = settings.value("SERIALLINK_COMM_STOPBITS").toInt();
m_dataBits = settings.value("SERIALLINK_COMM_DATABITS").toInt();
m_flowControl = settings.value("SERIALLINK_COMM_FLOW_CONTROL").toInt();
}
}
void SerialLink::writeSettings()
{
// Store settings
QSettings settings(QGC::COMPANYNAME, QGC::APPNAME);
settings.setValue("SERIALLINK_COMM_PORT", getPortName());
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
if (!hardwareConnect())
{
//Need to error out here.
emit communicationError(getName(),"Error connecting: " + m_port->errorString());
disconnect(); // This tidies up and sends the necessary signals
return;
}
// Qt way to make clear what a while(1) loop does
qint64 msecs = QDateTime::currentMSecsSinceEpoch();
qint64 initialmsecs = QDateTime::currentMSecsSinceEpoch();
quint64 bytes = 0;
bool triedreset = false;
bool triedDTR = false;
qint64 timeout = 5000;
forever
{
{
QMutexLocker locker(&this->m_stoppMutex);
if(m_stopp)
{
m_stopp = false;
break; // exit the thread
}
if (m_reqReset)
{
m_reqReset = false;
communicationUpdate(getName(),"Reset requested via DTR signal");
m_port->setDataTerminalReady(true);
msleep(250);
m_port->setDataTerminalReady(false);
}
if (m_transmitBuffer.length() > 0) {
QMutexLocker writeLocker(&m_writeMutex);
int numWritten = m_port->write(m_transmitBuffer);
bool txError = m_port->waitForBytesWritten(-1);
// if ((txError) || (numWritten == -1))
// qDebug() << "TX Error!";
m_transmitBuffer = m_transmitBuffer.remove(0, numWritten);
} else {
// qDebug() << "Wait write response timeout %1" << QTime::currentTime().toString();
bool error = m_port->waitForReadyRead(10);
if(error) { // Waits for 1/2 second [TODO][BB] lower to SerialLink::poll_interval?
QByteArray readData = m_port->readAll();
while (m_port->waitForReadyRead(10))
readData += m_port->readAll();
if (readData.length() > 0) {
emit bytesReceived(this, readData);
// qDebug() << "rx of length " << QString::number(readData.length());
m_bytesRead += readData.length();
m_bitsReceivedTotal += readData.length() * 8;
}
} else {
// qDebug() << "Wait write response timeout %1" << QTime::currentTime().toString();
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
if (bytes != m_bytesRead) // i.e things are good and data is being read.
{
bytes = m_bytesRead;
msecs = QDateTime::currentMSecsSinceEpoch();
}
else
{
if (QDateTime::currentMSecsSinceEpoch() - msecs > timeout)
{
//It's been 10 seconds since the last data came in. Reset and try again
msecs = QDateTime::currentMSecsSinceEpoch();
if (msecs - initialmsecs > 25000)
{
//After initial 25 seconds, timeouts are increased to 30 seconds.
//This prevents temporary silences from things like calibration commands
//from screwing things up. In all reality, timeouts should be enabled/disabled
//for events like that on a case by case basis.
//TODO ^^
timeout = 30000;
}
if (!triedDTR && triedreset)
{
triedDTR = true;
communicationUpdate(getName(),"No data to receive on COM port. Attempting to reset via DTR signal");
qDebug() << "No data!!! Attempting reset via DTR.";
m_port->setDataTerminalReady(true);
msleep(250);
m_port->setDataTerminalReady(false);
}
else if (!triedreset)
{
qDebug() << "No data!!! Attempting reset via reboot command.";
communicationUpdate(getName(),"No data to receive on COM port. Assuming possible terminal mode, attempting to reset via \"reboot\" command");
m_port->write("reboot\r\n",8);
triedreset = true;
}
else
{
communicationUpdate(getName(),"No data to receive on COM port....");
qDebug() << "No data!!!";
}
}
}
MG::SLEEP::msleep(SerialLink::poll_interval);
} // end of forever
if (m_port) { // [TODO][BB] Not sure we need to close the port here
qDebug() << "Closing Port #"<< __LINE__ << m_port->portName();
m_port->close();
delete m_port;
m_port = NULL;
emit disconnected();
emit connected(false);
}
}
void SerialLink::writeBytes(const char* data, qint64 size)
{
// qDebug() << "writeBytes" << m_portName << "attempting to tx " << size << "bytes.";
QByteArray byteArray(data, size);
{
QMutexLocker writeLocker(&m_writeMutex);
m_transmitBuffer.append(byteArray);
}
// Increase write counter
m_bitsSentTotal += size * 8;
// Extra debug logging
} else {
disconnect();
// Error occured
emit communicationError(getName(), tr("Could not send data - link %1 is disconnected!").arg(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()
{
m_dataMutex.lock();
if(m_port && m_port->isOpen()) {
const qint64 maxLength = 2048;
char data[maxLength];
/* Read as much data in buffer as possible without overflow */
if(maxLength < numBytes) numBytes = maxLength;
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");
}
/**
* @brief Get the number of bytes to read.
*
* @return The number of bytes to read
**/
qint64 SerialLink::bytesAvailable()
{
return 0;
}
}
/**
* @brief Disconnect the connection.
*
* @return True if connection has been disconnected, false if connection couldn't be disconnected.
**/
bool SerialLink::disconnect()
{
qDebug() << "disconnect";
if (m_port)
qDebug() << m_port->portName();
qDebug() << "running so disconnect" << m_port->portName();
QMutexLocker locker(&m_stoppMutex);
m_stopp = true;
wait(); // This will terminate the thread and close the serial port
emit disconnected(); // [TODO] There are signals from QSerialPort we should use
emit connected(false);
}
qDebug() << "already disconnected";
return true;
}
/**
* @brief Connect the connection.
*
* @return True if connection has been established, false if connection couldn't be established.
**/
bool SerialLink::connect()
{
QMutexLocker locker(&this->m_stoppMutex);
}
/**
* @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()
{
if(m_port)
{
qDebug() << "SerialLink:" << QString::number((long)this, 16) << "closing port";
m_port->close();
delete m_port;
m_port = NULL;
}
qDebug() << "SerialLink: hardwareConnect to " << m_portName;
m_port = new QSerialPort(m_portName);
Michael Carpenter
committed
{
emit communicationUpdate(getName(),"Error opening port: " + m_port->errorString());
return false; // couldn't create serial port.
Michael Carpenter
committed
}
QObject::connect(m_port,SIGNAL(aboutToClose()),this,SIGNAL(disconnected()));
QObject::connect(m_port, SIGNAL(error(QSerialPort::SerialPortError)),
this, SLOT(linkError(QSerialPort::SerialPortError)));
// port->setCommTimeouts(QSerialPort::CtScheme_NonBlockingRead);
m_connectionStartTime = MG::TIME::getGroundTimeNow();
if (!m_port->open(QIODevice::ReadWrite))
Michael Carpenter
committed
{
emit communicationUpdate(getName(),"Error opening port: " + m_port->errorString());
m_port->close();
return false; // couldn't open serial port
Michael Carpenter
committed
}
// Need to configure the port
m_port->setBaudRate(m_baud);
m_port->setDataBits(static_cast<QSerialPort::DataBits>(m_dataBits));
m_port->setFlowControl(static_cast<QSerialPort::FlowControl>(m_flowControl));
m_port->setStopBits(static_cast<QSerialPort::StopBits>(m_stopBits));
m_port->setParity(static_cast<QSerialPort::Parity>(m_parity));
emit connected();
emit connected(true);
qDebug() << "CONNECTING LINK: " << __FILE__ << __LINE__ << "with settings" << m_port->portName()
<< getBaudRate() << getDataBits() << getParityType() << getStopBits();
writeSettings();
void SerialLink::linkError(QSerialPort::SerialPortError error)
{
qDebug() << error;
}
/**
* @brief Check if connection is active.
*
* @return True if link is connected, false otherwise.
**/
bool SerialLink::isConnected()
{
if (m_port) {
bool isConnected = m_port->isOpen();
// qDebug() << "SerialLink #" << __LINE__ << ":"<< m_port->portName()
// << " isConnected =" << QString::number(isConnected);
// qDebug() << "SerialLink #" << __LINE__ << ":" << m_portName
// << " isConnected = NULL";
return m_portName;
Bryant Mairs
committed
/**
* This function maps baud rate constants to numerical equivalents.
* It relies on the mapping given in qportsettings.h from the QSerialPort library.
*/
qint64 SerialLink::getNominalDataRate()
{
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
int baudRate;
if (m_port) {
baudRate = m_port->baudRate();
} else {
baudRate = m_baud;
}
qint64 dataRate;
switch (baudRate)
{
case QSerialPort::Baud1200:
dataRate = 1200;
break;
case QSerialPort::Baud2400:
dataRate = 2400;
break;
case QSerialPort::Baud4800:
dataRate = 4800;
break;
case QSerialPort::Baud9600:
dataRate = 9600;
break;
case QSerialPort::Baud19200:
dataRate = 19200;
break;
case QSerialPort::Baud38400:
dataRate = 38400;
break;
case QSerialPort::Baud57600:
dataRate = 57600;
break;
case QSerialPort::Baud115200:
dataRate = 115200;
break;
// Otherwise do nothing.
case QSerialPort::UnknownBaud:
default:
dataRate = -1;
break;
qint64 SerialLink::getTotalUpstream()
{
m_statisticsMutex.lock();
return m_bitsSentTotal / ((MG::TIME::getGroundTimeNow() - m_connectionStartTime) / 1000);
m_statisticsMutex.unlock();
qint64 SerialLink::getCurrentUpstream()
{
qint64 SerialLink::getMaxUpstream()
{
qint64 SerialLink::getBitsSent()
{
qint64 SerialLink::getBitsReceived()
{
qint64 SerialLink::getTotalDownstream()
{
m_statisticsMutex.lock();
return m_bitsReceivedTotal / ((MG::TIME::getGroundTimeNow() - m_connectionStartTime) / 1000);
m_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()
{
// We should replace the accessors below with one to get the QSerialPort
int SerialLink::getBaudRate()
{
int SerialLink::getBaudRateType()
{
int baudRate;
if (m_port) {
baudRate = m_port->baudRate();
} else {
baudRate = m_baud;
}
return baudRate;
int SerialLink::getFlowType()
{
int flowControl;
if (m_port) {
flowControl = m_port->flowControl();
} else {
flowControl = m_flowControl;
}
return flowControl;
int SerialLink::getParityType()
{
int parity;
if (m_port) {
parity = m_port->parity();
} else {
parity = m_parity;
}
return parity;
int SerialLink::getDataBitsType()
{
int dataBits;
if (m_port) {
dataBits = m_port->dataBits();
} else {
dataBits = m_dataBits;
}
return dataBits;
int SerialLink::getStopBitsType()
{
int stopBits;
if (m_port) {
stopBits = m_port->stopBits();
} else {
stopBits = m_stopBits;
}
return stopBits;
int SerialLink::getDataBits()
{
int ret;
int dataBits;
if (m_port) {
dataBits = m_port->dataBits();
} else {
dataBits = m_dataBits;
}
switch (dataBits) {
case QSerialPort::Data5:
ret = 5;
break;
ret = 6;
break;
ret = 7;
break;
ret = 8;
break;
default:
break;
}
return ret;
}
int SerialLink::getStopBits()
{
int stopBits;
if (m_port) {
stopBits = m_port->stopBits();
} else {
stopBits = m_stopBits;
}
ret = 2;
break;
default:
ret = -1;
break;
}
return ret;
}
qDebug() << "current portName " << m_portName;
qDebug() << "setPortName to " << portName;
bool accepted = false;
if ((portName != m_portName)
&& (portName.trimmed().length() > 0)) {
m_portName = portName.trimmed();
// m_name = tr("serial port ") + portName.trimmed(); // [TODO] Do we need this?
emit nameChanged(m_portName); // [TODO] maybe we can eliminate this
}
bool SerialLink::setBaudRateType(int rateIndex)
{
Q_ASSERT_X(m_port != NULL, "setBaudRateType", "m_port is NULL");
// These minimum and maximum baud rates were based on those enumerated in qserialport.h
const int minBaud = (int)QSerialPort::Baud1200;
const int maxBaud = (int)QSerialPort::Baud115200;
if (m_port && (rateIndex >= minBaud && rateIndex <= maxBaud))
result = m_port->setBaudRate(static_cast<QSerialPort::BaudRate>(rateIndex));
emit updateLink(this);
return result;
bool SerialLink::setBaudRateString(const QString& rate)
{
bool ok;
int intrate = rate.toInt(&ok);
if (!ok) return false;
return setBaudRate(intrate);
}
bool accepted = false;
if (rate != m_baud) {
m_baud = rate;
accepted = true;
if (m_port)
accepted = m_port->setBaudRate(rate);
bool SerialLink::setFlowType(int flow)
{
bool accepted = false;
if (flow != m_flowControl) {
m_flowControl = static_cast<QSerialPort::FlowControl>(flow);
accepted = true;
if (m_port)
accepted = m_port->setFlowControl(static_cast<QSerialPort::FlowControl>(flow));
bool SerialLink::setParityType(int parity)
{
bool accepted = false;
if (parity != m_parity) {
m_parity = static_cast<QSerialPort::Parity>(parity);
accepted = true;
if (m_port) {
switch (parity) {
case QSerialPort::NoParity:
accepted = m_port->setParity(QSerialPort::NoParity);
break;
case 1: // Odd Parity setting for backwards compatibilty
accepted = m_port->setParity(QSerialPort::OddParity);
break;
case QSerialPort::EvenParity:
accepted = m_port->setParity(QSerialPort::EvenParity);
break;
case QSerialPort::OddParity:
accepted = m_port->setParity(QSerialPort::OddParity);
break;
default:
// If none of the above cases matches, there must be an error
accepted = false;
break;
}
bool SerialLink::setDataBits(int dataBits)
{
bool accepted = false;
if (dataBits != m_dataBits) {
m_dataBits = static_cast<QSerialPort::DataBits>(dataBits);
accepted = true;
if (m_port)
accepted = m_port->setDataBits(static_cast<QSerialPort::DataBits>(dataBits));
bool SerialLink::setStopBits(int stopBits)
{
// Note 3 is OneAndAHalf stopbits.
bool accepted = false;
if (stopBits != m_stopBits) {
m_stopBits = static_cast<QSerialPort::StopBits>(stopBits);
accepted = true;
if (m_port)
accepted = m_port->setStopBits(static_cast<QSerialPort::StopBits>(stopBits));
bool SerialLink::setDataBitsType(int dataBits)
{
bool accepted = false;
if (dataBits != m_dataBits) {
m_dataBits = static_cast<QSerialPort::DataBits>(dataBits);
accepted = true;
if (m_port)
accepted = m_port->setDataBits(static_cast<QSerialPort::DataBits>(dataBits));
}
return accepted;
}
bool SerialLink::setStopBitsType(int stopBits)
{
bool accepted = false;
if (stopBits != m_stopBits) {
m_stopBits = static_cast<QSerialPort::StopBits>(stopBits);
accepted = true;
if (m_port)
accepted = m_port->setStopBits(static_cast<QSerialPort::StopBits>(stopBits));
}
return accepted;
}