Files
giesCANne/serialworker.cpp
T
Collin Kidder f6b5db9617 Added the ability to track which direction frames are coming from
(from us or to us), import that detail from file formats that support
it, save it to file formats that support it. Also some bug fixes.
2015-12-01 20:25:52 -05:00

501 lines
14 KiB
C++

#include "serialworker.h"
#include <QSerialPort>
#include <QDebug>
#include <QTimer>
#include <QSettings>
SerialWorker::SerialWorker(CANFrameModel *model, QObject *parent) : QObject(parent)
{
serial = NULL;
rx_state = IDLE;
rx_step = 0;
buildFrame = new CANFrame;
canModel = model;
gotFrames = 0;
ticker = NULL;
elapsedTime = NULL;
framesPerSec = 0;
capturing = true;
gotValidated = true;
isAutoRestart = false;
targetID = -1;
readSettings();
}
SerialWorker::~SerialWorker()
{
if (serial != NULL)
{
if (serial->isOpen())
{
serial->clear();
serial->close();
}
serial->disconnect(); //disconnect all signals
delete serial;
}
if (ticker != NULL) ticker->stop();
}
void SerialWorker::run()
{
ticker = new QTimer;
connect(ticker, SIGNAL(timeout()), this, SLOT(handleTick()));
elapsedTime = new QTime;
elapsedTime->start();
ticker->setInterval(250); //tick four times per second
ticker->setSingleShot(false); //keep ticking
ticker->start();
}
void SerialWorker::readSettings()
{
QSettings settings;
if (settings.value("Main/ValidateComm", true).toBool())
{
doValidation = true;
}
else doValidation = false;
//doValidation=false;
}
void SerialWorker::setSerialPort(QSerialPortInfo *port)
{
QSettings settings;
currentPort = port;
if (serial != NULL)
{
if (serial->isOpen())
{
serial->clear();
serial->close();
}
serial->disconnect(); //disconnect all signals
delete serial;
}
serial = new QSerialPort(*port);
qDebug() << "Serial port name is " << port->portName();
//serial->setBaudRate(10000000); //more speed! probably does nothing for USB serial
serial->setDataBits(serial->Data8);
serial->setFlowControl(serial->HardwareControl); //this is important though
if (!serial->open(QIODevice::ReadWrite))
{
qDebug() << serial->errorString();
}
serial->setDataTerminalReady(true); //you do need to set these or the fan gets dirty
serial->setRequestToSend(true);
QByteArray output;
output.append((char)0xE7); //this puts the device into binary comm mode
output.append((char)0xE7);
output.append((char)0xF1); //signal we want to issue a command
output.append((char)0x06); //request canbus stats from the board
output.append((char)0xF1); //another command to the GVRET
output.append((char)0x07); //request device information
output.append((char)0xF1);
output.append((char)0x08); //setting singlewire mode
if (settings.value("Main/SingleWireMode", false).toBool())
{
output.append((char)0x10); //signal that we do want single wire mode
}
else
{
output.append((char)0xFF); //signal we don't want single wire mode
}
output.append((char)0xF1); //yet another command
output.append((char)0x09); //comm validation command
serial->write(output);
if (doValidation) connected = false;
else connected = true;
connect(serial, SIGNAL(readyRead()), this, SLOT(readSerialData()));
if (doValidation) QTimer::singleShot(1000, this, SLOT(connectionTimeout()));
}
void SerialWorker::connectionTimeout()
{
//one second after trying to connect are we actually connected?
if (!connected) //no?
{
//then emit the the failure signal and see if anyone cares
qDebug() << "Failed to connect to GVRET at that com port";
ticker->stop();
closeSerialPort(); //make sure it's properly closed anyway
emit connectionFailure();
}
}
void SerialWorker::readSerialData()
{
QByteArray data = serial->readAll();
unsigned char c;
//qDebug() << (tr("Got data from serial. Len = %0").arg(data.length()));
for (int i = 0; i < data.length(); i++)
{
c = data.at(i);
procRXChar(c);
}
}
void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0)
{
QByteArray buffer;
int c;
int ID;
CANFrame tempFrame = *frame;
tempFrame.isReceived = false;
//qDebug() << "Sending out frame with id " << frame->ID;
//show our sent frames in the list too. This happens even if we're not connected.
canModel->addFrame(tempFrame, false);
gotFrames++;
if (!connected) return;
ID = frame->ID;
if (frame->extended) ID |= 1 << 31;
buffer[0] = (char)0xF1; //start of a command over serial
buffer[1] = 0; //command ID for sending a CANBUS frame
buffer[2] = (unsigned char)(ID & 0xFF); //four bytes of ID LSB first
buffer[3] = (unsigned char)(ID >> 8);
buffer[4] = (unsigned char)(ID >> 16);
buffer[5] = (unsigned char)(ID >> 24);
buffer[6] = (unsigned char)(bus & 1);
buffer[7] = (unsigned char)frame->len;
for (c = 0; c < frame->len; c++)
{
buffer[8 + c] = frame->data[c];
}
buffer[8 + frame->len] = 0;
if (serial == NULL) return;
if (!serial->isOpen()) return;
//qDebug() << "writing " << buffer.length() << " bytes to serial port";
serial->write(buffer);
}
//a simple way for another thread to pass us a bunch of frames to send.
//Don't get carried away here. The GVRET firmware only has finite
//buffers and besides, the other end will get buried in traffic.
void SerialWorker::sendFrameBatch(const QList<CANFrame> *frames)
{
for (int i = 0; i < frames->length(); i++) sendFrame(&frames->at(i), frames->at(i).bus);
}
void SerialWorker::updateBaudRates(int Speed1, int Speed2)
{
QByteArray buffer;
qDebug() << "Got signal to update bauds. 1: " << Speed1 <<" 2: " << Speed2;
buffer[0] = (char)0xF1; //start of a command over serial
buffer[1] = 5; //setup canbus
buffer[2] = (unsigned char)(Speed1 & 0xFF); //four bytes of ID LSB first
buffer[3] = (unsigned char)(Speed1 >> 8);
buffer[4] = (unsigned char)(Speed1 >> 16);
buffer[5] = (unsigned char)(Speed1 >> 24);
buffer[6] = (unsigned char)(Speed2 & 0xFF); //four bytes of ID LSB first
buffer[7] = (unsigned char)(Speed2 >> 8);
buffer[8] = (unsigned char)(Speed2 >> 16);
buffer[9] = (unsigned char)(Speed2 >> 24);
buffer[10] = 0;
if (serial == NULL) return;
if (!serial->isOpen()) return;
serial->write(buffer);
}
void SerialWorker::procRXChar(unsigned char c)
{
switch (rx_state)
{
case IDLE:
if (c == 0xF1) rx_state = GET_COMMAND;
break;
case GET_COMMAND:
switch (c)
{
case 0: //receiving a can frame
rx_state = BUILD_CAN_FRAME;
rx_step = 0;
break;
case 1: //we don't accept time sync commands from the firmware
rx_state = IDLE;
break;
case 2: //process a return reply for digital input states.
rx_state = GET_DIG_INPUTS;
rx_step = 0;
break;
case 3: //process a return reply for analog inputs
rx_state = GET_ANALOG_INPUTS;
break;
case 4: //we set digital outputs we don't accept replies so nothing here.
rx_state = IDLE;
break;
case 5: //we set canbus specs we don't accept replies.
rx_state = IDLE;
break;
case 6: //get canbus parameters from GVRET
rx_state = GET_CANBUS_PARAMS;
rx_step = 0;
break;
case 7: //get device info
rx_state = GET_DEVICE_INFO;
rx_step = 0;
break;
case 9:
gotValidated = true;
//qDebug() << "Got validated";
rx_state = IDLE;
break;
}
break;
case BUILD_CAN_FRAME:
switch (rx_step)
{
case 0:
buildFrame->timestamp = c;
break;
case 1:
buildFrame->timestamp |= (uint)(c << 8);
break;
case 2:
buildFrame->timestamp |= (uint)c << 16;
break;
case 3:
buildFrame->timestamp |= (uint)c << 24;
break;
case 4:
buildFrame->ID = c;
break;
case 5:
buildFrame->ID |= c << 8;
break;
case 6:
buildFrame->ID |= c << 16;
break;
case 7:
buildFrame->ID |= c << 24;
if ((buildFrame->ID & 1 << 31) == 1 << 31)
{
buildFrame->ID &= 0x7FFFFFFF;
buildFrame->extended = true;
}
else buildFrame->extended = false;
break;
case 8:
buildFrame->len = c & 0xF;
if (buildFrame->len > 8) buildFrame->len = 8;
buildFrame->bus = (c & 0xF0) >> 4;
break;
default:
if (rx_step < buildFrame->len + 9)
{
buildFrame->data[rx_step - 9] = c;
}
else
{
rx_state = IDLE;
rx_step = 0;
//qDebug() << "emit from serial handler to main form id: " << buildFrame->ID;
if (capturing)
{
buildFrame->isReceived = true;
canModel->addFrame(*buildFrame, false);
gotFrames++;
if (buildFrame->ID == targetID) emit gotTargettedFrame(canModel->rowCount() - 1);
}
}
break;
}
rx_step++;
break;
case GET_ANALOG_INPUTS: //get 9 bytes - 2 per analog input plus checksum
switch (rx_step)
{
case 0:
break;
}
rx_step++;
break;
case GET_DIG_INPUTS: //get two bytes. One for digital in status and one for checksum.
switch (rx_step)
{
case 0:
break;
case 1:
rx_state = IDLE;
break;
}
rx_step++;
break;
case GET_CANBUS_PARAMS:
switch (rx_step)
{
case 0:
can0Enabled = c;
break;
case 1:
can0Baud = c;
break;
case 2:
can0Baud |= c << 8;
break;
case 3:
can0Baud |= c << 16;
break;
case 4:
can0Baud |= c << 24;
break;
case 5:
can1Enabled = c;
break;
case 6:
can1Baud = c;
break;
case 7:
can1Baud |= c << 8;
break;
case 8:
can1Baud |= c << 16;
break;
case 9:
can1Baud |= c << 24;
rx_state = IDLE;
qDebug() << "Baud 0 = " << can0Baud;
qDebug() << "Baud 1 = " << can1Baud;
if (!can1Enabled) can1Baud = 0;
if (!can0Enabled) can0Baud = 0;
connected = true;
emit connectionSuccess(can0Baud, can1Baud);
break;
}
rx_step++;
break;
case GET_DEVICE_INFO:
switch (rx_step)
{
case 0:
deviceBuildNum = c;
break;
case 1:
deviceBuildNum |= c << 8;
break;
case 2:
break; //don't care about eeprom version
case 3:
break; //don't care about file type
case 4:
break; //don't care about whether it auto logs or not
case 5:
deviceSingleWireMode = c;
rx_state = IDLE;
qDebug() << "build num: " << deviceBuildNum;
qDebug() << "single wire can: " << deviceSingleWireMode;
emit deviceInfo(deviceBuildNum, deviceSingleWireMode);
break;
}
rx_step++;
break;
case TIME_SYNC:
rx_state = IDLE;
break;
case SET_DIG_OUTPUTS:
rx_state = IDLE;
break;
case SETUP_CANBUS:
rx_state = IDLE;
break;
case SET_SINGLEWIRE_MODE:
rx_state = IDLE;
break;
}
}
void SerialWorker::handleTick()
{
//qDebug() << "Tick!";
if (connected)
{
if (!gotValidated)
{
if (serial->isOpen()) //if it's still false we have a problem...
{
qDebug() << "Comm validation failed. ";
closeSerialPort(); //start by stopping everything.
QTimer::singleShot(500, this, SLOT(handleReconnect()));
return;
}
}
}
framesPerSec += gotFrames * 1000 / elapsedTime->elapsed() - (framesPerSec / 4);
elapsedTime->restart();
emit frameUpdateTick(framesPerSec / 4, gotFrames); //sends stats to interested parties
canModel->sendBulkRefresh(gotFrames);
gotFrames = 0;
if (doValidation && serial) sendCommValidation();
}
void SerialWorker::handleReconnect()
{
qDebug() << "Automatically reopening the connection";
setSerialPort(currentPort); //then go back through the re-init
}
void SerialWorker::sendCommValidation()
{
QByteArray output;
gotValidated = false;
output.append((char)0xF1); //another command to the GVRET
output.append((char)0x09); //request a reply to get validation
serial->write(output);
}
//totally shuts down the whole thing
void SerialWorker::closeSerialPort()
{
if (serial == NULL) return;
if (serial->isOpen())
{
serial->clear();
serial->close();
}
serial->disconnect();
ticker->stop();
delete serial;
serial = NULL;
}
void SerialWorker::stopFrameCapture()
{
qDebug() << "Stopping frame capture";
capturing = false;
}
void SerialWorker::startFrameCapture()
{
qDebug() << "Starting up frame capture";
capturing = true;
}
void SerialWorker::targetFrameID(int target)
{
targetID = target;
}