diff --git a/bc.h b/bc.h index 49b166a..4f1b007 100644 --- a/bc.h +++ b/bc.h @@ -73,15 +73,6 @@ namespace bc [[maybe_unused]] constexpr static double NORMALIZED_VOLTAGE_OFFSET = 20.8333; [[maybe_unused]] constexpr static double NORMALIZED_VOLTAGE_FAKTOR = 0.416667; - // misc - //#define cbc::Version "CanBusControl 0.0.01 / 02.07.2022" - [[maybe_unused]] constexpr static const char* Version = "BionxControl 0.1.00 / 08.11.2022 © 2022 chris@sourceworx.org"; - - [[maybe_unused]] constexpr static const char* OrgName = "source::worx"; - [[maybe_unused]] constexpr static const char* DomainName = "sourceworx.org"; - [[maybe_unused]] constexpr static const char* AppName = "BionxControl"; - - // timer void delay_seconds( uint32_t ); void delay_millis( uint32_t ); @@ -91,27 +82,6 @@ namespace bc QString formatInt( int count, int len ); } // namespace bc -// abbreviations: -// SOC = State Of Charge -// LMD = Last Measured Discharge -// NIP = ? - -/* - -Needed ? -#include - -template -constexpr auto to_u(E e) noexcept { - return static_cast>(e); -} - -// constants.h -#pragma once -#include - - -*/ struct BC { @@ -775,6 +745,7 @@ namespace BCTags inline constexpr auto Label = "Label"_L1; inline constexpr auto UnitLabel = "UnitLabel"_L1; inline constexpr auto IsWord = "IsWord"_L1; + inline constexpr auto ReadOnly = "ReadOnly"_L1; inline constexpr auto Default = "Default"_L1; inline constexpr auto Current = "Current"_L1; diff --git a/bcanimateddelegate.cpp b/bcanimateddelegate.cpp index eaaef64..6397b8f 100644 --- a/bcanimateddelegate.cpp +++ b/bcanimateddelegate.cpp @@ -202,13 +202,18 @@ void BCAnimatedDelegate::paint(QPainter *painter, const QStyleOptionViewItem& op case 1: if(_rowOpacities.contains(row)) - paintHighlightRow(painter,option,index); + paintHighlightRow(painter,option,index.row()); break; case 2: if( row>-1 && row <= _valueList.size() ) - paintSliderIndicator(painter,option,index); + { + const BCValue& bcValue = *(_valueList[ index.row()].get()); + qDebug() << " --- paintSLider: " << bcValue.label << " type: " << (int)bcValue.valueType << " flags:" << bcValue.valueFlags.toInt() << " RO: " << bcValue.isReadOnly(); + if( !bcValue.isReadOnly()) + paintSliderIndicator(painter,option,bcValue); + } default: break; @@ -216,35 +221,39 @@ void BCAnimatedDelegate::paint(QPainter *painter, const QStyleOptionViewItem& op } -void BCAnimatedDelegate::paintHighlightRow(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +void BCAnimatedDelegate::paintHighlightRow(QPainter* painter, const QStyleOptionViewItem& option, int row) const { painter->save(); painter->setRenderHint(QPainter::Antialiasing); - int row = index.row(); qreal opacity =_rowOpacities.value(row); painter->setOpacity(opacity); // Margin von 4px QRect itemRect = option.rect.adjusted(3, 3, -3, -3); // Border (2px solid #2196F3) - QPen borderPen( Qt::red, 1); + // oranger rahmen + QPen borderPen( QColor(0xFF8C00), 1); painter->setPen(borderPen); painter->setBrush(Qt::NoBrush); + + // highlight background + //QColor highlightColor = option.palette.highlight().color(); + //highlightColor.setAlphaF(0.3); // 0.0 bis 1.0 (float ist oft lesbarer) + //painter->fillRect(option.rect, highlightColor); + painter->drawRoundedRect(itemRect, 2, 2); painter->restore(); } -void BCAnimatedDelegate::paintSliderIndicator(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +void BCAnimatedDelegate::paintSliderIndicator(QPainter* painter, const QStyleOptionViewItem& option, const BCValue& bcValue) const { - const BCValue& bcValue = *(_valueList[ index.row()].get()); - qDebug() << " --- paintSLider: " << bcValue.label << ": " << (int)bcValue.valueType; // wenn Werte readOnly sind, dann brauchen keinen EditHint - if( bcValue.flags.testFlag(BCValue::Flag::ReadOnly) ) + //if( bcValue.valueFlags.testFlag(BCValue::Flag::ReadOnly) ) // || bcValue.valueType == BCValue::ValueType::Plain ) - return; + // return; // Hintergrund if (option.state & QStyle::State_Selected) diff --git a/bcanimateddelegate.h b/bcanimateddelegate.h index 3751d50..abea958 100644 --- a/bcanimateddelegate.h +++ b/bcanimateddelegate.h @@ -75,8 +75,8 @@ signals: protected: void updateRow(int row); - void paintHighlightRow(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; - void paintSliderIndicator(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + void paintHighlightRow(QPainter* painter, const QStyleOptionViewItem& option, int row) const; + void paintSliderIndicator(QPainter* painter, const QStyleOptionViewItem& option, const BCValue& bcValue) const; // Das ist ein Quickhack, der Delegate sollte // nichts über die Originaldaten wissen. Die diff --git a/bcdeviceview.cpp b/bcdeviceview.cpp index ef14c9b..ec75083 100644 --- a/bcdeviceview.cpp +++ b/bcdeviceview.cpp @@ -71,6 +71,17 @@ const BCValueList& BCDeviceView::getValueListX() } +/** + * @brief Flag, ob diese View schonmal angezeigt wurde. + */ + +bool BCDeviceView::firstExpose() +{ + bool stored = _firstExpose; + _firstExpose = false; + return stored; +} + /** * @brief SLOT, der aufgerufen wird, wenn die ValueList vom XML-Lader fertig geladen wurde. * Die DeviceView nimmt die ValueList dann in Besitz. @@ -93,6 +104,10 @@ void BCDeviceView::updateValue(int index, BCValue::Flag state, uint32_t rawValue _itemDelegate->onHighlightRow( index ); } +/** + * @brief Die Spalte mit dem Label soll immer bei 60% der Gesamtbreite liegen. + * @param event + */ void BCDeviceView::resizeEvent(QResizeEvent *event) { diff --git a/bcdeviceview.h b/bcdeviceview.h index f32aefe..28885b0 100644 --- a/bcdeviceview.h +++ b/bcdeviceview.h @@ -54,7 +54,7 @@ public: const BCValueList& getValueListX(); - bool hasContent(); + bool firstExpose(); void updateValue( int index, BCValue::Flag state, uint32_t rawValue ); @@ -66,10 +66,12 @@ protected: void resizeEvent(QResizeEvent *event) override; + bool _firstExpose{true}; BCDevice::ID _devideID{BCDevice::ID::Invalid}; BCValueModel _valueModel; BCAnimatedDelegate* _itemDelegate{}; + }; #endif // BCDEVICEVIEW_H diff --git a/bcguihelpers.cpp b/bcguihelpers.cpp index 356be06..d541a7c 100644 --- a/bcguihelpers.cpp +++ b/bcguihelpers.cpp @@ -136,7 +136,7 @@ void BCDriverStateWidget::updateStyle() // FLUENT GRAY (Neutral) // Wir machen es dunkelgrau mit hellem Rand -> "Ausgeschaltet"-Look ledStyle = "background-color: #3B3B3B; border: 1px solid #606060;"; - toolTipText = "Treiber nicht geladen."; + toolTipText = "Kein Treiber geladen."; break; case BCDriver::DriverState::Error: @@ -149,9 +149,9 @@ void BCDriverStateWidget::updateStyle() case BCDriver::DriverState::Loaded: case BCDriver::DriverState::Initialized: case BCDriver::DriverState::Opened: - // FLUENT RED (Critical) + // ORANGE ledStyle = "background-color: #FF8C00; border: 1px solid #A80000;"; - toolTipText = "Fehler beim Laden des Treibers."; + toolTipText = "Kein Gerät verbunden."; break; case BCDriver::DriverState::DeviceReady: diff --git a/bcmainwindow.cpp b/bcmainwindow.cpp index 1e755a5..990f354 100644 --- a/bcmainwindow.cpp +++ b/bcmainwindow.cpp @@ -31,6 +31,7 @@ #include #include +#include #include "qassert.h" #include @@ -139,16 +140,27 @@ void BCMainWindow::initMainWindow() _transmitter.moveToThread(&_worker); _worker.start(); - // die Daten des eBikes laden - _dataManager.loadXmlBikeData(":/bikeinfo.xml"_L1); + try + { + // die Daten des eBikes laden + _dataManager.loadXmlBikeData(":/bikeinfo.xml"_L1); + } + catch( BCException& exception ) + { + QMessageBox::critical( this, "Ladefehler", exception.what() ); + } + + // Konsolendaten als erstes anzeigen _consoleAction->trigger(); //_batteryAction->trigger(); + /* // Dummy sync beim starten QTimer::singleShot(1000, this, [this]() { onSyncDeviceView(); }); + */ } @@ -265,8 +277,15 @@ void BCMainWindow::onShowDevicePanel( BCDevice::ID deviceID ) { _currentPanel = nxtPanel; setHeaderLabel( _currentPanel->property( cBCKeyHeaderLabel ).toString() ); - _stackedWidget->setCurrentWidget( nxtPanel ); - + _stackedWidget->setCurrentWidget( _currentPanel ); + if( _currentPanel->firstExpose() ) + { + // Dummy sync beim starten + QTimer::singleShot(1000, this, [this]() + { + onSyncDeviceView(); + }); + } // knopf auch abschalten? } } @@ -312,7 +331,7 @@ void BCMainWindow::onSyncDeviceView() { qDebug() << " --- ### begin sync of value: " << QThread::currentThreadId() << " : " << value->label; // wir setzen auf 'lesen' - value->flags.setFlag( BCValue::Flag::ReadMe ); + value->valueFlags.setFlag( BCValue::Flag::ReadMe ); _syncButton->setEnabled( false ); diff --git a/bctransmitter.cpp b/bctransmitter.cpp index 652b316..dbb374b 100644 --- a/bctransmitter.cpp +++ b/bctransmitter.cpp @@ -179,7 +179,6 @@ void BCTransmitter::onProcessValue() // Value ist 'under construction' //emit valueUpdated( value.deviceID, value.indexRow, BCValue::Flag::Locked ); - uint32_t devID = static_cast(value.deviceID); uint8_t regID = static_cast (value.registerID); @@ -187,7 +186,7 @@ void BCTransmitter::onProcessValue() uint32_t newValue = value.rawValue; BCValue::Flag newState = BCValue::Flag::Failed;; - if(value.flags.testFlag( BCValue::Flag::WriteMe ) ) + if(value.valueFlags.testFlag( BCValue::Flag::WriteMe ) ) { @@ -195,7 +194,7 @@ void BCTransmitter::onProcessValue() // oder sollen wir hier beides erlauben ? readFlag & writeFlag ? // Was kommt dann zuerst? Schreiben und lesen als verify ? - else if( value.flags.testFlag( BCValue::Flag::ReadMe ) ) + else if( value.valueFlags.testFlag( BCValue::Flag::ReadMe ) ) { // wir sind hier im anderen thread! nicht einfach so reinschreiben, nur lesen TransmitResult result = value.isWord() ? readWordValue( devID, regID ) : readByteValue( devID, regID ); diff --git a/bcvalue.cpp b/bcvalue.cpp index d9334b8..b60cb59 100644 --- a/bcvalue.cpp +++ b/bcvalue.cpp @@ -55,9 +55,15 @@ QString BCValue::formatValue() const bool BCValue::isWord() const { - return flags.testFlag(BCValue::Flag::IsWord); + return valueFlags.testFlag(BCValue::Flag::IsWord); } +bool BCValue::isReadOnly() const +{ + return valueFlags.testFlag(BCValue::Flag::ReadOnly); +} + + double BCValue::calcRatio() const { return 0.33; @@ -88,7 +94,7 @@ void BCValue::dumpValue() const qDebug() << "DeviceID: " << deviceID << " Register: " << registerID << " state:" " << state << " << " label: " << label; qDebug() << "formattedValue: " << formatValue() << " min: " << optMin << " max: " << optMax << " factor: " << factor << " ValueType: " << (char)valueType << " "; - qDebug() << "indexRow: " << indexRow << " isWord: " << isWord(); + qDebug() << "indexRow: " << indexRow << " isWord: " << isWord() << " isRO: " << isReadOnly(); qDebug(); } diff --git a/bcvalue.h b/bcvalue.h index 2645772..cd97b99 100644 --- a/bcvalue.h +++ b/bcvalue.h @@ -98,8 +98,9 @@ public: double calcRatio() const; void dumpValue() const; bool isWord() const; + bool isReadOnly() const; - mutable Flags flags{BCValue::Flag::ReadOnly}; + mutable Flags valueFlags{BCValue::Flag::NoFlag}; BCDevice::ID deviceID{BCDevice::ID::Invalid}; BC::ID registerID{BC::ID::Invalid}; ValueType valueType{ValueType::Plain}; @@ -113,19 +114,12 @@ public: }; Q_DECLARE_OPERATORS_FOR_FLAGS(BCValue::Flags) - - -//Q_DECLARE_METATYPE(const BCValue&) - using BCValuePtr = std::shared_ptr; using BCValuePtrConst = std::shared_ptr; - //using BCValueList = QList; using BCValueList = QList; Q_DECLARE_METATYPE(const BCValuePtr) - - Q_DECLARE_METATYPE(BCValueList) diff --git a/bcvaluemodel.cpp b/bcvaluemodel.cpp index 84babdc..8be7996 100644 --- a/bcvaluemodel.cpp +++ b/bcvaluemodel.cpp @@ -87,7 +87,8 @@ void BCValueModel::updateValue(int row, BCValue::Flag state, uint32_t rawValue ) { const BCValue& value = *(_valueList[row].get()); - value.flags = state; + // Obacht hier! + //value.valueFlags = state; value.rawValue = rawValue; QModelIndex idx1 = index(row,1); diff --git a/bcxmlloader.cpp b/bcxmlloader.cpp index 8fc4280..5679f2b 100644 --- a/bcxmlloader.cpp +++ b/bcxmlloader.cpp @@ -54,7 +54,8 @@ void BCXmlLoader::loadXmlBikeData( const QString& fileName ) auto printAttrs = [](const QXmlStreamReader& xml) { QStringList parts; - for (const auto &attr : xml.attributes()) { + for (const auto &attr : xml.attributes()) + { parts << attr.name().toString() + "=\"" + attr.value().toString() + "\""; } qDebug().noquote() << parts.join(" "); @@ -64,19 +65,14 @@ void BCXmlLoader::loadXmlBikeData( const QString& fileName ) QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) - { - // __fix throw - QMessageBox::warning(nullptr, "Fehler", "Datei konnte nicht geöffnet werden."); - return; - } + throw BCException( "Fehler", "Datei konnte nicht geöffnet werden."); _xml.setDevice(&file); if (_xml.readNextStartElement()) { if (_xml.name() != "Bike"_L1 ) - // fix throw - _xml.raiseError(QObject::tr("The file is not an 'Bike' file.")); + throw BCException( "Fehler", "Falsches Datenformat."); } // ?? Q_ASSERT(_xml.isStartElement() && _xml.name() == "Bike"_L1); @@ -93,26 +89,22 @@ void BCXmlLoader::loadXmlBikeData( const QString& fileName ) // Wir wollen die Device-ID aus dem XML Tag ermitteln if( deviceType.isEmpty() ) - { - printAttrs (_xml); continue; - } - QByteArray byteArray = deviceType.toUtf8(); - const char* deviceKey = byteArray.constData(); - bool ok=false; - auto optDeviceID = bcDeviceEnum.keyToValue(deviceKey,&ok); - //_currentDeviceID = BCDevice::ID( deviceID.value_or( BCDevice::ID::Invalid ) ); - //if( optDeviceID.has_value()) - if(ok) - { - qDebug() << " --- FETCH 2: Device: " << deviceType << " : " << optDeviceID; - //BCDevice::ID currentDeviceID = BCDevice::ID( optDeviceID.value() ); - BCDevice::ID currentDeviceID = BCDevice::ID( optDeviceID ); - loadXmlBikeDeviceData(currentDeviceID); - } - } // if start element - } + QByteArray byteArray = deviceType.toUtf8(); + const char* deviceKey = byteArray.constData(); + bool ok=false; + auto optDeviceID = bcDeviceEnum.keyToValue(deviceKey,&ok); + //_currentDeviceID = BCDevice::ID( deviceID.value_or( BCDevice::ID::Invalid ) ); + //if( optDeviceID.has_value()) + if(!ok) + throw BCException( "Fehler", QString("Devicetype %1 existiert nicht.").arg(deviceType) ); + qDebug() << " --- FETCH 2: Device: " << deviceType << " : " << optDeviceID; + //BCDevice::ID currentDeviceID = BCDevice::ID( optDeviceID.value() ); + BCDevice::ID currentDeviceID = BCDevice::ID( optDeviceID ); + loadXmlBikeDeviceData(currentDeviceID); + } // if startElement + } // end while } @@ -126,9 +118,8 @@ void BCXmlLoader::loadXmlBikeDeviceData(BCDevice::ID deviceID) auto printAttrs = [](const QXmlStreamReader& xml) { QStringList parts; - for (const auto &attr : xml.attributes()) { + for (const auto &attr : xml.attributes()) parts << attr.name().toString() + "=\"" + attr.value().toString() + "\""; - } qDebug().noquote() << parts.join(" "); }; @@ -154,6 +145,7 @@ void BCXmlLoader::loadXmlBikeDeviceData(BCDevice::ID deviceID) .Min = _xml.attributes().value(BCTags::Min).toString(), .Max = _xml.attributes().value(BCTags::Max).toString(), .IsWord = _xml.attributes().value(BCTags::IsWord).toString(), + .ReadOnly = _xml.attributes().value(BCTags::ReadOnly).toString(), .ValueType = _xml.attributes().value(BCTags::ValueType).toString(), }; @@ -193,7 +185,7 @@ std::optional BCXmlLoader::makeValue( BCDevice::ID deviceID, const B { "Float", BCValue::ValueType::Float } }; - auto setIfExists = [&]( QStringView source, T& target ) + auto setIfExists = [&]( T& target, QStringView source ) { if( !source.isEmpty() ) { @@ -216,29 +208,36 @@ std::optional BCXmlLoader::makeValue( BCDevice::ID deviceID, const B int IDVal = s_bcValueEnum.keyToValue( byteArray.constData(), &ok ); qDebug() << " --- should create: " << params.Label; //if( IDVal.has_value() ) - if( ok ) - { - BCValuePtr newValuePtr = std::make_shared( deviceID, static_cast(IDVal) ); - BCValue& newValue = *newValuePtr.get(); + if( !ok ) + throw BCException( "Fehler", QString("Devicetype %1 existiert nicht.").arg(params.ID) ); - setIfExists( params.Factor, newValue.factor ); - setIfExists( params.Min, newValue.optMin ); - setIfExists( params.Max, newValue.optMax ); - //setIfExists( params.IsWord, newValue.isWord ); + BCValuePtr newValuePtr = std::make_shared( deviceID, static_cast(IDVal) ); + BCValue& newValue = *newValuePtr.get(); - newValue.label = params.Label; - newValue.unitLabel = params.UnitLabel; + // ValueType + if( !s_valueTypes.contains( params.ValueType ) ) + throw BCException( "Fehler", QString("ValueType %1 existiert nicht.").arg(params.ValueType) ); - if( s_valueTypes.contains( params.ValueType ) ) - newValue.valueType = s_valueTypes[params.ValueType]; + newValue.valueType = s_valueTypes[params.ValueType]; - qDebug() << " --- created: " << params.Label; - newValue.dumpValue(); + newValue.label = params.Label; + newValue.unitLabel = params.UnitLabel; - return std::optional( newValuePtr ); - } + setIfExists( newValue.factor, params.Factor ); + setIfExists( newValue.optMin, params.Min ); + setIfExists( newValue.optMax, params.Max ); + + if( params.IsWord == "true") + newValue.valueFlags.setFlag( BCValue::Flag::IsWord, true ); + + if( params.ReadOnly == "true") + newValue.valueFlags.setFlag( BCValue::Flag::ReadOnly, true ); + + qDebug() << " --- created: " << params.Label; + newValue.dumpValue(); + + return std::optional( newValuePtr ); - return std::nullopt; } diff --git a/bcxmlloader.h b/bcxmlloader.h index 80b69f9..dd7a65e 100644 --- a/bcxmlloader.h +++ b/bcxmlloader.h @@ -66,6 +66,7 @@ protected: QString Min; QString Max; QString IsWord; + QString ReadOnly; QString ValueType; }; diff --git a/resources/bikeinfo.xml b/resources/bikeinfo.xml index 694aab3..86cbb10 100644 --- a/resources/bikeinfo.xml +++ b/resources/bikeinfo.xml @@ -2,48 +2,48 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + - - + + - - + + - - - + + + - + - + - - - - - + + + +