diff --git a/bc.cpp b/bc.cpp new file mode 100644 index 0000000..4fd2ebc --- /dev/null +++ b/bc.cpp @@ -0,0 +1,56 @@ + +#include +#include +#include + +#include +#include +#include + +namespace bc +{ + + void delay_seconds( uint32_t tval ) + { + std::this_thread::sleep_for(std::chrono::seconds( tval ) ); + } + + + void delay_millis( uint32_t tval ) + { + std::this_thread::sleep_for(std::chrono::milliseconds( tval ) ); + } + + + void delay_micros( uint32_t tval ) + { + std::this_thread::sleep_for(std::chrono::microseconds( tval ) ); + } + + /** + * @brief Macht aus int x den String 000x zum schönaussehen. + * @param count der Wert + * @param len die voranzustellenden Nullen + * + * Macht aus int x den String 000x zum schönaussehen. + * + */ + + QString formatInt( int count, int len ) + { + QString result( "%0" ); + result = result.arg( count, len, 10, QChar( '0' ) ); + return result; + } + + + void processEventsFor(int milliseconds) + { + QElapsedTimer timer; + timer.start(); + + while (timer.elapsed() < milliseconds) { + QApplication::processEvents(QEventLoop::AllEvents, 50); + } + } +} // namespace cbc diff --git a/bccandriver.cpp b/bccandriver.cpp new file mode 100644 index 0000000..78c7718 --- /dev/null +++ b/bccandriver.cpp @@ -0,0 +1,88 @@ +#include + +#include + + + +BCCanDriver::BCCanDriver(QObject* parent ) + : QObject{ parent } +{ + +} + +BCCanDriver::dState BCCanDriver::getState() +{ + return _driverState; +} + +void BCCanDriver::setState( dState newState ) +{ + _driverState = newState; +} + +QString BCCanDriver::stateLabel( dState state ) +{ + switch( (int) state ) + { + case sDriverError: + return stDriverError; + + case sIdle: + return stIdle; + + case sLoaded: + return stLoaded; + + case sReady: + return stReady; + }; + +} +/** + * + * @brief Der Slot, der das Initialisieren des Treibers auslöst. + * Teilt sich in die zwei Etappen + * - Driver dll laden + * - Driver mit der Console verbinden + * D.h. initDriver kann als Wiedereinstiegspunkt + * verwendet werden, falls der Treiber geladen wurde, + * aber die Console noch nicht eingeschaltet war. + */ + +void BCCanDriver::onStartDriver() +{ + + try + { + + // erstmal die Dll Laden: State::sIdle -> State::sLoaded + if( BCCanDriver::sIdle == _driverState ) + _driverState = loadDriver(); + + emit stateChanged( (int) _driverState ); + + // DLL geladen, Verbindungsversuch + _driverState = initDriver(); + + // Wir haben was erreicht + emit stateChanged( (int) _driverState ); + } + catch( std::exception& except ) + { + //::CanDownDriver(); + //::UnloadDriver(); + + emit errorOccured( except.what() ); + + } + +} + +/* +void BCCanDriver::onLoadItem( CBCItem& item ) +{ + + item.setValue( 99.99 ); + emit itemLoaded( item ); +} +*/ diff --git a/bccandriver.h b/bccandriver.h new file mode 100644 index 0000000..4799403 --- /dev/null +++ b/bccandriver.h @@ -0,0 +1,113 @@ +#ifndef BCCANDRIVER_H +#define BCCANDRIVER_H + +#include + +#include + +/* +int32_t CanInitDriver(char *options); +void CanDownDriver(void); +int32_t CanSetOptions(char *options); +int32_t CanDeviceOpen(uint32_t index, char *parameter); +int32_t CanDeviceClose(uint32_t index); + +int32_t CanSetMode(uint32_t index, unsigned char can_op_mode, uint16_t can_command); + +int32_t CanTransmit(uint32_t index, struct TCanMsg *msg, int32_t count); +void CanTransmitClear(uint32_t index); +uint32_t CanTransmitGetCount(uint32_t index); +int32_t CanTransmitSet(uint32_t index, uint16_t cmd, uint32_t time); +int32_t CanReceive(uint32_t index, struct TCanMsg *msg, int32_t count); +void CanReceiveClear(uint32_t index); +uint32_t CanReceiveGetCount(uint32_t index); + +int32_t CanSetSpeed(uint32_t index, uint16_t speed); +int32_t CanSetSpeedUser(uint32_t index, uint32_t value); +char* CanDrvInfo(void); +char* CanDrvHwInfo(uint32_t index); +int32_t CanSetFilter(uint32_t index, struct TMsgFilter *msg_filter); +int32_t CanGetDeviceStatus(uint32_t index, struct TDeviceStatus *status); +void CanSetPnPEventCallback(void (DRV_CALLBACK_TYPE *event)(uint32_t index, int32_t status)); +void CanSetStatusEventCallback(void (DRV_CALLBACK_TYPE *event) (uint32_t index, struct TDeviceStatus *device_status) ); +void CanSetRxEventCallback(void (DRV_CALLBACK_TYPE *event)(uint32_t index, struct TCanMsg *msg, int32_t count) ); + +void CanSetEvents( uint16_t events ); +uint32_t CanEventStatus(void); +*/ + +struct CBCItem; +class BCCanDriverStatus; + +/** + * @Abstrakte Basisklasse für alle CAN-Bus Treiber. + * Das Bionx CAN-Bus System kann auf verschiedenen Wegen + * angesprochen werden, etwa den über BBI USB2CAN Controller, + * über den TinyCAN Adapter oder ggf. über einen EL327 Stecker. + * + * Die hier relevante Implementierung über das TinyCan System + * findet sich in der Unterklasse 'BCCanDriverTinyCan'. + * + * @see BCCanDriverTinyCan + */ + + +class BCCanDriver : public QObject +{ + Q_OBJECT + +public: + + typedef enum + { + sDriverError = 0, + sIdle, + sLoaded, + sReady, + dStateSize + } dState; + + [[maybe_unused]] constexpr static const char* stDriverError = "Error"; + [[maybe_unused]] constexpr static const char* stIdle = "[not loaded]"; + [[maybe_unused]] constexpr static const char* stLoaded = "[not connected]"; + [[maybe_unused]] constexpr static const char* stReady = "Ready"; + + static QString stateLabel( dState state ); + + explicit BCCanDriver( QObject* parent = nullptr ); + virtual ~BCCanDriver() = default; + + dState getState(); + + virtual BCCanDriver::dState loadDriver() = 0; + virtual BCCanDriver::dState initDriver() = 0; + + virtual uint getValue( BCDevice::ID dev, BC::ID reg ) = 0; + virtual void setValue( BCDevice::ID dev, BC::ID reg, int value ) = 0; + +signals: + + void errorOccured( const QString& errMsg ); + void statusHint( const QString& msg ); + + void stateChanged( int state ); + void itemReady( const QString& label ); + void itemLoaded( CBCItem& item ); + +public slots: + + void onStartDriver(); + //void onLoadItem( CBCItem& item ); + +protected: + + void setState( dState newState ); + + dState _driverState = sIdle; + + int _retries = 5; + int _timeOuts = 20; + +}; + +#endif // BCCANDRIVER_H diff --git a/bccandrivertinycan.cpp b/bccandrivertinycan.cpp new file mode 100644 index 0000000..38b49ec --- /dev/null +++ b/bccandrivertinycan.cpp @@ -0,0 +1,219 @@ +#include + + +//#define UNLIMITED_SPEED_VALUE 70 /* Km/h */ +//#define UNLIMITED_MIN_SPEED_VALUE 30 /* Km/h */ +//#define MAX_THROTTLE_SPEED_VALUE 70 /* Km/h */ + +#include + + +#include + + +#include +//#include "mhstcan.h" + +BCCanDriverTinyCan::BCCanDriverTinyCan( QObject* parent ) + : BCCanDriver(parent ) +{ + +} + + +BCCanDriver::dState BCCanDriverTinyCan::loadDriver() +{ + + //qDebug() << "CanBusControl " << cbc::Version << '\n' << "based on BigXionFlasher (c) 2011-2013 by Thomas Koenig - www.bigxionflasher.org"; + + struct ::TDeviceStatus status; + + if( ::LoadDriver( NULL ) < 0 ) + throw std::runtime_error( "Driver Error: 'LoadDriver'" ); + + if( ::CanInitDriver( NULL ) < 0 ) + throw std::runtime_error( "Driver Error: 'InitDriver'" ); + + if( ::CanDeviceOpen( 0, NULL ) < 0 ) + throw std::runtime_error( "Driver Error: 'DeviceOpen'" ); + + ::CanSetSpeed( 0, CAN_125K_BIT ); + ::CanSetMode( 0, OP_CAN_START, CAN_CMD_ALL_CLEAR ); + ::CanGetDeviceStatus( 0, &status ); + + if( status.DrvStatus < DRV_STATUS_CAN_OPEN ) + throw std::runtime_error( "Driver Error: could not open device." ); + + if( status.CanStatus == CAN_STATUS_BUS_OFF ) + { + ::CanSetMode( 0, OP_CAN_RESET, CAN_CMD_NONE ); + throw std::runtime_error( "Driver Error: CAN Status 'BusOff'" ); + } + + setState(BCCanDriver::sLoaded); + + return BCCanDriver::sLoaded; + +} + + +/* + try + { + loadDriver(); + initBCDevice::ID::Console(); + } + + catch( std::exception& except ) + { + ::CanDownDriver(); + ::UnloadDriver(); + + // re-throw + throw std::runtime_error( except.what() ); + } + */ + + +BCCanDriver::dState BCCanDriverTinyCan::initDriver() +{ + + qDebug() << "XXX BCCanDriverTinyCan::Driver Init: putting BCDevice::ID::Console in slave mode ... "; + // BCDevice::ID::Console already in slave mode. good! + if( getValue( BCDevice::ID::Console, BC::ID::Cons_Status_Slave ) ) + return BCCanDriver::sReady; + + qDebug() << "BCCanDriverTinyCan::BCCanDriverTinyCan::XXX Driver Init: putting BCDevice::ID::Console in slave mode ... "; + + unsigned int retry = _timeOuts; + emit statusHint( "Driver Init: putting BCDevice::ID::Console in slave mode ... " ); + + int isSlave = 0; + do + { + setValue( BCDevice::ID::Console, BC::ID::Cons_Status_Slave, 1 ); + isSlave = getValue( BCDevice::ID::Console, BC::ID::Cons_Status_Slave ); + bc::delay_millis( 200 ); + + } while( retry-- && !isSlave ); + + bc::delay_millis( 500 ); // give the Console some time to settle + //if( !isSlave ) + emit statusHint( QString("putting BCDevice::ID::Console in slave mode ") + (isSlave ? "done" : "failed") ); + // ist das jetzt irgendwie schlimm, wenn wir keine slave BCDevice::ID::Console haben + //return isSlave ? BCCanDriver::connected + + return BCCanDriver::sReady; + + +} + + +//unsigned int BCCanDriverTinyCan::getValue( unsigned char receipient, unsigned char reg ) +uint BCCanDriverTinyCan::getValue(BCDevice::ID deviceID, BC::ID registerID ) +{ + + struct TCanMsg msg; + + uint32_t device = static_cast(deviceID); + uint32_t reg = static_cast(registerID); + + // msg verpacken + msg.MsgFlags = 0L; + msg.Id = device; + msg.MsgLen = 2; + msg.MsgData[0] = 0x00; + msg.MsgData[1] = reg; + + // msg verschicken + ::CanTransmit( 0, &msg, 1 ); + + int retries = _retries; + // _timeOuts (== 20) mal cTIMEOUT_MS (== 10 ms ) Versuche ... + int timeOuts = _timeOuts; + + // ... warten bis der Sendepuffer leer ist + while( timeOuts-- && ::CanTransmitGetCount( 0 ) ) + bc::delay_millis( cTIMEOUT_MS ); + + if( timeOuts == -1 ) + throw std::runtime_error( "getValue error: could not send value" ); + +retry: + + // _timeOuts (== 20) mal cTIMEOUT_MS (== 10 ms ) Versuche ... + timeOuts = _timeOuts; + // ... warten, bis der Empfangspuffer nicht mehr leer ist + while( timeOuts-- && !::CanReceiveGetCount( 0 ) ) + bc::delay_millis( cTIMEOUT_MS ); + + if( timeOuts == -1 ) + throw std::runtime_error( "getValue error: no response from node" ); + + // message empfangen + int err = ::CanReceive( 0, &msg, 1 ); + qDebug() << "HÄÄ ?" << err << "reg: "<< reg <<" timeOuts: " << timeOuts; + + if( err < 0 ) + //throw std::runtime_error( "getValue error: could not receive value" ); + qDebug( "getValue error: could not receive value" ); + + qDebug() << "HÄÄ 2" < 0 ) + if( --retries && ( msg.Id != BIB || msg.MsgLen != 4 || msg.MsgData[1] != reg ) ) + goto retry; + + if( !timeOuts ) + throw std::runtime_error( "CAN --response errror" ); + + return (unsigned int) msg.MsgData[3]; + +} + + +// void BCCanDriverTinyCan::setValue( unsigned char receipient, unsigned char reg, unsigned char value ) +void BCCanDriverTinyCan::setValue(BCDevice::ID deviceID, BC::ID registerID, int value ) +{ + + qDebug() << "SaveItem( BCCanDriverTinyCan::CBCItem& item ): "; + + uint32_t device = static_cast(deviceID); + uint32_t reg = static_cast(registerID); + + struct TCanMsg msg; + int timeout_count = cTIMEOUT_COUNT; + + msg.MsgFlags = 0L; + msg.Id = device; + msg.MsgLen = 4; + msg.MsgData[0] = 0x00; + msg.MsgData[1] = reg; + msg.MsgData[2] = 0x00; + msg.MsgData[3] = value; + + ::CanTransmit( 0, &msg, 1 ); + + while( timeout_count-- && ::CanTransmitGetCount( 0 ) ) + bc::delay_millis( cTIMEOUT_MS ); + + if( timeout_count == -1 ) + emit statusHint( QString( "error: could not send value to %1" ).arg( device ) ); + +} + + + + +void BCCanDriverTinyCan::awaitReceiveBuf( int timeout ) +{ + +} + +void BCCanDriverTinyCan::awaitSendBuf(int timeout ) +{ + +} + diff --git a/bccandrivertinycan.h b/bccandrivertinycan.h new file mode 100644 index 0000000..2e98850 --- /dev/null +++ b/bccandrivertinycan.h @@ -0,0 +1,47 @@ +#ifndef BCCANDRIVERTINYCAN_H +#define BCCANDRIVERTINYCAN_H + +#include + +class BCCanDriverTinyCan : public BCCanDriver +{ + Q_OBJECT + +public: + + explicit BCCanDriverTinyCan( QObject* parent=nullptr ); + virtual ~BCCanDriverTinyCan() = default; + + BCCanDriver::dState loadDriver() override; + BCCanDriver::dState initDriver() override; + + uint getValue( BCDevice::ID dev, BC::ID reg ) override; + void setValue( BCDevice::ID dev, BC::ID reg, int value ) override; + + QString getNodeName( unsigned char id ); + +protected: + + // typen? + //void setValue( unsigned char receipient, unsigned char reg, unsigned char value ); + //unsigned int getValue( unsigned char receipient, unsigned char reg ); + + void awaitReceiveBuf( int timeout ); + void awaitSendBuf(int timeout ); + + const char* CBCDLL_LIN = "libmhstcan.so"; + const char* CBCDLL_WIN = "mhstcan.dll"; + + static constexpr int cTIMEOUT_MS = 10; // 10ms + static constexpr int cTIMEOUT_COUNT = 10; + + + const unsigned int BATTERY = 0x010; + const unsigned int MOTOR = 0x020; + const unsigned int BIB = 0x048; + + +}; + + +#endif // BCCANDRIVERTINYCAN_H diff --git a/bctransmitter.cpp b/bctransmitter.cpp new file mode 100644 index 0000000..aaeee5e --- /dev/null +++ b/bctransmitter.cpp @@ -0,0 +1,131 @@ +#include +#include + +#include + + +BCTransmitter::BCTransmitter(QObject *parent) + : QObject(parent), _isBusy(false) +{ +} + + +void BCTransmitter::onToggleConnectionState( bool connect ) +{ + if( connect ) + { + if( BCCanDriver::sIdle == _canDriver.getState() ) + _canDriver.onStartDriver(); + + int hwVersion = _canDriver.getValue( BCDevice::ID::Console, BC::ID::Cons_Rev_Hw); + + if (hwVersion == 0) + { + qDebug() << "Console not responding"; + } + else + { + /* + swVersion = getValue(CONSOLE, CONSOLE_REF_SW); + printf( "Console information:" _NL + " hardware version ........: %02d" _NL + " software version ........: %02d" _NL + " assistance level ........: %d" _NL, + hwVersion, swVersion, + getValue(CONSOLE, CONSOLE_ASSIST_INITLEVEL) + ); + + if (!gNoSerialNumbers) + printf( " part number .............: %05d" _NL + " item number .............: %05d" _NL _NL, + ((getValue(CONSOLE, CONSOLE_SN_PN_HI) << 8) + getValue(CONSOLE, CONSOLE_SN_PN_LO)), + ((getValue(CONSOLE, CONSOLE_SN_ITEM_HI) << 8) + getValue(CONSOLE, CONSOLE_SN_ITEM_LO)) + ); + */ + + } + + qDebug() << " ---HAIL to the kings: " << hwVersion; + } +} + + +void BCTransmitter::enqueueValueCommand(BC::OpID opID, const BCValue& value) +{ + QMutexLocker locker(&_mutex); + _valueQueue.enqueue( std::ref(value) ); + + // Logging (Thread-safe via Signal) + //emit messageLogged(QString("Command %1 queued.").arg(cmd.label)); + + // wir wollen nicht den ganzen Value verschicken, erstrecht + // wollen wir 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 enqueueValueCommand sofort zurückkehrt (non-blocking für den Aufrufer). + + //QMetaObject::invokeMethod(this, "processValueCommand", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, [this, opID]() + { + this->processValueCommand(opID); + }, Qt::QueuedConnection ); +} + +void BCTransmitter::processValueCommand( BC::OpID opID ) +{ + + if (_isBusy) + return; + + _isBusy = true; + + while (true) + { + BCValue currentValue; + { + QMutexLocker locker(&_mutex); + if (_valueQueue.isEmpty()) + { + _isBusy = false; + break; // Schleife verlassen, warten auf neue Events + } + currentValue =_valueQueue.dequeue(); + } // Mutex wird hier freigegeben! WICHTIG: Execute ohne Lock! + + std::optional result; + // 3. Ausführung (Dauert potentiell lange) + if( opID == BC::OpID::ReadValue ) + result = executeRead(currentValue); + else if( opID == BC::OpID::WriteValue ) + result = executeRead(currentValue); + + //emit commandFinished(cmd.id, true); + //emit commandFinished(0, true); + } +} + +std::optional BCTransmitter::executeRead(const BCValue& value) +{ + + std::optional result; + switch( value.deviceID ) + { + // hier lacht der blaue riesenhase! + case BCDevice::ID::Invalid: + case BCDevice::ID::Console: + case BCDevice::ID::Console_Master: + case BCDevice::ID::Battery: + case BCDevice::ID::Motor: + case BCDevice::ID::BIB: + case BCDevice::ID::Sensor: + break; + }; +} + + +std::optional BCTransmitter::executeWrite(const BCValue& value) +{ + +} diff --git a/bctransmitter.h b/bctransmitter.h new file mode 100644 index 0000000..ec44703 --- /dev/null +++ b/bctransmitter.h @@ -0,0 +1,47 @@ +#ifndef BCTRANSMITTER_H +#define BCTRANSMITTER_H + +#include +#include +#include +#include +#include + +#include +#include + +class BCTransmitter : public QObject +{ + Q_OBJECT + +public: + + explicit BCTransmitter(QObject *parent = nullptr); + +public slots: + + void onToggleConnectionState( bool connect ); + void enqueueValueCommand(BC::OpID opID, const BCValue& value); + void processValueCommand(BC::OpID opID); + +signals: + + void commandFinished(int id, bool success); + void messageLogged(const QString& msg); + +private: + + std::optional executeRead(const BCValue& value); + std::optional executeWrite(const BCValue& value); + + using BCValueQueue = QQueue>; + + BCValueQueue _valueQueue; + QMutex _mutex; + std::atomic _isBusy{ false }; + + BCCanDriverTinyCan _canDriver; +}; + + +#endif // BCTRANSMITTER_H