262 lines
7.3 KiB
C++
262 lines
7.3 KiB
C++
/***************************************************************************
|
|
|
|
BionxControl
|
|
© 2025 -2026 christoph holzheuer
|
|
christoph.holzheuer@gmail.com
|
|
|
|
Using:
|
|
|
|
mhs_can_drv.c
|
|
© 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany
|
|
Klaus Demlehner, klaus@mhs-elektronik.de
|
|
@see www.mhs-elektronik.de
|
|
|
|
Based on Bionx data type descriptions from:
|
|
|
|
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
|
|
|
|
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 <QMessageBox>
|
|
#include <QMetaEnum>
|
|
#include <QHash>
|
|
|
|
#include <bcxmlloader.h>
|
|
|
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
BCXmlLoader::BCXmlLoader(QObject *parent)
|
|
: QObject(parent)
|
|
{
|
|
//qRegisterMetaType<BCValue*>("BCValue*");
|
|
//qRegisterMetaType<BCValue*>();
|
|
}
|
|
|
|
|
|
void BCXmlLoader::loadXmlBikeData( const QString& fileName )
|
|
{
|
|
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;
|
|
*/
|
|
|
|
QMetaEnum bcDeviceEnum{QMetaEnum::fromType<BCDevice::ID>()};
|
|
|
|
QFile file(fileName);
|
|
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);
|
|
// Wir wollen die Device-ID aus dem XML Tag ermitteln
|
|
const char* deviceKey = _xml.attributes().value("Type"_L1).toLatin1().constData();
|
|
bool ok;
|
|
auto optDeviceID = bcDeviceEnum.keyToValue(deviceKey,&ok);
|
|
//_currentDeviceID = BCDevice::ID( deviceID.value_or( BCDevice::ID::Invalid ) );
|
|
//if( optDeviceID.has_value())
|
|
if(ok)
|
|
{
|
|
qDebug() << " --- Device: " << _xml.name() << ": " << deviceType << " : " << optDeviceID;
|
|
//BCDevice::ID currentDeviceID = BCDevice::ID( optDeviceID.value() );
|
|
BCDevice::ID currentDeviceID = BCDevice::ID( optDeviceID );
|
|
loadXmlBikeDeviceData(currentDeviceID);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (xml.hasError())
|
|
{
|
|
QMessageBox::critical(nullptr, "Parsing Fehler", xml.errorString());
|
|
}
|
|
else
|
|
{
|
|
m_model->setDevices(parsedValues);
|
|
}
|
|
*/
|
|
|
|
// create & add new model to the model map
|
|
|
|
}
|
|
|
|
void BCXmlLoader::loadXmlBikeDeviceData(BCDevice::ID deviceID)
|
|
{
|
|
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() << " XXX ---------------";
|
|
|
|
// temporäre Wertliste für neues Device
|
|
BCValueList currentValues;
|
|
|
|
while( _xml.readNextStartElement() )
|
|
{
|
|
if( _xml.attributes().hasAttribute(BCTags::ID) )
|
|
{
|
|
//qDebug() << " --- found: " << _xml.name() << " : " << _xml.attributes().value(BCTags::ID);
|
|
|
|
// füllen des Parameter packs
|
|
BCValueParams params
|
|
{
|
|
.ID = _xml.attributes().value(BCTags::ID).toString(),
|
|
.Label = _xml.attributes().value(BCTags::Label).toString(),
|
|
.UnitLabel = _xml.attributes().value(BCTags::UnitLabel).toString(),
|
|
.Factor = _xml.attributes().value(BCTags::Factor).toString(),
|
|
.Min = _xml.attributes().value(BCTags::Min).toString(),
|
|
.Max = _xml.attributes().value(BCTags::Max).toString(),
|
|
.IsWord = _xml.attributes().value(BCTags::IsWord).toString(),
|
|
.ValueType = _xml.attributes().value(BCTags::ValueType).toString(),
|
|
};
|
|
|
|
// nur gültige Werte sind vorhanden und können gespeichert werden
|
|
std::optional<BCValuePtr> newValue = makeValue( deviceID, params );
|
|
if(newValue)
|
|
{
|
|
// wir merken uns gleich den index in der Werteliste
|
|
(*newValue)->indexRow = currentValues.size();
|
|
currentValues.push_back( *newValue );
|
|
}
|
|
|
|
}
|
|
// weiter zum nächsten Element
|
|
_xml.skipCurrentElement();
|
|
}
|
|
|
|
// Wenn dieses Device fertig geladen wurde, soll das MainWindow es abholen
|
|
if( !currentValues.isEmpty() )
|
|
emit valueListReady( deviceID, currentValues );
|
|
|
|
}
|
|
|
|
std::optional<BCValuePtr> BCXmlLoader::makeValue( BCDevice::ID deviceID, const BCValueParams& params )
|
|
{
|
|
|
|
static QHash<QString,BCValue::ValueType> s_valueTypes
|
|
{
|
|
{ "Plain", BCValue::ValueType::Plain },
|
|
{ "Bool", BCValue::ValueType::Bool },
|
|
{ "Number", BCValue::ValueType::Number },
|
|
{ "Float", BCValue::ValueType::Float }
|
|
};
|
|
|
|
auto setIfExists = [&]<typename T>( QStringView source, T& target )
|
|
{
|
|
if( !source.isEmpty() )
|
|
{
|
|
bool ok;
|
|
double testVal = source.toDouble(&ok);
|
|
if (ok)
|
|
target = testVal;
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
Wir brauchen:
|
|
- einen gültige ID String um die enum ID herauszufinden.
|
|
- einen gültige UnitType String um den ValueType herauszufinden.
|
|
|
|
*/
|
|
|
|
// geht nicht auf qt6.8 von debian trixie
|
|
//std::optional<quint64> IDVal = s_bcValueEnum.keyToValue64( params.ID.toLatin1().constData() );
|
|
bool ok;
|
|
static QMetaEnum s_bcValueEnum{QMetaEnum::fromType<BC::ID>()};
|
|
int IDVal = s_bcValueEnum.keyToValue( params.ID.toLatin1().constData(), &ok );
|
|
qDebug() << " --- should create: " << params.Label;
|
|
//if( IDVal.has_value() )
|
|
if( ok )
|
|
{
|
|
BCValuePtr newValuePtr = std::make_shared<BCValue>( deviceID, static_cast<BC::ID>(IDVal) );
|
|
BCValue& newValue = *newValuePtr.get();
|
|
|
|
setIfExists( params.Factor, newValue.factor );
|
|
setIfExists( params.Min, newValue.min );
|
|
setIfExists( params.Max, newValue.max );
|
|
setIfExists( params.IsWord, newValue.isWord );
|
|
|
|
newValue.label = params.Label;
|
|
newValue.unitLabel = params.UnitLabel;
|
|
|
|
if( s_valueTypes.contains( params.ValueType ) )
|
|
newValue.valueType = s_valueTypes[params.ValueType];
|
|
|
|
/*
|
|
QString ID;
|
|
QString Label;
|
|
QString UnitLabel;
|
|
QString Factor;
|
|
QString Min;
|
|
QString Max;
|
|
QString IsWord;
|
|
QString ValueType;
|
|
*/
|
|
|
|
qDebug() << " --- created: " << params.Label;
|
|
newValue.dumpValue();
|
|
|
|
return std::optional<BCValuePtr>( newValuePtr );
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
|
|
|
|
|
|
|