Skip to content

Commit

Permalink
Devel2.0.25 (#73)
Browse files Browse the repository at this point in the history
* globalErrorSignal moved to lib loader: inheritance problems with static
* globalErrorSignal implementation: connect and fire
* globalErrorSignal connect wrapper
* lib loader global err signals hookup
* move GlobalSignaler to file CCanAccess.h/cpp to simplify implenetations
* introduce port status change signals
* move implementation code to cpp files
* corrected CanModule_bus_state
* signal send error format kept everywhere
* udev debugging for peak for cal9, working for cc7 as well
* add global err signals windows and linux to libloader
* static init call for LogIt ptr, drop thread LogIt init by inheritance
* restructure Dll LogIt init for vendor libs, explicitly pass static ptr
* suppress double call to stats onTransmit when transmitting
* replace chrono::system_clock with chrono::high_resolution_clock everywhere to see if that works
  • Loading branch information
meeludwig authored Apr 18, 2023
1 parent d59900b commit 965becb
Show file tree
Hide file tree
Showing 28 changed files with 1,009 additions and 562 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/).


### 2.0.25 [ 16.march.2023 ]
we have 4 types of signals
- 1-bus specific message sending (CAN message transmission)
- 2-bus specific errors (classical bus error detection)
- 3-bus specific port status changes (ecah time status or nb. of handler connection changes, not neccessarily an error)
- 4-global errors, non bus specific (e.g. port opening problems, lib problems, high level global problems, NOT port specific)
- peak for cal9 (kernel5) and cc7 (kernel3): fixed udev calls, found a common solution for both kernel versions
- peak: kernel3 driver 8.8.1, kernel5 driver 8.15.2. If you use 8.15.2 on a cc7 you get a kernel corruption
- review LogIt component logging for all vendors to fix log level propagation, strange static method declaration for window: must not have implementation in the header
- cleanup "#pragma once" and replace with classical include guard since it is deprecated


### 2.0.24 [ 30.jan.2023 ]
- socketcan: reworks for CanOpenNG server integration, keeping compatibility with other implementations
- send remote requests also with stats, error signals and reconnection thread
Expand Down
15 changes: 11 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#
#
cmake_minimum_required( VERSION 3.0 )
project( CanModuleProject LANGUAGES C CXX VERSION 2.0.24 )
project( CanModuleProject LANGUAGES C CXX VERSION 2.0.25 )
message(STATUS "[${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}]: CanModule version= ${PROJECT_VERSION}" )

