/* 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 "BaseIUVirtBus.hh" using namespace std; // Initialize mutexes volatile int dummyInt = BaseIUVirtBus::InitGlobalMutex(); //! Sleep time in microseconds before next round of poling unsigned long BaseIUVirtBus::bvbSleepTime = 50000; // Sleep time (usec) before checking the FIFO again //! Timeout time in microsec for a board to respond to readback request for a parameter long BaseIUVirtBus::bvbTimeOutTime = 2000000; long BaseIUVirtBus::bvbTimeOutTimeInit = 6000000; //! Constructor to create a new object and open new communication port. After //! creating the serial port object it creates the BaseIUBoard //! objects corresponding to the boards in this BaseIU bus. BaseIUVirtBus::BaseIUVirtBus(BaseIUVirtBusID& busID) : bvbCommOK(false), bvbFailureCount(0), bvbBusID(busID) { if (bmDebFlag > 1) { cout << "BaseIUVirtBus::BaseIUVirtBus() : Started constructing CAN bus for port " << hex << showbase << busID.GetPort() << endl; } //! Create the CANbus communication device object MtxLock objLock(bmMutex); bvbCommOK = false; bmDevice = new BaseIUDevice(busID); bvbCommOK = true; objLock.Unlock(); if (bmDebFlag > 1) cout << "BaseIUVirtBus::BaseIUVirtBus() : Finished constructing virtual bus for port " << hex << showbase << bmDevice->GetPortName() << endl; return; } //! Copy constructor, should not be used BaseIUVirtBus::BaseIUVirtBus(BaseIUVirtBus& bus) : BaseIUModule(bus) { cerr << "BaseIUVirtBus::BaseIUVirtBus() : Error, copy constructor cannot be used" << endl; return; } //! Destructor is expected to be called after the thread corresponding //! to this instance is already stopped. A this point all it needs to //! do is to close the serial port if it is open and destroy the boards on this bus. BaseIUVirtBus::~BaseIUVirtBus() { if ( bmDebFlag > 1 ) { cout << "BaseIUVirtBus::~BaseIUVirtBus() : Bus destructor for port " << bmDevice->GetPortName() << endl; } MtxLock objLock( bmMutex ); for ( map::iterator it = bvbBoard.begin(); it != bvbBoard.end(); it++ ) { BaseIUVirtBoard* boardAdddress = it->second; if ( boardAdddress != 0 ) delete boardAdddress; } if ( bmDevice != 0 ) delete bmDevice; cout << "BaseIUVirtBus::~BaseIUVirtBus() : Bus destruction done" << endl; return; } //! Assignment operator for the bus, should not be used BaseIUVirtBus& BaseIUVirtBus::operator=(BaseIUVirtBus& bus) { cerr << "BaseIUVirtBus::operator=() : Error, assignment operator cannot be used" << endl; return *this; } // Create and return a vector that contains a list of base addresses // that currently can communicate on the bus. Will send a message to // collect ID from the bus and collect them pushing them into a vector. vector BaseIUVirtBus::GetAddressVector() { vector addrVec; //! Get the vector of board addresses that are alive on the bus try { addrVec = bmDevice->GetAddressVector(); } //! In case of read or write exceptions return with communication //! status set to false catch ( BaseIUDevice::FailedWriteDevice& err ) { cerr << err.GetMessage() << endl; MtxLock objLock( bmMutex ); bvbCommOK = false; bvbFailureCount++; return vector(); } catch ( BaseIUDevice::FailedReadDevice& err ) { cerr << err.GetMessage() << endl; MtxLock objLock( bmMutex ); bvbCommOK = false; bvbFailureCount++; return vector(); } if (bmDebFlag > 1) { cout << "BaseIUVirtBus::GetAddressVector() : There were " << hex << showbase << addrVec.size() << " responses on the bus for port " << bmDevice->GetPortName() << endl; } //! Check if all the boards in the map responded if ( CheckAddressVector(addrVec) ) { //! Set communication status to true if we have consistent responses MtxLock objLock(bmMutex); bvbCommOK = true; if (bmDebFlag > 1) { cout << "BaseIUVirtBus::GetAddressVector() : The responses on the bus for port " << bmDevice->GetPortName() << " are consistent " << endl; } } else { //! Set communication status to false if (bmDebFlag > 1) { cout << "BaseIUVirtBus::GetAddressVector() : The responses on the bus for port " << bmDevice->GetPortName() << " are not consistent " << endl; } MtxLock objLock(bmMutex); bvbCommOK = false; bvbFailureCount++; } return addrVec; } //! Sync the particular bus with the hardware. It loops over all //! boards and calls Sync method of each board, which is supposed to //! chain down to every parameter in the bus. bool BaseIUVirtBus::Sync(bool syncFromHW) { if (bmDebFlag > 1) { cout << "BaseIUVirtBus::Sync() : Entered syncing for port " << hex << showbase << bmDevice->GetPortName() << " sync from HW " << syncFromHW << endl; } // Get the address vector vector addrVec; // cout << "Current list of addresses for bus " << bmDevice->GetPortName() << " is " << endl; // Get the address vector from what was already found the very first time. for ( map::iterator it = bvbBoard.begin(); it != bvbBoard.end(); it++ ) { int boardAddress = it->first; // BaseIUVirtBoard* boardPtr = it->second; // Do not add board with address 0 which is the broadcast address, // it will scre things up. if( boardAddress != 0 ) { addrVec.push_back( boardAddress ); // cout << " " << hex << showbase << boardAddress; } } // cout << dec << endl << " Address vector size is " << addrVec.size() << endl; if (addrVec.size() > 0) { if (bmDebFlag > 1) { cout << "BaseIUVirtBus::Sync() : Looping through parameters for port " << hex << showbase << bmDevice->GetPortName() << endl; } // If board with address 0 does not exist or there are more than one, // then throw a runtime exception. if( bvbBoard.count( 0 ) != 1 ) { stringstream ssMsg ; ssMsg << "BaseIUVirtBus::Sync: there are " << bvbBoard.count( 0 ) << " " "broadcast board object for bus " << bmDevice->GetPortName() << " , needs to be one and only one."; string sMsg = ssMsg.str(); throw ( runtime_error( ssMsg.str( ) ) ); } // The board with address 0 is the one for broadcast BaseIUVirtBoard* fakeBoard = bvbBoard[0]; // Get all parameter types into a map map >& typeMap = fakeBoard->GetParMap(); // cout << "Parameter map size for fake board on port " << bmDevice->GetPortName() << " is " << typeMap.size() << endl; map >::iterator itType; // loop over all parameter types found in the fake board for (itType = typeMap.begin(); itType != typeMap.end(); itType++) { string parType = itType->first; // cout << "Dealing with type " << parType << endl; // Get the map with pointers to the parameters map& nameMap = itType->second; map::iterator itName; for (itName = nameMap.begin(); itName != nameMap.end(); itName++) { string parName = itName->first; //! This is the parameter with fake address 0 BaseIUVirtPar* parPtr = itName->second; // cout << "Working on parameter " << parName << " at address " << hex << showbase // << parPtr << dec << " on port " << bmDevice->GetPortName() << endl; // Sync parameters on all boards. The pointer to the parameter // parPtr on board with address zero is there to identify which // parameter to sync right now. SyncParsOnBoards(parPtr, addrVec, syncFromHW); // Check if there are priority calls to be executed before going to the // the next parameter syncing. This is used for instance when setting all // bases on the bus on or off. if (bvbPriorityCall.size() > 0) cout << "Found " << bvbPriorityCall.size() << " calls in the call queue, will execute" << endl; for (unsigned iCall = 0; iCall < bvbPriorityCall.size(); iCall++) { PriorityFuncType callFunc = bvbPriorityCall[iCall]; callFunc(); // Execute the call in this thread } // Remove all entries from the vector of priority calls // after they have been executed. MtxLock objLock(bmMutex); bvbPriorityCall.erase(bvbPriorityCall.begin(), bvbPriorityCall.end()); objLock.Unlock(); } } } else { cerr << "BaseIUVirtBus::Sync() : Did not see any boards on bus for port " << hex << showbase << bmDevice->GetPortName() << " : IP " << bmDevice->GetIP() << " CANbus connector " << bmDevice->GetBus() << endl; } // Sync the parameters that belong to this bus directly, if any if (!this->SyncPars(addrVec, syncFromHW)) return false; if (bmDebFlag > 1) { cout << "BaseIUVirtBus::Sync() : Syncing done for bus on port " << bmDevice->GetPortName() << endl; } return true; } // Loop through the parameters of the bus itself (not to the bases) and sync them bool BaseIUVirtBus::SyncPars(vector& addrVec, bool syncFromHW ) { // MtxLock objLock( bmMutex ); // cout << "The size of the parameter map for " << bmDevice->GetPortName() << " is " << bvbPar.size() << endl; for (map >::iterator it1 = bvbPar.begin(); it1 != bvbPar.end(); it1++) { // The first key of this map of string type is the type of the parameter string parType = it1->first; // cout << " parameter type is " << parType << endl; map& parMap = it1->second; for (map::iterator it2 = parMap.begin(); it2 != parMap.end(); it2++) { // The second key of string type is the name of the parameter BaseIUVirtPar* parPtr = it2->second; // cout << "Par type is " << parType << " , name is " << it2->first << ", pointer is " << hex << showbase << parPtr << endl; if (parPtr != 0) { SyncBusPar(parPtr, addrVec, syncFromHW); } } } return true; } // Synch parameters with name of parPtr on all boards on this bus bool BaseIUVirtBus::SyncParsOnBoards(BaseIUVirtPar* parPtr, vector& addrVec, bool syncFromHW) { bmDevice->ClearFIFO(); unsigned long startTime = GetTime(); // cout << "Will sync board parameter with address " << parPtr << endl; //! Send a readback request to all boards on the bus try { parPtr->ReadRequest(bmDevice, parPtr->GetName()); } catch ( BaseIUDevice::FailedWriteDevice& err ) { cerr << err.GetMessage() << endl; MtxLock objLock( bmMutex ); bvbCommOK = false; bvbFailureCount++; return false; } try { vector missVector = addrVec; bool timeOut = false; long deltaT = 0; //! Loop until all boards respond or we timeout, or until some communication problem while ( (missVector.size() > 0) && (!timeOut) ) { // cout << "Sleeping " << dec << bbSleepTime << " microseconds" << endl; bmDevice->Sleep( bvbSleepTime ); // sleep before trying tp read responses from the bus. // Try to sync the bases that still did not respond and get a new vector // of missing boards (bases). missVector = SyncMissingBoards( missVector, *parPtr, syncFromHW ); //! get the current time and calculate the elapsed time in useconds unsigned long currentTime = GetTime(); deltaT = currentTime - startTime; timeOut = (deltaT > GetTimeouTime( syncFromHW )); // if( !syncFromHW ) timeOut = ( deltaT >= 0 * bvbSleepTime ) ; // cout << "Start time is " << hex << startTime << " , stop time is " << // hex << currentTime << endl; // cout << "Delta T is " << dec << deltaT << endl; // cout << "missing " << hex << showbase << missVector.size() << // " , timeout is " << timeOut << " , bbComOK is " << bbCommOK << endl; } // If timeout occurred then check if the alarms will need to be set for the missing // boards for that particular parameter. if ( timeOut ) { cout << "BaseIUVirtBus::SyncParsOnBoards: Timed out for parameter " << parPtr->GetName() << " on bus " << bmDevice->GetPortName() << endl; CheckBoardParameterAlarms( parPtr, missVector, deltaT ); } } catch ( ... ) { std::runtime_error( "error" ); exit(-1); } return true; } // Loop over all missing boards and check their alarms for the given parameter // specified by the fakeParam argument. void BaseIUVirtBus::CheckBoardParameterAlarms( BaseIUVirtPar* fakePar, const vector& missingBoards, const long timeIncr ) { for( unsigned iBoard = 0; iBoard < missingBoards.size(); iBoard++ ) { int boardAddress = missingBoards[iBoard]; if ( bvbBoard.count( boardAddress ) > 0 ) { // Get the pointer for the parameter BaseIUVirtPar* parPtr = bvbBoard[boardAddress]->GetParameter( fakePar->GetType(), fakePar->GetName() ); // Increment the silent time for the parameter by timeIncr. This call will also check for the alarm condition // for the parameter. parPtr->IncrementSilentTime( timeIncr ); cout << "Incremented silent time for parameter " << parPtr->GetName() << " on board " << hex << showbase << boardAddress << dec << " by " << timeIncr << " microseconds " << endl; cout << "Alarm status for "<< parPtr->GetName() << " on board " << hex << showbase << boardAddress << dec << " is " << parPtr->GetStatus() << endl; } } return; } // Synch the bus parameters with name of parPtr bool BaseIUVirtBus::SyncBusPar(BaseIUVirtPar* parPtr, vector& addrVec, bool syncFromHW) { unsigned long startTime = GetTime(); //! Send a readback request to all boards on the bus try { parPtr->ReadRequest(bmDevice, parPtr->GetName()); } catch ( BaseIUDevice::FailedWriteDevice& err ) { cerr << err.GetMessage() << endl; MtxLock objLock( bmMutex ); bvbCommOK = false; bvbFailureCount++; return false; } vector missVector = addrVec; bool timeOut = false; long deltaT = 0; //! Loop until all boards respond or we timeout, or until some communication problem/ //! For board-idependent parameters the loop needs to be skipped. while ( (missVector.size() > 0) && (!timeOut) && parPtr->IsBoardDependent() ) { // cout << "Sleeping " << dec << bbSleepTime << " microseconds" << endl; bmDevice->Sleep(bvbSleepTime); // cout << "Waking up to check buffer" << endl; missVector = FindMissingBoards(missVector, *parPtr); //! get the current time and calculate the elapsed time in useconds unsigned long currentTime = GetTime(); deltaT = currentTime - startTime; timeOut = ( deltaT > GetTimeouTime( syncFromHW ) ); // cout << "Start time is " << hex << startTime << " , stop time is " << // hex << currentTime << dec << endl; // cout << "Delta T is " << dec << deltaT << endl; // cout << "missing " << hex << showbase << missVector.size() << // " , timeout is " << dec << timeOut << endl; } // If timeout occurred then check if the alarms will need to be set for this bus parameter. // In case of timeout for any board, the parameter will not be synced. if( timeOut ) { parPtr->IncrementSilentTime( deltaT ); cout << "Incremented silent time for bus parameter " << parPtr->GetType() << ":" << parPtr->GetName() << " by " << deltaT << " microseconds " << endl; cout << "Alarm status for "<< parPtr->GetName() << " is " << parPtr->GetStatus() << endl; return false; }else { parPtr->SetSilentTime( 0 ); } return parPtr->Sync( syncFromHW ); } //! Loop over all boards and check for missing boards. The boards //! that are found are synced and removed from the list of the //! missing boards. The entries in the CANbus FIFO for the missing //! boards are removed from the FIFO. vector BaseIUVirtBus::SyncMissingBoards(vector addrVec, BaseIUVirtPar& fakePar, bool syncFromHW) { if (bmDebFlag > 1) { cout << "BaseIUVirtBus::SyncMissingBoards() : Checking for missing boards for parameter " << fakePar.GetType() << " : " << fakePar.GetName() << " for port " << bmDevice->GetPortName() << endl; } vector missVector; //! Loop over boards that responded to the broadcast and synch //! them with the hardware for (unsigned iBoard = 0; iBoard < addrVec.size(); iBoard++) { int address = addrVec[iBoard]; //! If board with this address did not exist create it on the fly if (bvbBoard.count(address) == 0) { //! If board does not exist in the map then something is wrong //! set the communication status to bad and go to the next board cerr << "BaseIUVirtBus::SyncMissingBoards() : Error - The board with address " << address << " does not exist in the board map for port " << bmDevice->GetPortName() << endl; continue; } else { //! Call synchronizing method for the board with the address try { bool boardIsOK = bvbBoard[address]->SyncOnePar(fakePar, syncFromHW); if (!boardIsOK) { // If got here then no response was found the board with that address. // Will add to the list of missing boards. if (bmDebFlag > 1) { cout << "BaseIUVirtBus::SyncMissingBoards() : Board with address " << hex << showbase << address << " on port " << bmDevice->GetPortName() << " has not yet responded" << endl; } missVector.push_back(address); // Add to the vector of missing boards } } //! In case of read or write exceptions continue with communication //! status set to false catch (BaseIUDevice::FailedWriteDevice& err) { cerr << "BaseIUVirtBus::SyncMissingBoards() : Exception caught - "; cerr << err.GetMessage() << endl; MtxLock objLock(bmMutex); bvbCommOK = false; bvbFailureCount++; } catch (BaseIUDevice::FailedReadDevice& err) { cerr << "BaseIUVirtBus::SyncMissingBoards() : Exception caught - "; cerr << err.GetMessage() << endl; MtxLock objLock(bmMutex); bvbCommOK = false; bvbFailureCount++; } } } return missVector; } //! Loop over all boards and check for missing boards. The boards //! that are found are removed from the list of the //! missing boards. The entries for those boards in the FIFO are NOT removed. vector BaseIUVirtBus::FindMissingBoards( vector addrVec, BaseIUVirtPar& fakePar ) { if ( bmDebFlag > 1 ) { cout << "BaseIUVirtBus::FindMissingBoards() : Checking for missing boards for parameter " << fakePar.GetType() << " : " << fakePar.GetName() << endl; } vector missVector; //! Loop over boards that responded to the broadcast and synch //! them with the hardware for ( unsigned iBoard = 0; iBoard < addrVec.size(); iBoard++ ) { int address = addrVec[iBoard]; //! If board with this address did not exist create it on the fly if ( bvbBoard.count( address ) == 0 ) { //! If board does not exist in the map then something is wrong //! set the communication status to bad and go to the next board cerr << "BaseIUVirtBus::FindMissingBoards() : Error - The board with address " << address << " does not exist in the board map for port " << bmDevice->GetPortName() << endl; // bvbCommOK = false; continue; } else { //! Call the method to check for presence of a response for the parameter for the //! board with the address bool boardIsPresent = bvbBoard[address]->FindOnePar( fakePar ); if ( !boardIsPresent ) { // If got here no response was found from the board with that address. // Will add to the list of missing boards. if ( bmDebFlag > 1 ) { cerr << "BaseIUVirtBus::FindMissingBoards() : Board with address " << hex << showbase << address << " on port " << bmDevice->GetPortName() << " has not yet responded" << dec << endl; } missVector.push_back( address ); // Add to the vector of missing boards } } } return missVector; } //! Compare the size of the address vector from the bus and the //! size of the map of the boards. THen check existence of each //! address from the vector in the map. bool BaseIUVirtBus::CheckAddressVector(vector& addrVec) { // MtxLock objLock( MtxLock ); bool comStatus = false; //! Check that the size of the address vector from the //! bus is the same as the number of boards in the map minus 1. //! The minus 1 comes from the fact that we have a fake board with //! address 0 in the map for each bus to broadcast to all boards. if (addrVec.size() == ((bvbBoard.size() - 1))) { //! the numbers of boards in the map and the address vector are //! the same it is already pretty good... but not all comStatus |= true; //! Make sure that we do not have a situation when one board //! disappeared and another one appeared leaving the total //! number of boards the same for (unsigned iBrd = 0; iBrd < addrVec.size(); iBrd++) { if (bvbBoard.count(addrVec[iBrd]) == 0) comStatus &= false; } } return (bvbCommOK = comStatus); } //! Method to return the pointer to the object corresponding to the //! board with address boardAddr on this bus. inline BaseIUVirtBoard* BaseIUVirtBus::GetBoard(int boardAddr) { MtxLock objLock(bmMutex); if (bvbBoard.count(boardAddr) > 0) { // cout << "Board with address " << hex << showbase << boardAddr << " exists " << endl; return bvbBoard[boardAddr]; } return 0; } //! Get the pointer to the parameter with a given type and name //! after checking its existence. inline BaseIUVirtPar* BaseIUVirtBus::GetParameter(string type, string name) { MtxLock objLock(bmMutex); if (bvbPar.count(type) == 0) return 0; if (bvbPar[type].count(name) == 0) return 0; return bvbPar[type][name]; }