Chromophant/shimlccontroller.cpp
2025-09-24 20:28:18 +02:00

829 lines
26 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "shimlccontroller.h"
/* Encode Message and store to data
* 01 00 01 00 -> Header: First 2 Bytes are type uint16 LE 1 called "info", second 2 bytes are uint8 LE 1 called "code"
* (other info, code numbers unknown, always 1?)
* xx xx xx xx => Msg len+1 (becuase of 0d at the end) uint32 LE
* ... msg in ASCII ...
* 0d -> End */
void cbmenc(QString message, QByteArray *data)
{
data->append("\x01\x00\x01\x00", 4); // Fixed header
char buf[4];
qToLittleEndian((quint32)message.length()+1, buf);
data->append(buf, 4);
data->append(message.toLatin1());
data->append("\x0d");
}
void cbmenc(QByteArray *data)
{
quint32 len = data->length()+1;
char buf[4];
qToLittleEndian(len, buf);
data->prepend(buf, 4);
data->prepend("\x01\x00\x01\x00", 4);
data->append("\x0d");
}
QString cbmSet(const QString& attr, float val) {
QByteArray buf(reinterpret_cast<const char*>(&val), sizeof(val));
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
std::reverse(buf.begin(), buf.end());
#endif
QString hexval = QString(buf.toHex().toUpper());
QString cmd = QString("%1=0%2L").arg(attr, hexval);
return cmd;
}
// Encode Data with cbmenc and send to peer
void ShimTCPController::cbmquery(QString message)
{
QByteArray data{};
cbmenc(message, &data);
this->writeData(&data);
}
/* Actual Read command
* 49 52 -> IR (Interface Read?)
* xx -> fileHandle
* xx xx -> size (max 1020!)
* Repeat until data complete, then exit */
void ShimTCPController::cbmir(quint8 filehandle, QByteArray *irDataRet)
{
mode = ParseCbmir;
QByteArray data{"IR"};
irFileHandle = filehandle;
char fh[1];
qToLittleEndian(filehandle, fh);
data.append(fh, 1);
char buf[2];
// Max Size 1020!
if(irPendingSize > 1020)
{
qToLittleEndian((quint16)1020, buf);
irPendingSize -= 1020;
}
else
{
qToLittleEndian((quint16)irPendingSize, buf);
irPendingSize = 0;
}
data.append(buf, 2);
cbmenc(&data);
if(irDataRet != nullptr)
irData = irDataRet;
writeData(&data);
}
void ShimTCPController::cbmiw(quint8 filehandle, QByteArray *irDataToSend)
{
mode = ParseCbmiw;
QByteArray data{"IW"};
irFileHandle = filehandle;
char fh[1];
qToLittleEndian(filehandle, fh);
data.append(fh, 1);
if(irDataToSend != nullptr)
irData = irDataToSend;
irPendingSize = irData->length();
// Max send size too 1020!
if(irPendingSize > 1020)
{
data.append(irData->data(), 1020);
irData->remove(0, 1020);
irPendingSize -= 1020;
}
else
{
data.append(irData->data(), irPendingSize);
irPendingSize = 0;
}
cbmenc(&data);
writeData(&data);
}
ShimTCPController::ShimTCPController(QObject *parent) : QObject(parent)
{
socket = new QTcpSocket(this);
connect(socket, &QTcpSocket::readyRead, this, &ShimTCPController::readData);
connect(socket, &QTcpSocket::errorOccurred, this, &ShimTCPController::displayError);
connect(socket, &QTcpSocket::stateChanged, this, &ShimTCPController::emitConnectionStateChanged);
connect(this, &ShimTCPController::dataAvailable, this, &ShimTCPController::dataAvailableErrorNoF);
}
ShimTCPController::~ShimTCPController()
{
socket->close();
}
void ShimTCPController::connectToHost(QString host, int port)
{
qDebug() << TAG << "Connecting to host" << host;
// Close Connection CAUTION: If currently waiting for response this could mess up the state machine in an infinte waiting loop, TODO: proper handeling
socket->abort();
// Delete any maybe pending data
delete pendingData;
pendingData = nullptr;
// Connect TCP Socket Port 5001
socket->connectToHost(host, port);
//socket->waitForConnected();
}
// Wirte and flush data if connected
bool ShimTCPController::writeData(QByteArray *data)
{
if(socket->state() == QAbstractSocket::ConnectedState)
{
socket->write(*data);
socket->flush();
return true;
}
else
{
pendingData = new QByteArray(*data);
qDebug() << TAG << "socket write Data sceduled when connection state changed to ConnectedState!";
return false;
}
}
void ShimTCPController::displayError(QAbstractSocket::SocketError socketError)
{
// TODO: Proper centering by passing parent object!
switch (socketError) {
case QAbstractSocket::RemoteHostClosedError:
break;
case QAbstractSocket::HostNotFoundError:
QMessageBox::information(nullptr, tr("ShimLCController"),
tr("The host was not found. Please check the "
"host name and port settings."));
break;
case QAbstractSocket::ConnectionRefusedError:
QMessageBox::information(nullptr, tr("ShimLCController"),
tr("The connection was refused by the peer."));
break;
default:
QMessageBox::information(nullptr, tr("ShimLCController"),
tr("The following error occurred: %1.")
.arg(socket->errorString()));
break;
}
qDebug() << socketError;
}
// Parse Data and reset parserFunction signal
void ShimTCPController::readData()
{
switch(mode)
{
case ParseQuery: parseCbmquery(socket->readAll()); break;
case ParseCbmir: parseCbmIr(); break;
case ParseCbmiw: parseCbmIw(); break;
}
}
void ShimTCPController::parseCbmquery(QByteArray data)
{
if(data.length() < 5)
{
qWarning("Data len < 5!"); // TODO: Error handling
emit dataAvailable("-");
return;
}
int len = qFromLittleEndian<qint16>(data.mid(4, 2));
data.slice(8);
if(len != data.length())
{
qWarning("Data len mismatch!"); // TODO: Error handling
emit dataAvailable("-");
return;
}
//data.removeLast();
QTextCodec::ConverterState state;
QTextCodec *codec = QTextCodec::codecForName("ASCII");
const QString validText = codec->toUnicode(data.constData(), data.size(), &state);
if (state.invalidChars == 0)
emit dataAvailable(data);
else
{
qCritical("Invalid ASCII chars in response!");
qDebug() << data;
emit dataAvailable("-");
}
}
void ShimTCPController::parseCbmIr()
{
if (irPendingSize == -2)
{
QByteArray resp = socket->readAll();
if(resp.at(10) != '0')
{
qWarning("Did not exit IR mode!");
qDebug() << "Response:" << resp;
}
irPendingSize = -1;
mode = ParseQuery;
emit irDone();
}
else
{
int len_before = irData->length();
irData->append(socket->readAll());
char check = irData->at(5+len_before);
int msgLen = (irData->mid(5, 1) + irData->mid(4,1)).toHex().toUInt(nullptr, 16);
irData->remove(len_before, 8);
if(irData->length() - len_before != msgLen)
{
qWarning("irData->length() != msgLen | Expected on 2nd request - FIXME");
qDebug() << "msgLen:" << msgLen;
qDebug() << "actual:" << irData->length() - len_before << " | TOTAL: " << irData->length();
}
// More Data if last request was maxed out
if(irData->length() - len_before == 1024)
{
cbmir((quint8)irFileHandle);
return;
}
// Close File
else
{
if (irPendingSize != 0)
{
qDebug() << "File Size Configured wrong! Still pending " << irPendingSize << " but got STOP!";
}
QByteArray data{"IC"};
char fh[1];
qToLittleEndian((quint8)irFileHandle, fh);
data.append(fh, 1);
cbmenc(&data);
writeData(&data);
irPendingSize = -2;
}
}
}
void ShimTCPController::parseCbmIw()
{
if(irPendingSize == 0) // No more data to write
{
QByteArray data{"IC"};
char fh[1];
qToLittleEndian((quint8)irFileHandle, fh);
data.append(fh, 1);
cbmenc(&data);
writeData(&data);
irPendingSize = -2;
}
else if(irPendingSize == -2) // Still waiting for closing confirm
{
QByteArray resp = socket->readAll();
if(resp.at(10) != '0')
{
qWarning("Did not exit IW mode!");
qDebug() << "Response:" << resp;
}
irPendingSize = -1;
mode = ParseQuery;
emit irDone();
}
else // Still more data to send
{
cbmiw((quint8)irFileHandle);
return;
}
}
void ShimTCPController::dataAvailableErrorNoF(QByteArray data)
{
qWarning("Data recieved but no dataAvailable function selected!");
qDebug() << data;
}
void ShimTCPController::emitConnectionStateChanged()
{
QAbstractSocket::SocketState state = socket->state();
if(state == QAbstractSocket::ConnectedState and (pendingData != nullptr))
{
writeData(pendingData);
delete pendingData;
pendingData = nullptr;
qDebug() << "pendng data sent!";
}
emit connectionStateChanged(state);
}
// -----------------------------------------------------------------------------------------------------------------------------
float QByteArrayToFloatBE(QByteArray arr)
{
static_assert(std::numeric_limits<float>::is_iec559, "Only supports IEC 559 (IEEE 754) float");
quint32 temp = ((quint8)arr[0] << 24) | ((quint8)arr[1] << 16) | ((quint8)arr[2] << 8) | (quint8)arr[3]; // Big endian
float* out = reinterpret_cast<float*>(&temp);
return *out;
}
float QByteArrayToFloatLE(QByteArray arr)
{
static_assert(std::numeric_limits<float>::is_iec559, "Only supports IEC 559 (IEEE 754) float");
quint32 temp = ((quint8)arr[3] << 24) | ((quint8)arr[2] << 16) | ((quint8)arr[1] << 8) | (quint8)arr[0]; // Little endian
float* out = reinterpret_cast<float*>(&temp);
return *out;
}
ShimLCCommandStack::ShimLCCommandStack(QObject *parent)
:parent(parent)
{
disconnect(&connection, &ShimTCPController::dataAvailable, 0, 0);
connect(&connection, &ShimTCPController::dataAvailable, this, &ShimLCCommandStack::newResult);
connect(&connection, &ShimTCPController::irDone, this, &ShimLCCommandStack::exitedIr);
connect(&connection, &ShimTCPController::connectionStateChanged, this, &ShimLCCommandStack::connectionStateChanged);
}
ShimLCCommandStack::~ShimLCCommandStack()
{
}
uint32_t ShimLCCommandStack::addCmd(char opcode, QString cmd, QByteArray* dest, uint iSize)
{
qDebug() << TAG << "added cmd: Opcode:" << opcode << "Cmd:" << cmd;
id ++;
stackIn.push_back(ShimLCCmd{id, opcode, cmd, dest, iSize});
qDebug() << TAG << "Current size of cmd stack:" << stackIn.size();
if(busy_id == 0)
this->next();
return id;
}
void ShimLCCommandStack::next()
{
if(stackIn.length() == 0)
{
busy_id = 0;
return;
}
busy_id = stackIn.front().id;
// TODO: Handling unimplemented opcodes!
switch(stackIn.first().opcode)
{
case 'I':
// I Commands (difference for TCP Controller! Returns Raw data)
connection.cbmquery(stackIn.first().opcode + stackIn.first().cmd);
break;
case 'K': case 'M': case 'G':
// K, M, G Commands (Return ASCII)
connection.cbmquery(stackIn.first().opcode + stackIn.first().cmd);
break;
}
}
void ShimLCCommandStack::newResult(QByteArray data)
{
bool ok = 0;
if(stackIn.first().dest != nullptr)
switch(stackIn.first().opcode)
{
case 'K':
*(stackIn.first().dest) = data.mid(2);
break;
case 'M': case 'G':
// This is all horribly hacky, pls fix
data = QByteArray::fromHex(data.mid(4, 8));
*(stackIn.first().dest) = QByteArray::number(QByteArrayToFloatBE(data));
break;
case 'I':
if(data.mid(2,1).toInt(&ok) == 0 && ok)
{
quint8 fileHandle = data.mid(4,1).toHex().toUInt(nullptr, 16);
connection.irPendingSize = stackIn.first().iSize;
// Read Handler >>> IOR
if (stackIn.first().cmd.at(1) == 'R')
connection.cbmir(fileHandle, stackIn.first().dest);
// Write Handler >>> IOW | currently hacky: dest is not destination in this case, but rather data to send, FIXME
if (stackIn.first().cmd.at(1) == 'W')
connection.cbmiw(fileHandle, stackIn.first().dest);
}
else
qWarning() << TAG << "Got invalid I command! Cmd, resp:" << stackIn.first().cmd << data;
stackIn.pop_front();
return; // Return without dataAV!
}
stackIn.pop_front();
emit dataAvailable(busy_id);
next();
}
uint32_t ShimLCCommandStack::setVal(QString cmd, QByteArray* dest)
{
char opcode = 'K';
return addCmd(opcode, cmd, dest);
}
uint32_t ShimLCCommandStack::getSetVal(QString cmd, QByteArray* dest)
{
char opcode = 'G';
return addCmd(opcode, cmd, dest);
}
uint32_t ShimLCCommandStack::getActVal(QString cmd, QByteArray* dest)
{
char opcode = 'M';
return addCmd(opcode, cmd, dest);
}
void ShimLCCommandStack::reconnect(QSettings *settings)
{
// Signal reconnect to TCP stack, will close TCP stream
connection.connectToHost(settings->value("connection/addr", "localhost").toString(), settings->value("connection/port", 5001).toInt());
// Clear stack and pending flag
stackIn.clear();
busy_id = 0;
// Clear for new data
qDebug() << TAG << "Cleared stack! Good for new data";
}
void ShimLCCommandStack::exitedIr()
{
emit dataAvailable(busy_id);
next();
}
uint32_t ShimLCCommandStack::readFile(QString filename, QByteArray* dest, uint size)
{
dest->clear();
return addCmd('I', "OR "+filename, dest, size);
}
uint32_t ShimLCCommandStack::writeFile(QString filename, QByteArray* data)
{
return addCmd('I', "OW "+filename, data, data->size());
}
// -----------------------------------------------------------------------------------------------------------------------------
void ShimLCController::updateConfig()
{
connect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parseLcnf);
ids[ID_CONF] = cmd.readFile("lcnf.", &data, 4236);
//ids[ID_CONF] = cmd.iCmd("OR lcnf.", &data);
}
void ShimLCController::updateStatus()
{
connect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parseStatus);
ids[ID_STATUS] = cmd.readFile("lmon.", &data, 1575);
//ids[ID_STATUS] = cmd.iCmd("OR lmon.", &data);
}
void ShimLCController::updateEnv()
{
connect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parseEnv);
ids[ID_ENV] = cmd.readFile("lenv.", &data, 90);
}
void ShimLCController::updateChk()
{
connect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parseChk);
ids[ID_CHK] = cmd.readFile("lchk.", &data, 248);
}
void ShimLCController::updatePif()
{
connect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parsePif);
ids[ID_PIF] = cmd.readFile("lpif.", &data, 4236);
}
void ShimLCController::updateBuf()
{
connect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parseLbuf);
ids[ID_BUF] = cmd.readFile("lbuf.0", &data, 1000);
}
void ShimLCController::reconnect(QSettings *settings)
{
// Signal reconnect to CMD stack (clears stack, deletes pending commands etc)
cmd.reconnect(settings);
// Disconnect any maybe pending parser functions
disconnect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parseLcnf);
disconnect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parseStatus);
// Reconnect done, clear to send new commands
cmd.setVal("AUXOPE 64", nullptr);
cmd.setVal("FIX.CH -1", nullptr);
cmd.setVal("SETCBM", nullptr);
//cmd.setVal("LMONLVL=000000000L", nullptr);
}
ShimLCController::ShimLCController(QObject *parent)
{
connect(&cmd, &ShimLCCommandStack::connectionStateChanged, this, &ShimLCController::connectionStateChanged);
}
ShimLCController::~ShimLCController()
{
}
void ShimLCController::parseLcnf(uint32_t idVal)
{
if(ids[ID_CONF] != idVal)
return;
//qDebug() << data;
if(data.length() < 6+48*12)
{
qWarning("Status update failed!");
return;
}
configuration.clear();
for(int i = 0; i<12; i++)
{
QByteArray snippet = data.mid(6+48*i, 48);
int id = snippet.at(0);
QString name = QString(snippet.mid(2,16)).split('\x00').at(0);
QString version = QString(snippet.mid(18,4)).split('\x00').at(0);
QString serial = QString(snippet.mid(22,12)).split('\x00').at(0);
qDebug() << "Device Config:" << i+1 << id << name << version << serial;
//if(name.left(2).compare("LC") == 0)
// Known Pumps: 1619, 2131, 82, 8895, 281
if(INBOUND(id, 16, 19) or INBOUND(id, 21, 31) or id == 82 or INBOUND(id, 88, 95) or id == 281)
configuration.pumps.append(LCPort(i+1, id, name, version, serial));
//else if(name.left(3).compare("SPD") == 0)
// Known Detectors: 32, 33, 35, 36, 3943, 4955, 59, 6163, 74, 7679
else if(INBOUND(id, 32, 33) or INBOUND(id, 35, 36) or INBOUND(id, 39, 43) or INBOUND(id, 49, 55) or id == 59 or INBOUND(id, 61, 63) or id == 74 or INBOUND(id, 76, 79))
configuration.detectors.append(LCPort(i+1, id, name, version, serial));
// TODO:
// Known Autosamplers: 128, 129, 135139, 140148, 150157, 159
// Known Ovens: 1215, 176182, 184191, 432, 434
// Known Subcon: 192, 193, 449
else if(id != 0)
configuration.other.append(LCPort(i+1, id, name, version, serial));
}
// SCL Information
QByteArray snippet = data.mid(612+4, 48); // Offset 4 because of AI0.
quint32 unitData = qFromLittleEndian<quint32>(snippet.mid(40, 4));
if(unitData & 64u)
{
if(unitData & 4096u)
cmd.setVal(cbmSet("LMONLVL", 3.0), nullptr);
else if(unitData & 2048u)
cmd.setVal(cbmSet("LMONLVL", 2.0), nullptr);
else
cmd.setVal(cbmSet("LMONLVL", 1.0), nullptr);
}
if(unitData & 256u)
cmd.setVal(cbmSet("LBUFLVL", 0.0), nullptr);
if(unitData & 512u)
qFatal("SetupSecurityExtension not implemented yet! TODO!");
// Options
QByteArray opInf = data.mid(578+4, 35); // Again, offset 4
qDebug() << "Device Config: AD1 " << (quint8)opInf.at(0);
configuration.AD1 = (quint8)opInf.at(0);
qDebug() << "Device Config: AD2 " << (quint8)opInf.at(1);
configuration.AD2 = (quint8)opInf.at(1);
emit newConfigAv();
disconnect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parseLcnf);
}
void ShimLCController::parseStatus(uint32_t idVal)
{
if(ids[ID_STATUS] != idVal)
return;
// Uptime is at data[354:360] encoded in int LE! Unit: [1/100min]
uint32_t time = 0;
for(int i = 0; i<6; i++)
time += data.mid(354+i,1).toHex().toInt(nullptr, 16) << 8*i;
lastStatus.time = time;
// --- STATUS ---
lastStatus.stats.silst = 0;
lastStatus.stats.status1 = 0;
lastStatus.stats.status2 = 0;
for(int i = 0; i<4; i++)
{
lastStatus.stats.silst += data.mid(4+19+i,1).toHex().toUInt(nullptr, 16) << 8*i;
lastStatus.stats.status1 += data.mid(4+587+i,1).toHex().toUInt(nullptr, 16) << 8*i;
lastStatus.stats.status2 += data.mid(4+591+i,1).toHex().toUInt(nullptr, 16) << 8*i;
}
lastStatus.autosampler.rack = 0;
for(int i = 0; i<4; i++)
lastStatus.autosampler.rack += data.mid(49+i,1).toHex().toInt(nullptr, 16) << 8*i;
// --- PUMPS ---
// Pressure is at A: data[4:8], B: data[8:12], C:[12:16] encoded in int LE! Unit: wtf... Conversion was done via curve fitting!
for(int num_pump=1; num_pump<4; num_pump++)
{
uint32_t pres = 0;
for(int i = 0; i<4; i++)
pres += data.mid(num_pump*4+i,1).toHex().toInt(nullptr, 16) << 8*i;
if(pres != 0)
lastStatus.pump[num_pump-1].pPressure(pres); // <- formula was done via curve fitting - reading values in Bar from the display, looking at values here and using scipy
}
// Set FLow is at A: data[77:81] ... encoded in float LE! Unit: [ml/min]
for(int num_pump=0; num_pump<3; num_pump++)
lastStatus.pump[num_pump].setFlow = QByteArrayToFloatLE(data.mid(77+4*num_pump, 4));
lastStatus.totalFlow = QByteArrayToFloatLE(data.mid(77+4*3, 4));
// --- DETECTORS ---
// TODO: Currently only limited support of UV-VIS detectors!
// TODO: Obv missing a lot of params (17!)!
for(int detector_num = 0; detector_num<2; detector_num++)
{
lastStatus.detector[detector_num].waveLen1 = QByteArrayToFloatLE(data.mid(1+4*(36 + 17*detector_num), 4));
lastStatus.detector[detector_num].waveLen2 = QByteArrayToFloatLE(data.mid(1+4*(37 + 17*detector_num), 4));
lastStatus.detector[detector_num].lamp = QByteArrayToFloatLE(data.mid(1+4*(50 + 17*detector_num), 4));
}
// --- AUTOSAMPLER ---
//lastStatus.autosampler.rack = data.mid(382,1).toHex().toUInt();
qDebug() << lastStatus.autosampler.rack;
// --- BUFFERS ---
for(int i = 0; i<7; i++)
lastStatus.buf_wait[i] = qFromLittleEndian<quint16>(data.mid(434+i*2, 2));
//qDebug() << lastStatus.totalFlow;
//for(int num_pump=0; num_pump<3; num_pump++)
// qDebug() << lastStatus.pump[num_pump].actPressure;
emit newStatusAv();
disconnect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parseStatus);
}
void ShimLCController::parseEnv(uint32_t idVal)
{
// TODO: proper handling!
if(ids[ID_ENV] != idVal)
return;
for(int i = 0; i<8; i++)
qDebug() << "CH" << i << " " << qFromLittleEndian<quint16>(data.mid(42+i*2, 2));
for(int i = 1; i<5; i++)
qDebug() << "CH" << i+7 << " " << qFromLittleEndian<quint16>(data.mid(71+i*2, 2));
qDebug() << "ADUSE 1 " << (quint8)data.at(41);
qDebug() << "ADUSE 2 " << (quint8)data.at(70);
data.replace(41, 1, "\x41");
data.replace(42, 2, "\x14\x00");
cmd.writeFile("lenv.", &data);
disconnect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parseEnv);
}
void ShimLCController::parseChk(uint32_t idVal)
{
// TODO: proper handling!
if(ids[ID_CHK] != idVal)
return;
qDebug() << "Re-Uploading lchk";
cmd.writeFile("lchk.", &data);
disconnect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parseChk);
}
void ShimLCController::parsePif(uint32_t idVal)
{
// TODO: proper handling!
if(ids[ID_PIF] != idVal)
return;
qDebug() << "PIF!";
cmd.setVal(cbmSet("BUFCLR", 0), nullptr);
cmd.setVal("BUFEND", nullptr);
cmd.setVal(cbmSet("BUFSTR", 1), nullptr);
cmd.setVal("KEYLOK -3", nullptr);
disconnect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parsePif);
}
void ShimLCController::parseLbuf(uint32_t idVal)
{
// TODO: proper handling!
if(ids[ID_BUF] != idVal)
return;
qDebug() << "Collecting new Buffer data... Len: " << data.length();
int i = 4;
while(i < data.length())
{
bool earlyReturn = 0;
bool wthmode = 0;
quint8 b = 0;
quint8 cur_ctrl = data.at(i);
while((i < data.length()) && ((cur_ctrl & 0x80u) != 0) && (not earlyReturn))
{
switch(cur_ctrl)
{
case 0xC1u:
// "CBM data Start event"
bufst[cur_buf].curFlag |= 0x80u; // Set CBM data bit (highest bit)
// curRT??? TODO?
bufst[cur_buf].period = qFromLittleEndian<quint16>(data.mid(i+1, 2));
i += 5;
earlyReturn = 1;
break;
case 0xC2u:
// "CBM data Stop event"
bufst[cur_buf].curFlag &= 0x7fu; // Unset CBM data bit (highest bit)
i += 1;
earlyReturn = 1;
break;
case 0xC4u:
// "Rate event"
bufst[cur_buf].period = qFromLittleEndian<quint16>(data.mid(i+1, 2));
i += 3;
earlyReturn = 1;
break;
case 0xC5u:
// "Fact Event"
bufst[cur_buf].convFact = qFromLittleEndian<qfloat16>(data.mid(i+1, 2));
qDebug() << "New conv Fact: " << bufst[cur_buf].convFact;
bufst[cur_buf].gainFact = qFromLittleEndian<qfloat16>(data.mid(i+5, 2));
i += 9;
earlyReturn = 1;
break;
case 0xC8u:
// "Data reset event"
bufst[cur_buf].curValue = 0;
i++;
break;
default:
if(0xA0u == (cur_ctrl & 0xE0u)) // If 0b101xxxxx
{
b = (quint8)(cur_ctrl << 5);
wthmode = 1;
}
else
bufst[cur_buf].curFlag |= (cur_ctrl & 0x1Fu);
i++;
break;
}
if(i < data.length())
cur_ctrl = data.at(i);
}
if((not earlyReturn) && (i < data.length()))
{
cur_ctrl = data.at(i);
quint8 upper3 = (quint8)((cur_ctrl >> 5) & 3u); // upper 3 bytes
if(wthmode)
bufst[cur_buf].databuf[upper3] = (quint8)((cur_ctrl & 0x1Fu) | b);
else
{
if((cur_ctrl & 0x10u) == 0)
for(int j = 0; j < 4; j++)
bufst[cur_buf].databuf[j] = 0;
else
for(int j = 0; j < 4; j++)
bufst[cur_buf].databuf[j] = 0xFFu;
bufst[cur_buf].databuf[upper3] = (quint8)((cur_ctrl & 0x1Fu) | (bufst[cur_buf].databuf[3] & 0xE0u));
}
i++;
while(upper3 > 0)
{
upper3 -= 1;
bufst[cur_buf].databuf[upper3] = data.at(i);
i++;
}
int num = bufst[cur_buf].curValue + qFromLittleEndian<qint32>(bufst[cur_buf].databuf);
bufst[cur_buf].curValue = num;
double data = (double)num * (double)bufst[cur_buf].convFact;
bufst[cur_buf].vals.append(data);
}
}
disconnect(&cmd, &ShimLCCommandStack::dataAvailable, this, &ShimLCController::parseLbuf);
data.clear();
emit newDataAv();
}