Add design prototype, some renamings.
This commit is contained in:
404
bcvdatamanager.cpp
Normal file
404
bcvdatamanager.cpp
Normal file
@@ -0,0 +1,404 @@
|
||||
/***************************************************************************
|
||||
|
||||
BionxControl
|
||||
Copyright © 2025 christoph holzheuer
|
||||
christoph.holzheuer@gmail.com
|
||||
|
||||
Using:
|
||||
|
||||
BigXionFlasher USB V 0.2.4 rev. 97
|
||||
© 2011-2013 by Thomas Koenig <info@bigxionflasher.org>
|
||||
@see www.bigxionflasher.org
|
||||
|
||||
Bionx Bike Info
|
||||
© 2018 Thorsten Schmidt (tschmidt@ts-soft.de)
|
||||
@see www.ts-soft.de
|
||||
|
||||
mhs_can_drv.c 3.00
|
||||
© 2011 - 2015 by MHS-Elektronik GmbH & Co. KG, Germany
|
||||
Demlehner Klaus, info@mhs-elektronik.de
|
||||
@see www.mhs-elektronik.de
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
@see https://github.com/bikemike/bionx-bikeinfo
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QTableView>
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
#include <QStatusBar>
|
||||
#include <QApplication>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include <bcvdatamanager.h>
|
||||
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
|
||||
|
||||
BCDataManager::BCDataManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
createValueTypes();
|
||||
|
||||
//qRegisterMetaType<BCData*>("BCData*");
|
||||
qRegisterMetaType<BCData*>();
|
||||
|
||||
_transmitter.moveToThread(&_worker);
|
||||
|
||||
connect(this, &BCDataManager::sendValueCommand, &_transmitter, &BCTransmitter::enqueueValueOp);
|
||||
|
||||
// B) Ergebnisse empfangen (Runner -> Manager)
|
||||
//connect(&_transmitter, &BCTransmitter::commandFinished, this, &BCDataManager::onCommandFinished);
|
||||
//connect(&_transmitter, &BCTransmitter::messageLogged, this, &BCDataManager::onRunnerMessage);
|
||||
|
||||
// C) Aufräumen: Wenn Thread endet, lösche den Runner
|
||||
connect(&_worker, &QThread::finished, &_transmitter, &QObject::deleteLater);
|
||||
|
||||
// 5. Thread starten
|
||||
_worker.start();
|
||||
}
|
||||
|
||||
BCDataManager::~BCDataManager()
|
||||
{
|
||||
// nothing to do here for now,
|
||||
// our models are autokilled.
|
||||
|
||||
_worker.quit(); // Event Loop stoppen
|
||||
_worker.wait(); // Warten bis Thread wirklich fertig ist
|
||||
}
|
||||
|
||||
|
||||
void BCDataManager::createValueTypes()
|
||||
{
|
||||
/*
|
||||
Invalid = 0x0,
|
||||
"Text"
|
||||
"Number"
|
||||
"Float"
|
||||
"Percent"
|
||||
"KWh"
|
||||
"Watt"
|
||||
"Km"
|
||||
"Kmh"
|
||||
"Mm"
|
||||
"Sec"
|
||||
"SoC"
|
||||
"Odo"
|
||||
"Date"
|
||||
|
||||
*/
|
||||
//_valueTypes.insert( { BCDataType::TypeID::Invalid, "Invalid" } );
|
||||
|
||||
_valueTypes.insert( "Invalid", { BCDataType::TypeID::Invalid, "Invalid" } );
|
||||
_valueTypes.insert( "Text", { BCDataType::TypeID::Text } );
|
||||
_valueTypes.insert( "Number", { BCDataType::TypeID::Number } );
|
||||
|
||||
_valueTypes.insert( "Byte", { BCDataType::TypeID::Byte } );
|
||||
_valueTypes.insert( "Word", { BCDataType::TypeID::Word } );
|
||||
_valueTypes.insert( "Quad", { BCDataType::TypeID::Quad } );
|
||||
|
||||
_valueTypes.insert( "Float", { BCDataType::TypeID::Float, "", 1.5625} );
|
||||
_valueTypes.insert( "Percent",{ BCDataType::TypeID::Percent, "%", 1.5625 } );
|
||||
_valueTypes.insert( "KWh", { BCDataType::TypeID::KWh, "kwh", 1.5625 } );
|
||||
_valueTypes.insert( "Watt", { BCDataType::TypeID::Watt, "w", 1.5625 } );
|
||||
_valueTypes.insert( "Km", { BCDataType::TypeID::Km, "km", 1.5625 } );
|
||||
_valueTypes.insert( "Kmh", { BCDataType::TypeID::Kmh, "km/h", 0.1 } );
|
||||
_valueTypes.insert( "Mm", { BCDataType::TypeID::Mm, "mm", 1.5625 } );
|
||||
_valueTypes.insert( "Sec", { BCDataType::TypeID::Sec, "s", 1.5625 } );
|
||||
_valueTypes.insert( "SoC", { BCDataType::TypeID::SoC, "%", 1.5625 } );
|
||||
_valueTypes.insert( "Odo", { BCDataType::TypeID::Odo, "km", 1.5625 } );
|
||||
_valueTypes.insert( "Assist", { BCDataType::TypeID::Assist, "", 0 ,4 } );
|
||||
_valueTypes.insert( "Assist", { BCDataType::TypeID::AssistFac, "%" } );
|
||||
_valueTypes.insert( "Date", { BCDataType::TypeID::Date } );
|
||||
}
|
||||
|
||||
void BCDataManager::onCommandFinished(int id, bool success)
|
||||
{
|
||||
qDebug() << "[Manager] Command" << id << "finished. Success:" << success;
|
||||
}
|
||||
|
||||
void BCDataManager::onRunnerMessage(const QString &msg)
|
||||
{
|
||||
qDebug() << "[Worker says]:" << msg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BCDataManager::onSyncFromDevice()
|
||||
{
|
||||
qDebug() << " ---Syncing";
|
||||
if( _currentDeviceID != BCDevice::ID::Invalid )
|
||||
{
|
||||
if( _valueModels.contains(_currentDeviceID) )
|
||||
{
|
||||
|
||||
BCDataModel* model = _valueModels[_currentDeviceID];
|
||||
BCDataList& currentList = model->getValueList();
|
||||
|
||||
//BCData& value = currentList[4];
|
||||
|
||||
for( const BCData& value : currentList )
|
||||
{
|
||||
qDebug() << " --- value: " << value.label;
|
||||
|
||||
// statt '_transmitter.enqueueValueCommand( value )' entkoppeln
|
||||
// wir das eleganter über emit sendValueCommand()
|
||||
|
||||
//_transmitter.enqueueValueCommand( value );
|
||||
emit sendValueCommand( BC::OpID::ReadValue, &value);
|
||||
|
||||
emit valueTouched( value.rowInModel );
|
||||
|
||||
bc::processEventsFor(500);
|
||||
|
||||
//QApplication::processEvents();
|
||||
// Thread schlafen lassen (Simulation einer blockierenden Operation)
|
||||
//QThread::msleep(500);
|
||||
|
||||
}
|
||||
} // if contains
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<BCDataModel*> BCDataManager::getModel(BCDevice::ID deviceID )
|
||||
{
|
||||
if( _valueModels.contains( deviceID) )
|
||||
return _valueModels[deviceID];
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
BCTransmitter* BCDataManager::getTransmitter()
|
||||
{
|
||||
return &_transmitter;
|
||||
};
|
||||
|
||||
void BCDataManager::loadBikeData()
|
||||
{
|
||||
auto printAttrs = [](const QXmlStreamReader& xml)
|
||||
{
|
||||
QStringList parts;
|
||||
for (const auto &attr : xml.attributes()) {
|
||||
parts << attr.name().toString() + "=\"" + attr.value().toString() + "\"";
|
||||
}
|
||||
qDebug().noquote() << parts.join(" ");
|
||||
};
|
||||
/*
|
||||
QString fileName = QFileDialog::getOpenFileName(this, "XML öffnen", "", "XML Files (*.xml)");
|
||||
if (fileName.isEmpty())
|
||||
return;
|
||||
*/
|
||||
|
||||
QFile file(":/bikeinfo.xml");
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
{
|
||||
// __fix throw
|
||||
QMessageBox::warning(nullptr, "Fehler", "Datei konnte nicht geöffnet werden.");
|
||||
return;
|
||||
}
|
||||
|
||||
_xml.setDevice(&file);
|
||||
|
||||
if (_xml.readNextStartElement())
|
||||
{
|
||||
if (_xml.name() != "Bike"_L1 )
|
||||
// fix throw
|
||||
_xml.raiseError(QObject::tr("The file is not an 'Bike' file."));
|
||||
}
|
||||
// ??
|
||||
Q_ASSERT(_xml.isStartElement() && _xml.name() == "Bike"_L1);
|
||||
|
||||
while (!_xml.atEnd() && !_xml.hasError())
|
||||
{
|
||||
QXmlStreamReader::TokenType token = _xml.readNext();
|
||||
if (token == QXmlStreamReader::StartElement)
|
||||
{
|
||||
QString deviceType = _xml.attributes().value("Type"_L1).toString();
|
||||
printAttrs (_xml);
|
||||
const char* deviceKey = _xml.attributes().value("Type"_L1).toLatin1().constData();
|
||||
auto deviceID = _bcDeviceEnum.keyToValue64(deviceKey);
|
||||
//_currentDeviceID = BCDevice::ID( deviceID.value_or( BCDevice::ID::Invalid ) );
|
||||
if(deviceID.has_value())
|
||||
{
|
||||
qDebug() << " --- Device: " << _xml.name() << ": " << deviceType << " : " << deviceID;
|
||||
|
||||
_currentDeviceID = BCDevice::ID( deviceID.value() );
|
||||
BCDataList parsedValues;
|
||||
loadDeviceData( parsedValues );
|
||||
if( parsedValues.count() )
|
||||
{
|
||||
BCDataModel* valueModel = new BCDataModel( this );
|
||||
valueModel->setValueList(parsedValues);
|
||||
_valueModels.insert( _currentDeviceID, valueModel );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_currentDeviceID = BCDevice::ID::Console;
|
||||
}
|
||||
|
||||
/*
|
||||
if (xml.hasError())
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Parsing Fehler", xml.errorString());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_model->setDevices(parsedValues);
|
||||
}
|
||||
*/
|
||||
|
||||
// create & add new model to the model map
|
||||
|
||||
}
|
||||
|
||||
void BCDataManager::loadDeviceData( BCDataList& parsedValues )
|
||||
{
|
||||
auto printAttrs = [](const QXmlStreamReader& xml)
|
||||
{
|
||||
QStringList parts;
|
||||
for (const auto &attr : xml.attributes()) {
|
||||
parts << attr.name().toString() + "=\"" + attr.value().toString() + "\"";
|
||||
}
|
||||
qDebug().noquote() << parts.join(" ");
|
||||
};
|
||||
|
||||
printAttrs (_xml);
|
||||
Q_ASSERT(_xml.isStartElement() && _xml.name() == "Device"_L1);
|
||||
qDebug() << " ---------------";
|
||||
|
||||
|
||||
//while (!_xml.atEnd() && !_xml.hasError())
|
||||
while( _xml.readNextStartElement() )
|
||||
{
|
||||
if( _xml.attributes().hasAttribute(BCTags::ID) )
|
||||
{
|
||||
//qDebug() << " --- found: " << _xml.name() << " : " << _xml.attributes().value(BCTags::ID);
|
||||
|
||||
QString id = _xml.attributes().value(BCTags::ID).toString();
|
||||
BCDataParams params
|
||||
{
|
||||
.ID = id,
|
||||
.Label = _xml.attributes().value(BCTags::Label).toString(),
|
||||
.Default = _xml.attributes().value(BCTags::Default).toString(),
|
||||
.UnitType = _xml.attributes().value(BCTags::UnitType).toString(),
|
||||
};
|
||||
|
||||
// __fix! können ungültige werte erzeugt werden ?
|
||||
//BCData newValue = BCData::makeValue( _currentDeviceID, params );
|
||||
//if(newValue)
|
||||
// parsedValues.push_back( newValue );
|
||||
std::optional<BCData> newValue = makeValue( _currentDeviceID, params );
|
||||
if(newValue)
|
||||
parsedValues.push_back( *newValue );
|
||||
}
|
||||
|
||||
//printAttrs (_xml);
|
||||
_xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<BCData> BCDataManager::makeValue( BCDevice::ID deviceID, const BCDataParams& params )
|
||||
{
|
||||
|
||||
/*
|
||||
auto setIfExists = [&]( QStringView source, optDouble& target )
|
||||
{
|
||||
if( !source.isEmpty() )
|
||||
{
|
||||
bool ok;
|
||||
double testVal = source.toDouble(&ok);
|
||||
if (ok)
|
||||
target = testVal;
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
static QMetaEnum s_bcValueEnum{QMetaEnum::fromType<BC::ID>()};
|
||||
|
||||
/*
|
||||
Wir brauchen:
|
||||
- einen gültige ID String um die enum ID herauszufinden.
|
||||
- einen gültige UnitType String um den ValueType herauszufinden.
|
||||
|
||||
*/
|
||||
|
||||
std::optional<BCData> newValue;
|
||||
|
||||
std::optional<quint64> IDVal = s_bcValueEnum.keyToValue64( params.ID.toLatin1().constData() );
|
||||
if( IDVal.has_value() )
|
||||
{
|
||||
if( _valueTypes.contains( params.UnitType ) )
|
||||
{
|
||||
|
||||
const BCDataType& valueType = _valueTypes[params.UnitType];
|
||||
newValue = BCData( valueType, deviceID, static_cast<BC::ID>(IDVal.value()) );
|
||||
|
||||
/*
|
||||
setIfExists( params.Factor, newValue.factor );
|
||||
setIfExists( params.Min, newValue.min );
|
||||
setIfExists( params.Max, newValue.max );
|
||||
*/
|
||||
newValue->label = params.Label;
|
||||
newValue->defaultValue = params.Default;
|
||||
/*
|
||||
|
||||
//qDebug() << " --- created: " << params.Label;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
// --- NEU: Speichern mit QXmlStreamWriter ---
|
||||
void BCDataManager::saveBikeData()
|
||||
{
|
||||
/*
|
||||
QString fileName = QFileDialog::getSaveFileName(this, "XML speichern", "", "XML Files (*.xml)");
|
||||
if (fileName.isEmpty()) return;
|
||||
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::warning(this, "Fehler", "Datei konnte nicht zum Schreiben geöffnet werden.");
|
||||
return;
|
||||
}
|
||||
|
||||
QXmlStreamWriter xml(&file);
|
||||
xml.setAutoFormatting(true); // Sorgt für schöne Einrückungen (Tabs/Spaces)
|
||||
xml.writeStartDocument();
|
||||
xml.writeStartElement("devices");
|
||||
|
||||
// Daten vom Model holen
|
||||
const QList<Device> &devices = m_model->getDevices();
|
||||
|
||||
for (const Device &d : devices) {
|
||||
xml.writeStartElement("device");
|
||||
xml.writeAttribute("name", d.name);
|
||||
xml.writeAttribute("ip", d.ip);
|
||||
xml.writeEndElement(); // </device>
|
||||
}
|
||||
|
||||
xml.writeEndElement(); // </devices>
|
||||
xml.writeEndDocument();
|
||||
|
||||
// Prüfen ob alles geschrieben wurde
|
||||
if (file.error() != QFile::NoError) {
|
||||
QMessageBox::critical(this, "Fehler", "Fehler beim Schreiben der Datei.");
|
||||
} else {
|
||||
statusBar()->showMessage("Datei erfolgreich gespeichert!", 3000);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user