cmake_policy(SET CMP0054 NEW)
Expand Down Expand Up @@ -70,8 +70,14 @@ function ( clone_LogIt )
message(STATUS "[${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}]: cloning LogIt from github. *NOTE* cloning version [${LOGIT_VERSION}]")
if ( NOT EXISTS ${PROJECT_BINARY_DIR}/LogIt )
execute_process(COMMAND mkdir -p ${PROJECT_BINARY_DIR} )
execute_process(COMMAND pwd )
execute_process(COMMAND git clone -b ${LOGIT_VERSION} https://github.com/quasar-team/LogIt.git WORKING_DIRECTORY ${PROJECT_BINARY_DIR} )
#execute_process(COMMAND pwd )
#execute_process(COMMAND git clone -b ${LOGIT_VERSION} https://github.com/quasar-team/LogIt.git WORKING_DIRECTORY ${PROJECT_BINARY_DIR} )
#xxxx
execute_process(COMMAND git clone -b ${LOGIT_VERSION} https://github.com/quasar-team/LogIt.git ${PROJECT_BINARY_DIR}/LogIt WORKING_DIRECTORY ${PROJECT_BINARY_DIR} )


#add_subdirectory( ${PROJECT_BINARY_DIR}/LogIt build )
include_directories( ${PROJECT_BINARY_DIR}/LogIt/include build )

#execute_process(COMMAND git clone -b ${LOGIT_VERSION} https://github.com/quasar-team/LogIt.git WORKING_DIRECTORY ./build )
message(STATUS "[${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}]: PROJECT_BINARY_DIR= ${PROJECT_BINARY_DIR}")
Expand Down Expand Up @@ -114,6 +120,7 @@ if( STANDALONE_BUILD )
clone_LogIt() # clones into ${PROJECT_SOURCE_DIR}/build/build/CanModule/LogIt
message(STATUS "[${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}] PROJECT_BINARY_DIR= ${PROJECT_BINARY_DIR}")
add_subdirectory( ${PROJECT_BINARY_DIR}/LogIt build )
message(STATUS "[${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}] added LogIt subdir to build: ${PROJECT_BINARY_DIR}/LogIt")
else()
locateLogItSharedLib()
message(STATUS "[${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}]: Using LogIt library: LOGIT_LIB [${LOGIT_LIB}]")
Expand All @@ -128,7 +135,7 @@ link_directories( ${BOOST_PATH_LIBS} )
# we have a mock up build without any vendor libs as well, for CI where we don't want to distribute vendor libs
# preferably inject this from the toolchain : set ( CANMODULE_BUILD_VENDORS ON )
# if you have nothing declared, vendors will be OFF
message(STATUS "[${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}]: building vendor libs is ${BUILD_VENDORS}")
message(STATUS "[${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}]: building vendor libs CANMODULE_BUILD_VENDORS= ${CANMODULE_BUILD_VENDORS}")
if ( NOT DEFINED CANMODULE_BUILD_VENDORS )
set ( CANMODULE_BUILD_VENDORS OFF )
message(STATUS "[${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}] you have not specified if you want to build the vendor code or not ( CANMODULE_BUILD_VENDORS ). Set to OFF")
Expand Down
231 changes: 88 additions & 143 deletions CanInterface/include/CCanAccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,52 @@
#include <VERSION.h>



/*
* CCanAccess is an abstract class that defines the interface for controlling a canbus. Different implementations for different hardware and platforms should
* inherit this class and implement the pure virtual methods.
*/
namespace CanModule
{
/**
* one (singleton) global signal for errors independent of bus. This is needed to listen to bus opening/creation errors where the bus does not yet exist.
* All buses and errors go into this signal.
*
* The recommendation is:
* connect a handler to the global signal
* open the bus
* if successful, connect bus specific handlers for errors, receptions, port status changes
* if not successful, keep global handler and try again (server logic)
* of course you can keep the global handler connected as well, but you might also disconnect it (let the sigleton object go out of scope)
*
* boost::signal2 need implicit this-> pointers to work, that means they only work as methods of existing objects. So we need a class
* for specifically implementing the globalErrorSignal, which is independent of the existence of lib and bus instances.
*/
class GlobalErrorSignaler /* singleton */
{

private:
GlobalErrorSignaler(){};
~GlobalErrorSignaler();

static GlobalErrorSignaler *instancePtr;
static LogItInstance *m_st_logIt;
static Log::LogComponentHandle m_st_lh;
boost::signals2::signal<void (const int,const char *,timeval &) > globalErrorSignal;

public:
SHARED_LIB_EXPORT_DEFN static GlobalErrorSignaler* getInstance();
SHARED_LIB_EXPORT_DEFN bool connectHandler( void (*fcnPtr)( int, const char*, timeval ) );
SHARED_LIB_EXPORT_DEFN bool disconnectHandler( void (*fcnPtr)( int, const char*, timeval ) );
SHARED_LIB_EXPORT_DEFN void disconnectAllHandlers();
SHARED_LIB_EXPORT_DEFN void fireSignal( const int code, const char *msg );


static void initializeLogIt(LogItInstance *remoteInstance);


};


const std::string LogItComponentName = "CanModule";
#define MLOG(LEVEL,THIS) LOG(Log::LEVEL) << __FUNCTION__ << " " << CanModule::LogItComponentName << " bus= " << THIS->getBusName() << " "
Expand Down Expand Up @@ -137,22 +177,25 @@ struct CanParameters {
class CCanAccess
{

public:

public: // accessible API
CCanAccess():
m_reconnectCondition( CanModule::ReconnectAutoCondition::sendFail ),
m_reconnectAction( CanModule::ReconnectAction::singleBus ),
m_timeoutOnReception( 120 ),
m_failedSendCountdown( 10 ),
m_maxFailedSendCount( 10 ),
m_lh(0),
m_logItRemoteInstance( NULL )
m_logItRemoteInstance( NULL ),
m_portStatus( 0 ),
m_canPortStateChanged_nbSlots_current( 0 ),
m_canPortStateChanged_nbSlots_previous( 0 )
{
resetTimeoutOnReception();
resetTimeNow();
CanModule::version();
};

virtual ~CCanAccess() {};

/**
* Method that sends a remote request trough the can bus channel. If the method createBus was not called before this, sendMessage will fail, as there is no
Expand All @@ -162,7 +205,6 @@ class CCanAccess
*/
virtual bool sendRemoteRequest(short cobID) = 0;


/**
* Method that initialises a can bus channel. The following methods called upon the same object will be using this initialised channel.
*
Expand Down Expand Up @@ -191,7 +233,6 @@ class CCanAccess
* @return: Was the message sent successfully? If not, we may reconnect.
*/
virtual bool sendMessage(short cobID, unsigned char len, unsigned char *message, bool rtr = false) = 0;

virtual bool sendMessage(CanMessage *canm)
{
if ( canm->c_id < 0 || canm->c_id > 2047 ){
Expand All @@ -203,10 +244,6 @@ class CCanAccess
return sendMessage(short(canm->c_id), canm->c_dlc, canm->c_data, canm->c_rtr);
}

/**
* Returns the can bus name (from buffered data)
*/
std::string& getBusName() { return m_sBusName; }

/**
* UNIFIED PORT status
Expand Down Expand Up @@ -309,82 +346,11 @@ class CCanAccess
*/
virtual uint32_t getPortBitrate() = 0;

/*
* Signal that gets called when a can Message is received from the initialised can bus.
* In order to process this message manually, a handler needs to be connected to the signal.
*
* Example: myCCanAccessPointer->canMessageCame.connect(&myMessageRecievedHandler);
*/
boost::signals2::signal<void (const CanMessage &) > canMessageCame;

virtual ~CCanAccess() {};


/**
* Signal that gets called when a can Error is detected in the message
* In order to process this message manually, a handler needs to be connected to the signal.
*
* Example: myCCanAccessPointer->canMessageError.connect(&myErrorRecievedHandler);
* Returns the CanStatistics object, which contains bus statistics, bus load, counters and time stamps
*/
boost::signals2::signal<void (const int,const char *,timeval &) > canMessageError;

/**
* signal gets called whenever there is a can port status change, across all vendors
* the int is the enum CanModule::CanModuleUtils::CanModule_bus_state
* In order to process this message, a handler needs to be connected to the signal.
*
* Example: myCCanAccessPointer->canPortStateChanged.connect(&myPortSateChangedRecievedHandler);
*/
boost::signals2::signal<void (const int,const char *,timeval &) > canPortStateChanged;

// Returns the CanStatistics object.
virtual void getStatistics( CanStatistics & result ) = 0;

inline bool initialiseLogging(LogItInstance* remoteInstance)
{
bool ret = Log::initializeDllLogging(remoteInstance);
m_logItRemoteInstance = remoteInstance;
return ret;
}

/**
* the LogIt instance is NOT shared by inheritance in windows, the instance has to be passed explicitly
* from the parent
*/
LogItInstance* getLogItInstance()
{
return( m_logItRemoteInstance );
}

std::vector<std::string> parseNameAndParameters( std::string name, std::string parameters);

// non blocking
void triggerReconnectionThread(){
// std::cout << "==> trigger reconnection thread " << getBusName() << std::endl;
m_reconnectTrigger = true;
m_reconnection_cv.notify_one();
}

// blocking
void waitForReconnectionThreadTrigger(){
std::unique_lock<std::mutex> lk(m_reconnection_mtx);
while ( !m_reconnectTrigger ) m_reconnection_cv.wait( lk );
m_reconnectTrigger = false;
}

void decreaseSendFailedCountdown(){
if ( m_failedSendCountdown > 0 )
m_failedSendCountdown--;
LOG(Log::TRC, m_lh) << __FUNCTION__ << " decrease m_failedSendCountdown= " << m_failedSendCountdown;
}

void resetSendFailedCountdown(){
m_failedSendCountdown = m_maxFailedSendCount;
LOG(Log::TRC, m_lh) << __FUNCTION__ << " reset internal m_failedSendCountdown= " << m_failedSendCountdown;
}



/**
* configuring the reconnection behavior: a condition triggers an action. The implementation is implementation-specific
* because not all vendor APIs permit the same behavior: not all combinations are available for all vendors/implementations.
Expand Down Expand Up @@ -452,38 +418,49 @@ class CCanAccess
*/
virtual void fetchAndPublishCanPortState () = 0;

/*
* Signal that gets called when a can Message is received from the initialised can bus.
* In order to process this message manually, a handler needs to be connected to the signal.
*
* Example: myCCanAccessPointer->canMessageCame.connect(&myMessageRecievedHandler);
*/
boost::signals2::signal<void (const CanMessage &) > canMessageCame;


/**
* just translate the ugly r.condition enum into a user-friendly string for convenience and logging.
* Signal that gets called when a can Error is detected in the message
* In order to process this message manually, a handler needs to be connected to the signal.
*
* Example: myCCanAccessPointer->canMessageError.connect(&myErrorRecievedHandler);
*/
static std::string reconnectConditionString(CanModule::ReconnectAutoCondition c) {
switch (c) {
case ReconnectAutoCondition::sendFail: return(" sendFail");
case ReconnectAutoCondition::timeoutOnReception: return(" timeoutOnReception");
case ReconnectAutoCondition::never: return(" never");
}
return(" unknown condition");
}
boost::signals2::signal<void (const int, const char *, timeval &) > canMessageError;

/**
* just translate the ugly r.action enum into a user-friendly string for convenience and logging.
* signal gets called whenever there is a can port status change, across all vendors
* the int is the enum CanModule::CanModuleUtils::CanModule_bus_state
* In order to process this message, a handler needs to be connected to the signal.
*
* Example: myCCanAccessPointer->canPortStateChanged.connect(&myPortSateChangedRecievedHandler);
*/
static std::string reconnectActionString(CanModule::ReconnectAction c) {
switch (c) {
case ReconnectAction::allBusesOnBridge: return(" allBusesOnBridge");
case ReconnectAction::singleBus: return(" singleBus");
}
return(" unknown action");
}
boost::signals2::signal<void (const int, const char *, timeval &) > canPortStateChanged;

std::string& getBusName();
bool initialiseLogging(LogItInstance* remoteInstance);
LogItInstance* getLogItInstance();
std::vector<std::string> parseNameAndParameters( std::string name, std::string parameters);
void triggerReconnectionThread();
void waitForReconnectionThreadTrigger();
void decreaseSendFailedCountdown();
void resetSendFailedCountdown(){ m_failedSendCountdown = m_maxFailedSendCount; }

static std::string reconnectConditionString(CanModule::ReconnectAutoCondition c);
static std::string reconnectActionString(CanModule::ReconnectAction c);

protected:

protected: // not public, but inherited
std::string m_sBusName;
CanParameters m_CanParameters;

unsigned int m_portStatus; // port status

// reconnection, reconnection thread triggering
CanModule::ReconnectAutoCondition m_reconnectCondition;
CanModule::ReconnectAction m_reconnectAction;
Expand All @@ -495,53 +472,21 @@ class CCanAccess
std::mutex m_reconnection_mtx; // trigger stuff
std::condition_variable m_reconnection_cv; // trigger stuff

bool hasTimeoutOnReception();
void resetTimeoutOnReception() { m_dreceived = std::chrono::high_resolution_clock::now(); } // reset the internal reconnection timeout counter
void resetTimeNow() { m_dnow = std::chrono::high_resolution_clock::now(); }
void publishPortStatusChanged ( unsigned int status );

/**
* compared to the last received message, are we in timeout?
*/
bool hasTimeoutOnReception() {
m_dnow = std::chrono::high_resolution_clock::now();
auto delta_us = std::chrono::duration<double, std::chrono::microseconds::period>( m_dnow - m_dopen);
if ( delta_us.count() / 1000 > m_timeoutOnReception ) return true;
else return false;
}

/**
* reset the internal reconnection timeout counter
*/
void resetTimeoutOnReception() {
m_dreceived = std::chrono::high_resolution_clock::now();
}
void resetTimeNow() {
m_dnow = std::chrono::high_resolution_clock::now();
}

/**
* we publish port status via a "portStatusChanged" signal, for all vendors, if it has changed. This is just the standardized mechanism
*/
void publishPortStatusChanged ( unsigned int status )
{
if ( m_portStatus != status ){
// std::string msg = CanModule::CanModuleUtils::translateCanBusStateToText((CanModule::CanModuleUtils::CanModule_bus_state) status);
std::string msg = CanModule::translateCanBusStateToText((CanModule::CanModule_bus_state) status);

// got to be compatible with boost1.59.0 for windows
timeval ftTimeStamp;
auto now = std::chrono::system_clock::now();
auto nMicrosecs = std::chrono::duration_cast<std::chrono::microseconds>( now.time_since_epoch());
ftTimeStamp.tv_sec = nMicrosecs.count() / 1000000L;
ftTimeStamp.tv_usec = (nMicrosecs.count() % 1000000L) ;
canPortStateChanged( status, msg.c_str(), ftTimeStamp ); // signal
}
m_portStatus = status;
}



private:
private: // object scope
static LogItInstance* st_logItRemoteInstance;
Log::LogComponentHandle m_lh;
LogItInstance* m_logItRemoteInstance;
std::chrono::time_point<std::chrono::high_resolution_clock> m_dnow, m_dreceived, m_dtransmitted, m_dopen;
unsigned int m_portStatus;
// keep track of connected handlers. If the number changes, send a signal so that all handlers are updated
unsigned int m_canPortStateChanged_nbSlots_current;
unsigned int m_canPortStateChanged_nbSlots_previous;


};
Expand Down
Loading

0 comments on commit 965becb

Please sign in to comment.