Files
giesCANne/serialworker.cpp
T
Collin Kidder 41ee0a8646 Now the playback window is capable of vastly quicker sending of frames.
Use the burst setting and crank it up. Set the time interval to around 2ms and the burst to 6 and you'll be slinging frames like crazy.
2015-07-29 20:16:44 -04:00

463 lines
13 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;
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::readSettings()
{
QSettings settings;
if (settings.value("Main/ValidateComm", true).toBool())
{
doValidation = true;
}
else 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
serial->open(QIODevice::ReadWrite);
serial->setDataTerminalReady(true); //you do need to set these or the fan gets dirty
serial->setRequestToSend(true);
QByteArray output;
output.append(0xE7); //this puts the device into binary comm mode
output.append(0xE7);
output.append(0xF1); //signal we want to issue a command
output.append(0x06); //request canbus stats from the board
output.append(0xF1); //another command to the GVRET
output.append(0x07); //request device information
output.append(0xF1);
output.append(0x08); //setting singlewire mode
output.append(0xF1); //yet another command
output.append(0x09); //comm validation command
if (settings.value("Main/SingleWireMode", false).toBool())
{
output.append(0x10); //signal that we do want single wire mode
}
else
{
output.append(0xFF); //signal we don't want single wire mode
}
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()));
if (ticker == NULL)
{
ticker = new QTimer;
connect(ticker, SIGNAL(timeout()), this, SLOT(handleTick()));
}
if (elapsedTime == NULL)
{
elapsedTime = new QTime;
elapsedTime->start();
}
ticker->setInterval(250); //tick four times per second
ticker->setSingleShot(false); //keep ticking
ticker->start();
}
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;
//qDebug() << "Sending out frame with id " << frame->ID;
if (!connected) return;
ID = frame->ID;
if (frame->extended) ID |= 1 << 31;
buffer[0] = 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] = 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)
{
canModel->addFrame(*buildFrame, false);
gotFrames++;
}
}
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;
}
}
void SerialWorker::handleTick()
{
//qDebug() << "Tick!";
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) 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(0xF1); //another command to the GVRET
output.append(0x09); //request a reply to get validation
serial->write(output);
}
//totally shuts down the whole thing
void SerialWorker::closeSerialPort()
{
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;
}