Files
BionxControl/bcmainwindow.cpp

266 lines
8.3 KiB
C++

/***************************************************************************
BionxControl
Copyright © 2025 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 <bcmainwindow.h>
#include <bcitemdelegate.h>
#include <ui_bcmainwindow.h>
/**
* @brief Das Mainwindow erzeugen
* @param parent
*/
BCMainWindow::BCMainWindow(QWidget *parent)
: QMainWindow(parent)
{
// WICHTIG: Registriere sowohl das Struct als auch die LISTE des Structs
qRegisterMetaType<BCDataValue>("BCDataValue");
qRegisterMetaType<QList<BCDataValue>>("BCValueList");
setupUi(this);
initMainWindow();
}
BCMainWindow::~BCMainWindow()
{
_worker.quit(); // Event Loop stoppen
_worker.wait(); // Warten bis Thread wirklich fertig ist
}
void BCMainWindow::initMainWindow()
{
// Die Daten und auch die Datenmodelle für die Views werden
// vom DataManager verwaltet und an die Views weitergereicht.
/*
auto setDeviceModel = [&]( BCDevice::ID deviceID, BCDevicePanel* panel )
{
auto model = _dataManager.getModel( deviceID );
if( model)
{
QAbstractItemView* valueView = panel->getValueView();
valueView->setModel( *model );
//valueView->resizeColumnsToContents();
//_valueView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
}
};
*/
auto configureAction = [&]( QToolButton* button, QAction* action, BCDevice::ID deviceID, const QString& panelTitle="" )
{
// Action an den Button binden
button->setDefaultAction( action);
// old school, not used
//connect( action, &QAction::triggered, this, &BCMainWindow::onActionButtonTriggered );
//connect( action, &QAction::toggled, this, &BCMainWindow::onActionButtonToggled );
// new way: die DeviceID muss aber explizit vom Lambda eingefanden werden.
connect( action, &QAction::triggered, this, [this,deviceID]()
{
onShowDevicePanel( deviceID );
});
if( _devicePanels.contains(deviceID) )
{
BCDevicePanel* currentPanel = _devicePanels[deviceID];
// den Panels ihren title geben
currentPanel->setHeaderText( panelTitle );
// ... 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, &BCDataManager::valueListReady, currentPanel, &BCDevicePanel::onValueListReady );
}
};
// Wir wollen die Devices den Views zuordnen können
_devicePanels[BCDevice::ID::Console] = _consolePanel;
_devicePanels[BCDevice::ID::Battery] = _batteryPanel;
_devicePanels[BCDevice::ID::Motor] = _motorPanel;
// Die actions an die Buttons binden
configureAction(_motorButton, _motorAction, BCDevice::ID::Motor, "Motor Settings"_L1 );
configureAction(_consoleButton, _consoleAction, BCDevice::ID::Console, "Console Settings"_L1 );
configureAction(_batteryButton, _batteryAction, BCDevice::ID::Battery, "Battery Settings"_L1 );
configureAction(_pimpButton, _pimpAction, BCDevice::ID::Pimp, "Pimp my Ride"_L1 );
/*
BCItemDelegate* _delegate = new BCItemDelegate( _valueView);
//_delegate = new AnimatedDelegate(_valueView );
_valueView->setItemDelegate( _delegate );
// Verwendung:
connect(_delegate, &BCItemDelegate::viewUpdateNeeded, _valueView->viewport(), QOverload<>::of(&QWidget::update));
//_valueView->setItemDelegate(_delegate);
// Highlight mit Fade-Out:
//_delegate->onHighlightRow(2); // 2 Sekunden Fade
BCTransmitter* transmitter = _dataManager.getTransmitter();
// besser: model::emit dataChanged
// also: emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, ValueRole});
connect( &_dataManager, &BCMainWindow::valueTouched, _delegate, &BCItemDelegate::onHighlightRow );
connect( _connectButton, &QPushButton::clicked, transmitter, &BCTransmitter::onToggleConnectionState );
connect( _syncButton, &QPushButton::clicked, &_dataManager, &BCMainWindow::onSyncFromDevice );
*/
//_delegate->onHighlightRow(2); // 2 Sekunden Fade
// besser: model::emit dataChanged
// also: emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, ValueRole});
//connect( &_dataManager, &BCMainWindow::valueTouched, _delegate, &BCItemDelegate::onHighlightRow );
connect( _connectButton, &QPushButton::clicked, &_transmitter, &BCTransmitter::onToggleConnectionState );
connect( _syncButton, &QPushButton::clicked, this, &BCMainWindow::onSyncFromDevice );
// die Daten des eBikes laden
_dataManager.loadXmlBikeData(":/bikeinfo.xml"_L1);
_transmitter.moveToThread(&_worker);
connect(this, &BCMainWindow::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();
_consoleAction->trigger();
}
/*
void BCMainWindow::onValueListReady( BCDevice::ID deviceID )
{
qDebug() << " --- onValueListReady!" << deviceID;
if( _devicePanels.contains( deviceID ) )
{
BCDevicePanel& panel = *_devicePanels[deviceID];
BCValueList& victim = panel.exposeValueList();
BCValueList& newValueList = _dataManager.getCurrentValueList();
qDebug() << " --- Before: " << victim.size() << " orig:" << newValueList.size();
victim = std::exchange(newValueList, BCValueList());
//_devicePanels[deviceID]->exchangeValueList( newValueList );
qDebug() << " ---After: " << victim.size() << " orig:" << newValueList.size();
}
}
*/
void BCMainWindow::onShowDevicePanel( BCDevice::ID deviceID )
{
qDebug() << " --- onShowDevicePanel:" << deviceID;
if( _devicePanels.contains( deviceID ) )
{
BCDevicePanel* nxtPanel = _devicePanels[deviceID];
if( nxtPanel != _currentPanel )
{
_stackedWidget->setCurrentWidget( nxtPanel );
_currentPanel = nxtPanel;
// knopf auch abschalten?
}
}
}
void BCMainWindow::onActionButtonTriggered( bool checked)
{
//qDebug() << " --- onActionButtonTriggered: " << checked;
}
void BCMainWindow::onActionButtonToggled( bool checked)
{
//qDebug() << " --- onActionButtonToggled: " << checked;
}
void BCMainWindow::onConnectButtonToggled(bool checked )
{
//_dataManager.setDriverConnectionState( checked );
}
void BCMainWindow::onCommandFinished(int id, bool success)
{
qDebug() << "[Manager] Command" << id << "finished. Success:" << success;
}
void BCMainWindow::onRunnerMessage(const QString &msg)
{
qDebug() << "[Worker says]:" << msg;
}
void BCMainWindow::onSyncFromDevice()
{
qDebug() << " ---Syncing";
Q_ASSERT(_currentPanel && "currentpanel ist null!");
const BCValueList& currentList =_currentPanel->getValueList();
// alle einzeln? echt jetzt?
for( const BCDataValue& 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);
}
}