/*************************************************************************** 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 @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 #include #include /** * @brief Kosntruktion. Aktiviert erstmal den Dummy-Driver. */ BCTransmitter::BCTransmitter(QObject *parent) : QObject(parent), _isBusy(false) { //_canDriver = new BCDriverTinyCan{this}; _canDriver = &_dummyDriver; } /** * @brief Steuert die Verbindung mit dem 'echten' CAN-Bus Treiber. * @param connect true: Vesuche den CAN-Bus Treiber zu laden und zu verbinden * false: Disconnect & Cleanup */ void BCTransmitter::onToggleDriverConnection( bool connect ) { qDebug() << " --- onToggleDriverConnection: " << connect; // FIX! Ende der current op abwarten! // Hier sind wir noch in GUI Thread QMutexLocker locker(&_mutex); // weitere operation stoppen _isBusy = true; connect ? connectCanDriver() : disconnectCanDriver(); _isBusy = false; } void BCTransmitter::connectCanDriver() { // hier gehts nur um den echten Treiber // Treiber laden und/oder starten: BCDriver::DriverStateResult result; //(defaults to 'NotPresent') if( _tinyCanDriver.getDriverState() != BCDriver::DriverState::DeviceReady ) result = _tinyCanDriver.loadAndStartDriver(); QString message("FitzeFatze!"); // hat geklappt if( _tinyCanDriver.getDriverState() >= BCDriver::DriverState::Opened ) { uint32_t console = static_cast(BCDevice::ID::Console); uint8_t hwRev = static_cast (BC::ID::Cons_Rev_Hw); TransmitResult hwVersion = _tinyCanDriver.readRawByte( console, hwRev); if( hwVersion.has_value() ) { message = " ---- HAIL to the king!"; qDebug() << message; // swap driver _canDriver = &_tinyCanDriver; } else { qDebug() << "Console not responding"; } } else { message = result.error(); } emit driverStateChanged( _tinyCanDriver.getDriverState(), message ); } void BCTransmitter::disconnectCanDriver() { _tinyCanDriver.resetDriver(); _canDriver = &_dummyDriver; emit driverStateChanged( _tinyCanDriver.getDriverState(), "Disconnected" ); } void BCTransmitter::enqueueValue( BCValuePtrConst value) { // Hier sind wir noch in GUI Thread QMutexLocker locker(&_mutex); _valueQueue.enqueue( value ); // wir wollen nicht den ganzen Value verschicken, erstrecht // wollen wir nicht den Value in verschiedenen Threads gleichzeitig // in die Hand nehmen, also hantieren wir nur mit den Inidizes. // Trigger processing im Event-Loop des Worker Threads // invokeMethod mit QueuedConnection entkoppelt den Aufruf, // damit enqueueValue sofort zurückkehrt (non-blocking für den Aufrufer). QMetaObject::invokeMethod(this, "processValue", Qt::QueuedConnection); /* QMetaObject::invokeMethod(this, [this]() { processValue(); }, Qt::QueuedConnection ); */ } void BCTransmitter::processValue() { if (_isBusy) return; _isBusy = true; while (true) { BCValuePtrConst valuePtr{}; { QMutexLocker locker(&_mutex); if (_valueQueue.isEmpty()) { _isBusy = false; break; // Schleife verlassen, warten auf neue Events } valuePtr =_valueQueue.dequeue(); } // Mutex wird hier freigegeben! WICHTIG: Execute ohne Lock! // Kosmetik const BCValue& value = *(valuePtr.get()); // Value ist 'under construction' //emit valueUpdated( value.deviceID, value.indexRow, BCValue::State::Locked ); uint32_t devID = static_cast(value.deviceID); uint8_t regID = static_cast (value.registerID); QString newVisibleValue; BCValue::State newState = BCValue::State::NoState; if(value.state.testFlag( BCValue::State::WriteMe ) ) { } // oder sollen wir hier beides erlauben ? readFlag & writeFlag ? // Was kommt dann zuerst? Schreiben und lesen als verify ? else if( value.state.testFlag( BCValue::State::ReadMe ) ) { // wir sind hier im anderen thread! nicht einfach so reinschreiben, nur lesen TransmitResult result = value.isWord ? readWordValue( devID, regID ) : readByteValue( devID, regID ); if( result.has_value() ) { newVisibleValue = value.formatValue( result.value() ); newState = BCValue::State::InSync; } else { newState = BCValue::State::Failed; } } emit valueUpdated( value.deviceID, value.indexRow, newState, newVisibleValue ); // __fix bc::processEventsFor(50); //emit valueStateChanged(cmd.id, true); //emit valueStateChanged(0, true); } } TransmitResult BCTransmitter::readByteValue( uint32_t deviceID, uint8_t registerID ) { qDebug() << " --- YES: Read ByteValue: " << registerID; // Wir lesen nur ein Byte und gut. return _canDriver->readRawByte( deviceID, registerID ); } TransmitResult BCTransmitter::readWordValue( uint32_t deviceID, uint8_t registerID ) { qDebug() << " --- YES: Read WordValue: " << registerID; uint32_t result{}; // hi byte Leseversuch. TransmitResult value = _canDriver->readRawByte( deviceID, registerID ); // Fehler? dann weg if( !value) return std::unexpected( value.error() ); // hi byte speichern result = *value << 8; // low byte, liegt im followup register: +1 value = _canDriver->readRawByte( deviceID, registerID+1 ); if( !value) return std::unexpected( value.error() ); result += *value; return result; }