Files
BionxControl/bcmainwindow.cpp

275 lines
8.3 KiB
C++
Raw Normal View History

2025-12-15 20:57:09 +01:00
/***************************************************************************
2025-12-26 14:07:55 +01:00
BionxControl
2026-01-03 23:51:14 +01:00
© 2025 -2026 christoph holzheuer
2025-12-26 14:07:55 +01:00
christoph.holzheuer@gmail.com
2025-12-15 20:57:09 +01:00
2025-12-26 14:07:55 +01:00
Using:
2025-12-15 20:57:09 +01:00
2025-12-26 14:07:55 +01:00
mhs_can_drv.c
© 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany
Klaus Demlehner, klaus@mhs-elektronik.de
@see www.mhs-elektronik.de
2025-12-15 20:57:09 +01:00
2025-12-26 14:07:55 +01:00
Based on Bionx data type descriptions from:
2025-12-15 20:57:09 +01:00
2025-12-26 14:07:55 +01:00
BigXionFlasher USB V 0.2.4 rev. 97
© 2011-2013 by Thomas Koenig <info@bigxionflasher.org>
@see www.bigxionflasher.org
2025-12-15 20:57:09 +01:00
2025-12-26 14:07:55 +01:00
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
***************************************************************************/
2025-12-15 20:57:09 +01:00
2026-01-01 02:40:12 +01:00
#include <QTimer>
2025-12-29 15:44:06 +01:00
2025-12-28 20:18:28 +01:00
#include "qassert.h"
2025-12-15 20:57:09 +01:00
#include <bcmainwindow.h>
2025-12-29 23:29:56 +01:00
#include <bcanimateddelegate.h>
2025-12-15 20:57:09 +01:00
#include <ui_bcmainwindow.h>
2026-01-03 23:51:14 +01:00
#include <bcguihelpers.h>
2025-12-19 21:20:14 +01:00
2025-12-26 14:07:55 +01:00
/**
* @brief Das Mainwindow erzeugen
2025-12-29 15:44:06 +01:00
* @param parent Das Elternwidget
2025-12-26 14:07:55 +01:00
*/
2025-12-19 21:20:14 +01:00
2025-12-15 20:57:09 +01:00
BCMainWindow::BCMainWindow(QWidget *parent)
: QMainWindow(parent)
{
2026-01-01 13:28:17 +01:00
// __fix! in der Form nötig?
2025-12-28 22:48:18 +01:00
qRegisterMetaType<BCValue>("BCValue");
qRegisterMetaType<QList<BCValue>>("BCValueList");
2025-12-27 18:43:15 +01:00
2025-12-16 22:42:35 +01:00
setupUi(this);
2025-12-26 23:09:53 +01:00
2026-01-03 00:09:26 +01:00
// den pimp-my-ride-button schalten wir vorerst aus.
_pimpButton->hide();
2026-01-01 13:28:17 +01:00
// Wir schreiben den 'initMainWindow()' Aufruf mit Hilfe des
// timers in die Event-Queue, damit er erst ausgeführt wird,
// wenn das Fenster sichtbar ist.
QTimer::singleShot(0, this, [this]()
{
2026-01-01 02:40:12 +01:00
initMainWindow();
});
2025-12-26 23:09:53 +01:00
}
2025-12-29 15:44:06 +01:00
/**
* @brief Destruktor. Räumt den Workthread auf.
*/
2025-12-26 23:09:53 +01:00
BCMainWindow::~BCMainWindow()
{
_worker.quit(); // Event Loop stoppen
_worker.wait(); // Warten bis Thread wirklich fertig ist
2025-12-26 23:09:53 +01:00
}
2025-12-29 15:44:06 +01:00
/**
* @brief Initialisiert alle Komponenten des MainWindows.
*/
void BCMainWindow::initMainWindow()
2025-12-26 23:09:53 +01:00
{
2026-01-01 13:28:17 +01:00
// Lambda um die buttons mit ihren Actions zu verbinden
2025-12-31 13:38:46 +01:00
auto configureAction = [&]( QToolButton* button, QAction* action, BCDevice::ID deviceID )
2025-12-26 23:09:53 +01:00
{
2025-12-26 23:37:15 +01:00
// Action an den Button binden
2025-12-26 23:09:53 +01:00
button->setDefaultAction( action);
2025-12-26 23:37:15 +01:00
// new way: die DeviceID muss aber explizit vom Lambda eingefanden werden.
connect( action, &QAction::triggered, this, [this,deviceID]()
2026-01-03 00:09:26 +01:00
{
onShowDevicePanel( deviceID );
});
2025-12-27 18:43:15 +01:00
2025-12-26 23:37:15 +01:00
if( _devicePanels.contains(deviceID) )
2026-01-03 00:09:26 +01:00
{
BCDeviceView* currentPanel = _devicePanels[deviceID];
// ... und ihre device ID
currentPanel->setDeviceID( deviceID );
// Wenn ein Device (entspricht einem Datenmodel) fertig eingelesen wurde,
// wird es weitergereicht.
// Problem: alle Panels bekommen alle Datenmodelle angeboten.
connect( &_dataManager, &BCXmlLoader::valueListReady, currentPanel, &BCDeviceView::onValueListReady );
}
2025-12-26 23:09:53 +01:00
};
// Wir wollen die Devices den Views zuordnen können
_devicePanels[BCDevice::ID::Console] = _consolePanel;
_devicePanels[BCDevice::ID::Battery] = _batteryPanel;
_devicePanels[BCDevice::ID::Motor] = _motorPanel;
2026-01-03 00:09:26 +01:00
//_devicePanels[BCDevice::ID::Pimp] = _pimpPanel;
2025-12-22 00:14:42 +01:00
2026-01-03 00:09:26 +01:00
// Die actions an die Buttons binden
2025-12-31 13:38:46 +01:00
configureAction(_motorButton, _motorAction, BCDevice::ID::Motor );
configureAction(_consoleButton, _consoleAction, BCDevice::ID::Console );
configureAction(_batteryButton, _batteryAction, BCDevice::ID::Battery );
2026-01-03 00:09:26 +01:00
//configureAction(_pimpButton, _pimpAction, BCDevice::ID::Pimp );
2025-12-22 00:14:42 +01:00
2026-01-03 00:09:26 +01:00
/*
2025-12-31 16:30:03 +01:00
bool m_isDarkMode = false;
QString icon = m_isDarkMode ? "☀️" : "🌙";
fitzeButton->setText(icon);
QString style = QString(
2026-01-03 00:09:26 +01:00
"QPushButton {"
" background-color: %1;"
" border: 1px solid %2;"
" border-radius: 6px;"
" font-size: 12pt;"
" padding: 0px;"
"}"
"QPushButton:hover {"
" background-color: %3;"
"}"
).arg(m_isDarkMode ? "#2B2B2B" : "#FFFFFF")
.arg(m_isDarkMode ? "#3F3F3F" : "#E1DFDD")
.arg(m_isDarkMode ? "#3A3A3A" : "#F9F9F9");
2025-12-31 16:30:03 +01:00
fitzeButton->setStyleSheet(style);
2026-01-03 00:09:26 +01:00
*/
2026-01-03 23:51:14 +01:00
initStatusBar();
// besser: model::emit dataChanged
// also: emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, ValueRole});
2026-01-03 20:10:30 +01:00
connect( _connectButton, &QToolButton::clicked, &_transmitter, &BCTransmitter::onToggleDriverConnection );
2026-01-02 16:25:21 +01:00
connect( _syncButton, &QToolButton::clicked, this, &BCMainWindow::onSyncDeviceView );
2026-01-01 02:40:12 +01:00
connect( &_transmitter, &BCTransmitter::valueUpdated, this, &BCMainWindow::onValueUpdated );
2025-12-29 00:04:15 +01:00
2026-01-05 17:57:13 +01:00
connect(this, &BCMainWindow::requestValueUpdate, &_transmitter, &BCTransmitter::onEnqueueValue);
connect(&_worker, &QThread::finished, &_transmitter, &QObject::deleteLater);
2026-01-03 00:09:26 +01:00
connect( &_transmitter, &BCTransmitter::driverStateChanged, this, &BCMainWindow::onDriverStateChanged );
2026-01-01 00:40:27 +01:00
2026-01-03 00:09:26 +01:00
// transmitter starten
2026-01-03 23:51:14 +01:00
_transmitter.moveToThread(&_worker);
_worker.start();
2025-12-28 14:42:12 +01:00
2026-01-01 02:40:12 +01:00
// die Daten des eBikes laden
_dataManager.loadXmlBikeData(":/bikeinfo.xml"_L1);
2026-01-03 00:09:26 +01:00
//_consoleAction->trigger();
_batteryAction->trigger();
2026-01-01 03:08:34 +01:00
2026-01-02 22:15:50 +01:00
}
2026-01-03 00:09:26 +01:00
2026-01-02 22:15:50 +01:00
void BCMainWindow::initStatusBar()
{
2026-01-01 03:08:34 +01:00
QStatusBar *statBar = statusBar();
2026-01-03 23:51:14 +01:00
BCDriverStateWidget* connector = new BCDriverStateWidget(this);
connect( &_transmitter, &BCTransmitter::driverStateChanged, connector, &BCDriverStateWidget::onDriverStateChanged );
statBar->addPermanentWidget(connector);
2026-01-01 03:08:34 +01:00
2026-01-03 23:51:14 +01:00
statBar->showMessage("Ready");
BCThemeSwitchButton *themeBtn = new BCThemeSwitchButton(this);
2026-01-01 03:08:34 +01:00
statBar->addPermanentWidget(themeBtn);
2026-01-03 23:51:14 +01:00
connect(themeBtn, &BCThemeSwitchButton::themeChanged, this, [this](bool isDark)
{
QString message = isDark ? "Dark Mode Activated" : "Light Mode Activated";
statusBar()->showMessage( message, 3000);
2026-01-01 03:08:34 +01:00
});
2025-12-15 20:57:09 +01:00
}
2025-12-16 22:42:35 +01:00
2026-01-03 00:09:26 +01:00
/**
* @brief Setzt den Headerlabel ( == die Device-Bezeichnung )
* @param headerLabel Der headerLabel
*/
void BCMainWindow::setHeaderLabel( const QString& headerText)
{
_headerLabel->setText( " BionxControl: " + headerText );
}
2026-01-02 22:15:50 +01:00
void BCMainWindow::autoConnect()
{
// __fix!
// if( !connect)
// fallBack
}
void BCMainWindow::onDriverStateChanged( BCDriver::DriverState state, const QString& message )
{
qDebug() << " --- on DriverStatusChanged: " << state << ":" << message;
2026-01-03 23:51:14 +01:00
_statusbar->showMessage( message, 8000 );
2026-01-02 22:15:50 +01:00
}
2025-12-26 23:09:53 +01:00
void BCMainWindow::onShowDevicePanel( BCDevice::ID deviceID )
2025-12-26 14:07:55 +01:00
{
2025-12-26 23:09:53 +01:00
qDebug() << " --- onShowDevicePanel:" << deviceID;
2025-12-28 22:48:18 +01:00
if( _devicePanels.contains( deviceID ) )
2025-12-26 14:07:55 +01:00
{
2025-12-29 23:29:56 +01:00
BCDeviceView* nxtPanel = _devicePanels[deviceID];
2025-12-26 23:37:15 +01:00
if( nxtPanel != _currentPanel )
{
_currentPanel = nxtPanel;
2025-12-31 13:38:46 +01:00
qDebug() << " --- Firz: " << _currentPanel->property( BCKeyHeaderLabel );
setHeaderLabel( _currentPanel->property( BCKeyHeaderLabel ).toString() );
_stackedWidget->setCurrentWidget( nxtPanel );
2025-12-26 23:37:15 +01:00
// knopf auch abschalten?
}
2025-12-26 23:09:53 +01:00
}
}
2025-12-26 14:07:55 +01:00
void BCMainWindow::onConnectButtonToggled(bool checked )
{
2025-12-26 14:07:55 +01:00
//_dataManager.setDriverConnectionState( checked );
}
void BCMainWindow::onValueUpdated(BCDevice::ID deviceID, int index, BCValue::State state, const QString& newValue )
{
qDebug() << "Reply: from: " << deviceID << " at: " << index << "finished. Success:" << (uint8_t)state << " on:" << newValue;
2025-12-28 22:48:18 +01:00
if( _devicePanels.contains( deviceID ) )
{
2025-12-29 23:29:56 +01:00
BCDeviceView& panel = *_devicePanels[deviceID];
2025-12-29 20:10:05 +01:00
panel.onValueUpdated( index, state, newValue );
2025-12-28 22:48:18 +01:00
}
}
2025-12-31 16:30:03 +01:00
/**
* @brief SLOT, der aufgerufen wird, um das akutelle Device (Battery, Motor, ... )
* zu synchronisieren, d.h. die aktuellen Werte über den CAN-Bus abzufragen.
*/
2026-01-02 16:25:21 +01:00
void BCMainWindow::onSyncDeviceView()
{
2026-01-02 16:25:21 +01:00
Q_ASSERT_X(_currentPanel, "onSyncDeviceView()", "_currentpanel ist null!");
2025-12-28 20:18:28 +01:00
qDebug() << " ---Syncing";
2025-12-28 14:42:12 +01:00
2025-12-28 22:48:18 +01:00
const BCValueList& currentList =_currentPanel->getValueListX();
2025-12-28 14:42:12 +01:00
// alle einzeln? echt jetzt?
2026-01-02 16:25:21 +01:00
for( const BCValuePtr& value : currentList )
2025-12-28 14:42:12 +01:00
{
2026-01-02 01:43:49 +01:00
qDebug() << " --- begin sync of value: " << value->label;
2026-01-02 16:25:21 +01:00
// wir setzen auf 'lesen'
value->state.setFlag( BCValue::State::ReadMe );
2026-01-05 17:57:13 +01:00
// statt '_transmitter.onEnqueueValueCommand( value )' entkoppeln
// wir das eleganter über emit requestValueUpdate()
2026-01-02 01:52:48 +01:00
emit requestValueUpdate( value);
}
2025-12-28 14:42:12 +01:00
}