/* Copyright (c) 20011 Hovanes Egiyan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "BaseIUMgr.hh" using namespace std; //! Sleeping time between the scans (in microseconds) unsigned long BaseIUMgr::bmgSleepTime = 500000; //unsigned long BaseIUMgr::bmgSleepTime = 100000; //unsigned long BaseIUMgr::bmgSleepTime = 5000000; //! Sleeping time between resetting busses (in microseconds) unsigned long BaseIUMgr::bmgResetSleepTime = 30000000; //! Map of all the buses. //! The first key is the port name, the corresponding value //! is a pair with the "first" being the exit flag, and the "second" the //! pointer to the bus object for that port name. map > BaseIUMgr::bmgBusMap; // Communication failure counts unsigned long BaseIUMgr::bmgFailureCount = 0; // Maximum number of communication failures before a bus gets reset unsigned long BaseIUMgr::bmgMaxFailureCount = 1; // Maximum number of communication failures before a bus gets reset unsigned long BaseIUMgr::bmgMaxBusFailureCount = 2; //! Mutex and its attributes for this class pthread_mutex_t BaseIUMgr::bmgGlobMutex; pthread_mutexattr_t BaseIUMgr::bmgGlobMtxAttr; pthread_cond_t BaseIUMgr::bmgThrdStop; //! Dummy variable is initialized to setup the mutex for the class static volatile int dummyInt = BaseIUMgr::InitGlobalMutex(); unsigned BaseIUMgr::bmgDebFlag = 0; //! Debug level //! Start a new CAN bus instance from within a newly launched thread. //! Crates a new bus which will start a loop which never exits unless the //! ForcedExit flag is set. void* BaseIUMgr::Thread( void* argPtr ) { BaseIUVirtBusID* busID = (BaseIUVirtBusID*) argPtr; string portName = busID->GetPort(); string ipNum = busID->GetIP(); string busType = busID->GetType(); // unsigned busNum = busID->GetBus();; cout << "BaseIUMgr::Thread(): Port called " << setw(3) << hex << showbase << portName << " with IP " << ipNum << " and CANbus " << portName << " starts its thread function" << endl; bool exitFlag = false; while ( !exitFlag ) { // Crate a new bus. This function will not return until exit flag for the bus is set // or there were too many communication faults. The loop for this bus is called from // CreateNewBus( busID ) method. BaseIUVirtBus* newBus = CreateNewBus( busID ); // If got here, then the loop for this bus exited. // If bus did not get created or exit flag was set the while loop will exit if( newBus == 0 || GetBusExitFlag( portName ) ) { exitFlag = true; } // classLock.Lock(); cout << "Removing CANbus port " << portName << " from the CANbus port map" << endl; cout << "Length is " << bmgBusMap.size() << endl; RemoveBus(portName); cout << "Length is " << bmgBusMap.size() << endl; try { cout << "Will call bus destructor" << endl; delete newBus; } catch (BaseIUDevice::FailedCloseDevice& err) { cerr << err.GetMessage() << endl; MtxLock classLock( bmgGlobMutex); bmgFailureCount++; return 0; } usleep(BaseIUMgr::bmgResetSleepTime); if( bmgFailureCount > bmgMaxFailureCount ) { exitFlag = true; } } cout << "Exiting the thread for CANbus on port " << portName << endl; return 0; } // Crate new bus and start the loop BaseIUVirtBus* BaseIUMgr::CreateNewBus( BaseIUVirtBusID* busID ) { string portName = busID->GetPort(); string ipNum = busID->GetIP(); string busType = busID->GetType(); //! Create a new bus object BaseIUVirtBus* newBus = 0; cout << "Will create a new bus on port " << portName << " for IP " << ipNum << endl; try { if ( busType == "base" ) newBus = new BaseIUBaseBus(*busID); else if( busType == "chassis" ) newBus = new BaseIUChassisBus(*busID); // If the bus type is none of the above throw an exception. This is supposed to be a lethal condition else throw( "Bad CAN-bus type!!! Valid options are 'base' and 'power'" ); } catch ( BaseIUDevice::FailedOpenDevice& err ) { cerr << "Exception: " << err.GetMessage() << endl; cerr << " Giving up on this thread for port " << hex << showbase << portName << endl; //! Send the signal that the IOC thread can continue now MtxLock classLock( bmgGlobMutex ); bmgFailureCount++; pthread_cond_signal( &bmgThrdStop ); classLock.Unlock(); return 0; } catch ( BaseIUDevice::FailedWriteDevice& err ) { cerr << "Exception: " << err.GetMessage() << endl; cerr << " Giving up on this thread for port " << hex << showbase << portName << endl; MtxLock classLock( bmgGlobMutex ); bmgFailureCount++; classLock.Unlock(); pthread_cond_signal( &bmgThrdStop ); return 0; } catch ( BaseIUDevice::FailedReadDevice& err ) { cerr << "Exception: " << err.GetMessage() << endl; cerr << " Giving up on this thread for port " << hex << showbase << portName << endl; //! Send the signal that the IOC thread can continue now MtxLock classLock( bmgGlobMutex ); bmgFailureCount++; classLock.Unlock(); pthread_cond_signal( &bmgThrdStop ); return 0; } //! Add the new bus and its exit flag to the bus map cout << "Adding bus " << newBus->GetDevice()->GetPortName() << " to the bus map " << endl; AddNewBus( portName, newBus ); //! Loop just once to read back the parameters before allowing the //! records on IOC to use this connection number. The lock //! is removed for the Loop to work, but the IOC will still need //! to wait for signal. cout << "Reading back the parameters from the hardware for port " << portName << endl; Loop( portName, 1, true ); cout << "Parameters from the hardware for port " << portName << " have been read " << endl; //! The bus constructor was called, parameters were read from the hardware. //! The IOC can continue on. pthread_cond_signal( &bmgThrdStop ); cout << "Starting loop for bus on port " << portName << endl; //! Start the loop for the bus which will not return for a //! while until exit flag for the device is not raised Loop( portName ); // Exit when loop ends return newBus; } //! Static method called from outside to start a new CANbus. //! It launches a new thread and exits int BaseIUMgr::StartBusThread( BaseIUVirtBusID& busID ) { cout << "Length is " << bmgBusMap.size() << endl; MtxLock classLock( bmgGlobMutex ); string portName = busID.GetPort(); if( bmgBusMap.find( portName ) != bmgBusMap.end() ) { cerr << "BaseIUMgr::StartBusThread(): Bus for port " << portName << " is already running " << endl; return(-1); } classLock.Unlock(); //! Create a description object for this bus //! this will be passed as the message pointer to the new thread BaseIUVirtBusID* thMsg = new BaseIUVirtBusID( busID ); //! Start the thread for this bus pthread_t thID; int thStat = pthread_create( &thID, NULL, (void* (*)(void*))BaseIUMgr::Thread, (void*) thMsg); if ( thStat !=0 ) { cerr << "BaseIUMgr::StartBusThread(): pthread_create(" << hex << showbase << thID << "[" << dec << portName << "],...) failure" << endl; return -1 ; } //! Wait until the object for the bus in the thread is created or //! failed to be created. This delay is to avoid IOC scanning buses //! that have not completed (or failed) the initialization. pthread_cond_wait( &bmgThrdStop, &bmgGlobMutex ); // classLock.Unlock(); // Can delete the object passed when crating the thread delete thMsg; cout << "BaseIUMgr::StartBusThread(): pthread_create(" << hex << showbase << thID << "[" << dec << portName << "],...) done." << endl; return 0; } //! Method to set forced exit flag to make the thread exit int BaseIUMgr::StopBus( string portName ) { MtxLock classLock( bmgGlobMutex ); if( bmgBusMap.find( portName ) == bmgBusMap.end() ) { cerr << "BaseIUMgr::StopBus: Bus on port " << portName << " is not running " << endl; return(-1); } cout << "BaseIUMgr::StopBus() : Setting Forced Exit Flag for CANbus on port " << portName << endl; //! Set the flag for the exit to "true" will cause the loop in the thread //! exit and the destructor of the bus object for this connection //! to be called. classLock.Unlock(); SetBusExitFlag( portName, true ); return 0; } // This method tries to start the first nBusses on a given Anagate device specified // by the IP address in busID. The port names are created using the port specified in // busID with ":" and a bus name appended a the end. Bus name specified in busID // should be blank. int BaseIUMgr::StartThreads4Buses(BaseIUVirtBusID& busID, unsigned nBusses) { if (busID.GetBusStr() != "E") { // The bus name has to be blank, otherwise this function will do nothing, // just print messages. cerr << "Requesting start of all busses on port " << busID.GetPort() << " while specifying bus name different from E " << busID.GetBusStr() << endl; return -1; } for (unsigned iBus = 0; iBus < nBusses; iBus++) { BaseIUVirtBusID definedBusID = busID; definedBusID = ( definedBusID + iBus ); // definedBusID.SetBus(iBus); // definedBusID.SetPort( busID.GetPort() + ":" + definedBusID.GetBusStr() ); StartBusThread(definedBusID); } return 0; } //! Method constantly looping until the exit flag is not set //! This loop executes within the thread of the bus void BaseIUMgr::Loop(string portName, long nMaxSyncs, bool syncFromHW) { bool exitFlag = false; BaseIUVirtBus* busPtr = 0; long iSync = 0; // Continue looping if exit flag is not raised and either the number of syncs is less that requested // number of syncs or the requested number of syncs is less than zero (infinite number of syncs). while (!exitFlag && (iSync < nMaxSyncs || nMaxSyncs < 0)) { //! Check the exit flag, and if it set, break out of the loop exitFlag = GetBusExitFlag(portName); // Check for exit flag busPtr = GetBus(portName); // Get the bus pointer for which this loop is running //! Sync the buses and all its subcomponents if (bmgDebFlag > 0) cout << "Syncing bus for port " << portName << endl; if (!exitFlag) { try { busPtr->Sync(syncFromHW); iSync++; } //! In case of read or write exceptions just continue catch (BaseIUDevice::FailedWriteDevice & err) { cerr << "BaseIUMgr::Loop() : Exception caught - "; cerr << err.GetMessage() << endl; MtxLock classLock(bmgGlobMutex); bmgFailureCount++; continue; } catch (BaseIUDevice::FailedReadDevice & err) { cerr << "BaseIUMgr::Loop() : Exception caught - "; cerr << err.GetMessage() << endl; MtxLock classLock(bmgGlobMutex); bmgFailureCount++; continue; } // if( busPtr->GetFailureCount() > bmgMaxBusFailureCount ) { // exitFlag = true; // } } if (!syncFromHW) usleep(BaseIUMgr::bmgSleepTime); // sleep for a while in usec } cout << "Exiting the loop for CANbus on port " << portName << endl; return; } //! Initialize global mutex int BaseIUMgr::InitGlobalMutex() { pthread_mutexattr_init( &bmgGlobMtxAttr ); pthread_mutexattr_setpshared( &bmgGlobMtxAttr, PTHREAD_PROCESS_PRIVATE ); pthread_mutex_init( &bmgGlobMutex, &bmgGlobMtxAttr ); pthread_cond_init( &bmgThrdStop, NULL ); return 0; }