829 lines
26 KiB
C++
829 lines
26 KiB
C++
#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: 16–19, 21–31, 82, 88–95, 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, 39–43, 49–55, 59, 61–63, 74, 76–79
|
||
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, 135–139, 140–148, 150–157, 159
|
||
// Known Ovens: 12–15, 176–182, 184–191, 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();
|
||
}
|