bool count_subsystem_particle_counts_for_match (const Wavefunction<amplitude_t>::Amplitude &wf1, const Wavefunction<amplitude_t>::Amplitude &wf2, const Subsystem &subsystem) { assert(subsystem.lattice_makes_sense(wf1.get_lattice())); assert(subsystem.lattice_makes_sense(wf2.get_lattice())); // (we are also assuming that the lattices are in fact equivalent) const PositionArguments &r1 = wf1.get_positions(); const PositionArguments &r2 = wf2.get_positions(); assert(r1.get_N_species() == r2.get_N_species()); assert(r1.get_N_sites() == r2.get_N_sites()); for (unsigned int species = 0; species < r1.get_N_species(); ++species) { assert(r1.get_N_filled(species) == r2.get_N_filled(species)); unsigned int count1 = 0, count2 = 0; for (unsigned int i = 0; i < r1.get_N_filled(species); ++i) { const Particle particle(i, species); if (subsystem.position_is_within(r1[particle], wf1.get_lattice())) ++count1; if (subsystem.position_is_within(r2[particle], wf2.get_lattice())) ++count2; } if (count1 != count2) return false; } return true; }
void Engine::update() { for( size_t i = 0; i < subsystems.size(); i++ ) { Subsystem* system = subsystems[i]; system->update(); } resourceManager->update(); #ifdef ENABLE_SCRIPTING_LUA scriptManager->update(); #endif }
/** * Runs a single iteration of the loop. * * This method should be called often in order to have a functioning * {@link Command} system. The loop has five stages: * * <ol> * <li> Poll the Buttons </li> * <li> Execute/Remove the Commands </li> * <li> Send values to SmartDashboard </li> * <li> Add Commands </li> * <li> Add Defaults </li> * </ol> */ void Scheduler::Run() { // Get button input (going backwards preserves button priority) { if (!m_enabled) return; std::lock_guard<priority_mutex> sync(m_buttonsLock); auto rButtonIter = m_buttons.rbegin(); for (; rButtonIter != m_buttons.rend(); rButtonIter++) { (*rButtonIter)->Execute(); } } m_runningCommandsChanged = false; // Loop through the commands auto commandIter = m_commands.begin(); for (; commandIter != m_commands.end();) { Command* command = *commandIter; // Increment before potentially removing to keep the iterator valid commandIter++; if (!command->Run()) { Remove(command); m_runningCommandsChanged = true; } } // Add the new things { std::lock_guard<priority_mutex> sync(m_additionsLock); auto additionsIter = m_additions.begin(); for (; additionsIter != m_additions.end(); additionsIter++) { ProcessCommandAddition(*additionsIter); } m_additions.clear(); } // Add in the defaults auto subsystemIter = m_subsystems.begin(); for (; subsystemIter != m_subsystems.end(); subsystemIter++) { Subsystem* lock = *subsystemIter; if (lock->GetCurrentCommand() == nullptr) { ProcessCommandAddition(lock->GetDefaultCommand()); } lock->ConfirmCommand(); } UpdateTable(); }
void IdleAnimationTask::Init(idAI* owner, Subsystem& subsystem) { // Just init the base class Task::Init(owner, subsystem); //Memory& memory = owner->GetMemory(); // Read the animation set and interval from the owner's spawnarg _idleAnimationInterval = SEC2MS(owner->spawnArgs.GetInt("idle_animations_interval", "-1")); // Read the general-purpose animations first ParseAnimsToList(owner->spawnArgs.GetString("idle_animations"), _idleAnimations); // Now read the anims for the torso only ParseAnimsToList(owner->spawnArgs.GetString("idle_animations_torso"), _idleAnimationsTorso); // Now read the anims for sitting AI ParseAnimsToList(owner->spawnArgs.GetString("idle_animations_sitting"), _idleAnimationsSitting); if (_idleAnimationInterval > 0 && (_idleAnimations.Num() > 0 || _idleAnimationsTorso.Num() > 0 || _idleAnimationsSitting.Num() > 0)) { _nextAnimationTime = static_cast<int>(gameLocal.time + gameLocal.random.RandomFloat()*_idleAnimationInterval); } else { // No idle animation interval set or no animations, finish this task subsystem.FinishTask(); } }
//----------------------------------------------------------------------- virtual void log( const Subsystem &inSubsystem, Log::Severity inSeverity, Log::Level inLevel, CSTR inMessage, CSTR inFunction, CSTR inFilePath, ULONG inLineNumber ) { IClientLogDelegatePtr delegate; // scope: get the delegate { AutoRecursiveLock lock(mLock); delegate = mDelegate; } if (!delegate) return; delegate->onLog( (PTRNUMBER)(&inSubsystem), inSubsystem.getName(), severityToSeverity(inSeverity), levelToLevel(inLevel), inMessage, inFunction, inFilePath, inLineNumber ); }
Client::Client(Resources& resources, Subsystem& subsystem, hostaddr_t host, hostport_t port, Configuration& config, const std::string& password) throw (Exception) : ClientServer(host, port), Gui(resources, subsystem, resources.get_font("normal")), OptionsMenu(*this, resources, subsystem, config, this), resources(resources), subsystem(subsystem), player_config(config), logged_in(false), me(0), updatecnt(0), factory(resources, subsystem, this), my_id(0), login_sent(false), throw_exception(false), exception_msg(), force_send(false), fhnd(0), running(true), reload_resources(true) { conn = 0; get_now(last); update_text_fade_speed(); /* start data receiver thread */ if (!thread_start()) { throw ClientException("Starting client thread failed."); } /* login */ GPlayerDescription player_desc; memset(&player_desc, 0, sizeof(GPlayerDescription)); strncpy(player_desc.player_name, player_config.get_player_name().c_str(), NameLength - 1); strncpy(player_desc.characterset_name, player_config.get_player_skin().c_str(), NameLength - 1); { Scope<Mutex> lock(mtx); login(password, GPlayerDescriptionLen, &player_desc); } binding.extract_from_config(player_config); /* start music player */ subsystem.start_music_player(resources, *this, config.get_string("external_music").c_str()); }
/** * Removes the {@link Command} from the {@link Scheduler}. * * @param command the command to remove */ void Scheduler::Remove(Command* command) { if (command == nullptr) { wpi_setWPIErrorWithContext(NullParameter, "command"); return; } if (!m_commands.erase(command)) return; Command::SubsystemSet requirements = command->GetRequirements(); auto iter = requirements.begin(); for (; iter != requirements.end(); iter++) { Subsystem* lock = *iter; lock->SetCurrentCommand(nullptr); } command->Removed(); }
void GreetingBarkTask::Init(idAI* owner, Subsystem& subsystem) { // Init the base class SingleBarkTask::Init(owner, subsystem); // Check the prerequisites - are both AI available for greeting? if ( !owner->CanGreet() || !_greetingTarget->CanGreet() || owner->m_isMute ) // grayman #3338 { // Owner or other AI cannot do greetings subsystem.FinishTask(); return; } // Allow state "waiting for greeting" for owner // Allow state "after Greeting" for the other AI if ( ( ( owner->greetingState != ENotGreetingAnybody ) && ( owner->greetingState != EWaitingForGreeting ) ) || ( ( _greetingTarget->greetingState != ENotGreetingAnybody ) && ( _greetingTarget->greetingState != EAfterGreeting ) ) ) { // Someone is busy DM_LOG(LC_AI, LT_INFO)LOGSTRING("Cannot greet: one of the actors is busy: %s to %s\r", owner->name.c_str(), _greetingTarget->name.c_str()); subsystem.FinishTask(); return; } // grayman #3415 - If both actors are sitting, disallow a greeting, on the // assumption that they've been that way for awhile, and any greetings would // already have happened. To keep pent-up greetings from occurring when one // actor stands up, move the allowed greeting time into the future. idAI* otherAI = static_cast<idAI*>(_greetingTarget); if ( (owner->GetMoveType() == MOVETYPE_SIT ) && (otherAI->GetMoveType() == MOVETYPE_SIT ) ) { int delay = (MINIMUM_TIME_BETWEEN_GREETING_SAME_ACTOR + gameLocal.random.RandomInt(EXTRA_DELAY_BETWEEN_GREETING_SAME_ACTOR))*1000; int nextGreetingTime = gameLocal.time + delay; owner->GetMemory().GetGreetingInfo(otherAI).nextGreetingTime = nextGreetingTime; otherAI->GetMemory().GetGreetingInfo(owner).nextGreetingTime = nextGreetingTime; subsystem.FinishTask(); return; } // Both AI are not greeting each other so far, continue owner->greetingState = EGoingToGreet; _greetingTarget->greetingState = EWaitingForGreeting; }
unsigned int BaseSwapPossibleWalk::count_subsystem_sites (const Subsystem &subsystem, const Lattice &lattice) { // (this is a static method) unsigned int N_subsystem_sites = 0; for (unsigned int i = 0; i < lattice.total_sites(); ++i) { if (subsystem.position_is_within(i, lattice)) ++N_subsystem_sites; } return N_subsystem_sites; }
void ResolveMovementBlockTask::Init(idAI* owner, Subsystem& subsystem) { // Just init the base class Task::Init(owner, subsystem); owner->GetMemory().resolvingMovementBlock = true; if (_blockingEnt == NULL) { DM_LOG(LC_AI, LT_WARNING)LOGSTRING("AI %s cannot resolve a NULL blocking entity.\r", owner->name.c_str()); owner->PushMove(); // grayman #2706 - save movement state, because it gets popped in OnFinish() subsystem.FinishTask(); } // Get the direction we're pushing against _initialAngles = owner->viewAxis.ToAngles(); // Set the TTL for this task _endTime = gameLocal.time + 20000; // 20 seconds // Save the movement state owner->PushMove(); if (_blockingEnt->IsType(idAI::Type)) { //DM_LOG(LC_AI, LT_WARNING)LOGSTRING("AI %s starting to resolve blocking AI: %s\r", owner->name.c_str(), _blockingEnt->name.c_str()); InitBlockingAI(owner, subsystem); } else if (_blockingEnt->IsType(idStaticEntity::Type)) { //DM_LOG(LC_AI, LT_WARNING)LOGSTRING("AI %s starting to resolve static blocking entity: %s\r", owner->name.c_str(), _blockingEnt->name.c_str()); InitBlockingStatic(owner, subsystem); } else { // Unknown entity type, exit task //DM_LOG(LC_AI, LT_WARNING)LOGSTRING("AI %s cannot resolve blocking entity: %s\r", owner->name.c_str(), _blockingEnt->name.c_str()); subsystem.FinishTask(); } }
Font::Font(Subsystem& subsystem, const std::string& filename, ZipReader *zip) throw (KeyValueException, FontException) : Properties(filename + ".font", zip), subsystem(subsystem) { try { PNG png(filename + ".png", zip); /* setup font operations */ FontOperations fo; if (zip) { fo.handle = zip; fo.open = &fo_zip_open; fo.read = &fo_zip_read; fo.close = &fo_zip_close; } else { fo.handle = 0; fo.open = &fo_file_open; fo.read = &fo_file_read; fo.close = &fo_file_close; } std::string font_description = filename + ".fds"; fo.open(fo, font_description); char header[4]; fo.read(fo, header, sizeof(header)); if (memcmp(header, "FNT1", 4)) { throw FontException("wrong font file " + font_description); } fo.read(fo, &font_height, sizeof font_height); font_height = ntohl(font_height) * 2; spacing = atoi(get_value("spacing").c_str()); font_char_t font; for (int i = 0; i < NumOfChars; i++) { fo.read(fo, &font, sizeof font); font.origin_x = ntohl(font.origin_x); font.origin_y = ntohl(font.origin_y); font.width = ntohl(font.width); font.height = ntohl(font.height); TileGraphic *tg = subsystem.create_tilegraphic(font.width, font.height); tg->punch_out_tile(png, font.origin_x, font.origin_y, false); tiles[i] = new Tile(tg, false, Tile::TileTypeNonblocking, 0, false, 0.0f); fw[i] = font.width; fh[i] = font.height; } } catch (const Exception& e) { throw FontException(e.what()); } }
void PlayAnimationTask::Init(idAI* owner, Subsystem& subsystem) { // Just init the base class Task::Init(owner, subsystem); // Parse animation spawnargs here if (_animName.IsEmpty()) { gameLocal.Warning("%s cannot start PlayAnimationTask with empty animation name.",owner->name.c_str()); subsystem.FinishTask(); } StartAnim(owner); }
//----------------------------------------------------------------------- virtual void onNewSubsystem(Subsystem &inSubsystem) { IClientLogDelegatePtr delegate; // scope: get the delegate { AutoRecursiveLock lock(mLock); mSubsystems[(PTRNUMBER)(&inSubsystem)] = &inSubsystem; delegate = mDelegate; } if (!delegate) return; delegate->onNewSubsystem((PTRNUMBER)(&inSubsystem), inSubsystem.getName()); }
void InvestigateSpotTask::Init( idAI *owner, Subsystem &subsystem ) { // Just init the base class Task::Init( owner, subsystem ); // Get a shortcut reference Memory &memory = owner->GetMemory(); // Stop previous moves //owner->StopMove(MOVE_STATUS_DONE); if( memory.currentSearchSpot != idVec3( idMath::INFINITY, idMath::INFINITY, idMath::INFINITY ) ) { // Set the goal position SetNewGoal( memory.currentSearchSpot ); } else { // Invalid hiding spot, terminate task DM_LOG( LC_AI, LT_DEBUG )LOGSTRING( "memory.currentSearchSpot not set to something valid, terminating task.\r" ); subsystem.FinishTask(); } //_exitTime = 0; // grayman #3507 }
Music::Music(Subsystem& subsystem, const std::string& filename, ZipReader *zip) throw (KeyValueException, MusicException) : Properties(filename + ".music", zip), subsystem(subsystem), audio(0), do_not_play_in_music_player(false), audio_filename(filename) { try { audio = subsystem.create_audio(); audio->generate_music(filename + ".ogg", zip); } catch (const AudioException& e) { if (audio) { delete audio; throw MusicException(e.what()); } } do_not_play_in_music_player = (atoi(get_value("do_not_play_in_music_player").c_str()) ? true : false); }
void Scheduler::ProcessCommandAddition(Command* command) { if (command == nullptr) return; // Check to make sure no adding during adding if (m_adding) { wpi_setWPIErrorWithContext(IncompatibleState, "Can not start command from cancel method"); return; } // Only add if not already in auto found = m_commands.find(command); if (found == m_commands.end()) { // Check that the requirements can be had Command::SubsystemSet requirements = command->GetRequirements(); Command::SubsystemSet::iterator iter; for (iter = requirements.begin(); iter != requirements.end(); iter++) { Subsystem* lock = *iter; if (lock->GetCurrentCommand() != nullptr && !lock->GetCurrentCommand()->IsInterruptible()) return; } // Give it the requirements m_adding = true; for (iter = requirements.begin(); iter != requirements.end(); iter++) { Subsystem* lock = *iter; if (lock->GetCurrentCommand() != nullptr) { lock->GetCurrentCommand()->Cancel(); Remove(lock->GetCurrentCommand()); } lock->SetCurrentCommand(command); } m_adding = false; m_commands.insert(command); command->StartRunning(); m_runningCommandsChanged = true; } }
bool CableTrackerSubsystem::isInstanceOf(const Subsystem& s) { return Impl::isA(s.getSubsystemGuts()); }
static void* timing_send_task(void* c) { setup_rt_task(30); Subsystem* s = (Subsystem*)c; s->timing_mq_send(); }
void ResolveMovementBlockTask::InitBlockingStatic(idAI* owner, Subsystem& subsystem) { // Get the bounds of the blocking entity and see if there is a way around it idPhysics* blockingPhys = _blockingEnt->GetPhysics(); idBounds blockBounds = blockingPhys->GetAbsBounds(); // grayman #2356 - special case of being stuck inside a func_static, i.e. a rat under a bed that's not monster_clipped idVec3 ownerOrigin = owner->GetPhysics()->GetOrigin(); if (blockBounds.ContainsPoint(ownerOrigin)) { // Try to get out, otherwise you're stuck until your surroundings change. if (owner->movementSubsystem->AttemptToExtricate()) { owner->movementSubsystem->SetBlockedState(ai::MovementSubsystem::EResolvingBlock); // grayman #2706 - stay in EResolvingBlock return; } // oh boy, you're really stuck owner->StopMove(MOVE_STATUS_DONE); owner->AI_BLOCKED = false; owner->AI_DEST_UNREACHABLE = false; subsystem.FinishTask(); return; } blockBounds[0].z = blockBounds[1].z = 0; // Check if there is space to the right of the obstacle idBounds bounds = owner->GetPhysics()->GetBounds(); if (owner->GetAAS() == NULL) { return; } // angua: move the bottom of the bounds up a bit, to avoid finding small objects on the ground that are "in the way" // grayman #2684 - except for AI whose bounding box height is less than maxStepHeight, otherwise applying the bump up // causes the clipmodel to be "upside-down", which isn't good. In that case, give the bottom a bump up equal to half // of the clipmodel's height so it at least gets a small bump. float ht = owner->GetAAS()->GetSettings()->maxStepHeight; if (bounds[0].z + ht < bounds[1].z) { bounds[0].z += ht; } else { bounds[0].z += (bounds[1].z - bounds[0].z)/2.0; } // Set all attachments to nonsolid, temporarily owner->SaveAttachmentContents(); owner->SetAttachmentContents(0); // check if there is a way around idTraceModel trm(bounds); idClipModel clip(trm); idVec3 ownerRight, ownerForward; _initialAngles.ToVectors(&ownerForward, &ownerRight); // Take a point to the right idVec3 testPoint = blockingPhys->GetOrigin(); testPoint.z = ownerOrigin.z; // Move one AAS bounding box size outwards from the model float blockBoundsWidth = blockBounds.GetRadius(blockBounds.GetCenter()); idBounds aasBounds = owner->GetAAS()->GetSettings()->boundingBoxes[0]; aasBounds[0].z = aasBounds[1].z = 0; float aasBoundsWidth = aasBounds.GetRadius(); testPoint += ownerRight * (blockBoundsWidth + aasBoundsWidth); int contents = gameLocal.clip.Contents(testPoint, &clip, mat3_identity, CONTENTS_SOLID, owner); if (cv_ai_debug_blocked.GetBool()) { idVec3 temp = blockingPhys->GetOrigin(); temp.z = ownerOrigin.z; gameRenderWorld->DebugArrow(colorWhite, temp, temp + idVec3(0,0,10), 1, 1000); temp += ownerRight * (blockBoundsWidth + aasBoundsWidth + 5); gameRenderWorld->DebugArrow(colorLtGrey, temp, temp + idVec3(0,0,10), 1, 1000); gameRenderWorld->DebugBounds(contents ? colorRed : colorGreen, bounds, testPoint, 1000); } // grayman #2345 - the point tested might be in the VOID, so let's check for a valid AAS area int areaNum = owner->PointReachableAreaNum(testPoint); if ((contents != 0) || (areaNum == 0)) { // Right side is blocked, look at the left. testPoint = blockingPhys->GetOrigin(); testPoint.z = ownerOrigin.z; testPoint -= ownerRight * (blockBoundsWidth + aasBoundsWidth + 5); contents = gameLocal.clip.Contents(testPoint, &clip, mat3_identity, CONTENTS_SOLID, owner); if (cv_ai_debug_blocked.GetBool()) { gameRenderWorld->DebugBounds(contents ? colorRed : colorGreen, bounds, testPoint, 1000); } areaNum = owner->PointReachableAreaNum(testPoint); // testPoint must not be in the VOID if ((contents != 0) || (areaNum == 0)) { // Neither left nor right has free space // grayman #2345 - before declaring failure, let's try extrication owner->RestoreAttachmentContents(); // AttemptToExtricate() will do an attachment save/restore, so we must restore here first owner->movementSubsystem->AttemptToExtricate(); } else { // Move to left position owner->MoveToPosition(testPoint); owner->RestoreAttachmentContents(); } } else { // Move to right position owner->MoveToPosition(testPoint); owner->RestoreAttachmentContents(); } owner->movementSubsystem->SetBlockedState(ai::MovementSubsystem::EResolvingBlock); // grayman #2706 - stay in EResolvingBlock }
bool Subsystem::Guts::isInSameSystem(const Subsystem& otherSubsystem) const { return isInSystem() && otherSubsystem.isInSystem() && getSystem().isSameSystem(otherSubsystem.getSystem()); }
void InvestigateSpotTask::Init(idAI* owner, Subsystem& subsystem) { // Just init the base class Task::Init(owner, subsystem); // Get a shortcut reference Memory& memory = owner->GetMemory(); // Stop previous moves //owner->StopMove(MOVE_STATUS_DONE); if (memory.currentSearchSpot != idVec3(idMath::INFINITY, idMath::INFINITY, idMath::INFINITY)) { // Set the goal position SetNewGoal(memory.currentSearchSpot); memory.hidingSpotInvestigationInProgress = true; // grayman #3857 memory.stopHidingSpotInvestigation = false; } else { // Invalid hiding spot, terminate task subsystem.FinishTask(); } // grayman #3857 - Only the first searcher in a search is allowed to investigate // the spot closely. Am I it? if (_investigateClosely) { Search* search = gameLocal.m_searchManager->GetSearch(owner->m_searchID); if (search) { if (search->_searcherCount > 0) { Assignment *assignment = &search->_assignments[0]; // first AI assigned to the search if (assignment->_searcher != owner) { _investigateClosely = false; } } } } if (owner->HasSeenEvidence()) { // Draw weapon, if we haven't already if (!owner->GetAttackFlag(COMBAT_MELEE) && !owner->GetAttackFlag(COMBAT_RANGED)) { if ( ( owner->GetNumRangedWeapons() > 0 ) && !owner->spawnArgs.GetBool("unarmed_ranged","0") ) { owner->DrawWeapon(COMBAT_RANGED); } else if ( ( owner->GetNumMeleeWeapons() > 0 ) && !owner->spawnArgs.GetBool("unarmed_melee","0") ) { owner->DrawWeapon(COMBAT_MELEE); } } } //_exitTime = 0; // grayman #3507 }
std::pair<bool /*success*/, std::string /* message */> ivy_engine::startup( const std::string& output_folder_root, const std::string& test_name, const std::string& ivyscript_filename, const std::string& hosts_string, const std::string& select_available_test_LUNs) { std::string api_log_entry; { std::ostringstream o; o << "ivy engine API startup(" << "output_folder_root = " << output_folder_root << ", test_name = " << test_name << ", ivyscript_filename = " << ivyscript_filename << ", test_hosts = " << hosts_string << ", select = " << select_available_test_LUNs << ")" << std::endl; std::cout << o.str(); api_log_entry = o.str(); } outputFolderRoot = output_folder_root; testName = test_name; struct stat struct_stat; if( stat(outputFolderRoot.c_str(),&struct_stat)) { std::ostringstream o; o << "<Error> directory \"" << outputFolderRoot << "\" does not exist." << std::endl; std::cout << o.str(); return std::make_pair(false,o.str()); } if(!S_ISDIR(struct_stat.st_mode)) { std::ostringstream o; o << "<Error> \"" << outputFolderRoot << "\" is not a directory." << std::endl; std::cout << o.str(); return std::make_pair(false,o.str()); } testFolder=outputFolderRoot + std::string("/") + testName; if(0==stat(testFolder.c_str(),&struct_stat)) // output folder already exists { if(!S_ISDIR(struct_stat.st_mode)) { std::ostringstream o; o << "<Error> Output folder for this test run \"" << testFolder << "\" already exists but is not a directory." << std::endl; std::cout << o.str(); return std::make_pair(false,o.str()); } // output folder already exists and is a directory, so we delete it to make a fresh one. if (0 == system((std::string("rm -rf ")+testFolder).c_str())) // ugly but easy. { std::ostringstream o; o << " Deleted pre-existing folder \"" << testFolder << "\"." << std::endl; std::cout << o.str(); } else { std::ostringstream o; o << "<Error> Failed trying to delete previously existing version of test run output folder \"" << testFolder << "\"." << std::endl; std::cout << o.str(); return std::make_pair(false,o.str()); } } if (mkdir(testFolder.c_str(),S_IRWXU | S_IRWXG | S_IRWXO)) { std::ostringstream o; o << "<Error> Failed trying to create output folder \"" << testFolder << "\" << errno = " << errno << " " << strerror(errno) << std::endl; std::cout << o.str(); return std::make_pair(false,o.str()); } std::cout << " Created test run output folder \"" << testFolder << "\"." << std::endl; if (mkdir((testFolder+std::string("/logs")).c_str(),S_IRWXU | S_IRWXG | S_IRWXO)) { std::ostringstream o; o << "<Error> - Failed trying to create logs subfolder in output folder \"" << testFolder << "\" << errno = " << errno << " " << strerror(errno) << std::endl; std::cout << o.str(); return std::make_pair(false,o.str()); } masterlogfile = testFolder + std::string("/logs/log.ivymaster.txt"); ivy_engine_logfile = testFolder + std::string("/logs/ivy_engine_API_calls.txt"); log(masterlogfile, api_log_entry); log(ivy_engine_logfile, api_log_entry); { std::ostringstream o; o << "ivy version " << ivy_version << " build date " << IVYBUILDDATE << " starting." << std::endl << std::endl; log(masterlogfile,o.str()); } if (!routine_logging) { std::ostringstream o; o << "For logging of routine (non-error) events, and to record the conversation with each ivydriver/ivy_cmddev, use the ivy -log command line option, like \"ivy -log a.ivyscript\".\n\n"; log(masterlogfile,o.str()); } if (ivyscript_filename.size() > 0) { std::string copyivyscriptcmd = std::string("cp -p ") + ivyscript_filename + std::string(" ") + testFolder + std::string("/") + testName + std::string(".ivyscript"); if (0!=system(copyivyscriptcmd.c_str())) // now getting lazy, but purist maintainers could write C++ code to do this. { std::ostringstream o; o << "<Error> Failed trying to copy input ivyscript to output folder: \"" << copyivyscriptcmd << "\"." << std::endl; log(masterlogfile,o.str()); std::cout << o.str(); return std::make_pair(false,o.str()); } } std::pair<bool,std::string> retval = rollups.initialize(); if (!retval.first) { std::ostringstream o; o << "<Error> Internal programming error - failed initializing rollups in ivymaster.cpp saying: " << retval.second << std::endl; log(masterlogfile,o.str()); std::cout << o.str(); return std::make_pair(false,o.str()); } test_start_time.setToNow(); initialize_io_time_clip_levels(); availableControllers[toLower(std::string("measure"))] = &the_dfc; auto rv = random_steady_template.setParameter("iosequencer=random_steady"); if ( ! rv.first) { std::ostringstream o; o << "<Error> dreaded internal programming error - ivymaster startup - failed trying to set the default random steady I/O generator template - " << rv.second << std::endl; std::cout << o.str(); return std::make_pair(false,o.str()); } rv = random_independent_template.setParameter("iosequencer=random_independent"); if ( !rv.first ) { std::ostringstream o; o << "<Error> dreaded internal programming error - ivymaster startup - failed trying to set the default random independent I/O generator template - " << rv.second << std::endl; std::cout << o.str(); return std::make_pair(false,o.str()); } rv = sequential_template.setParameter("iosequencer=sequential"); if ( !rv.first ) { std::ostringstream o; o << "<Error> dreaded internal programming error - ivymaster startup - failed trying to set the default sequential I/O generator template - " << rv.second << std::endl; std::cout << o.str(); return std::make_pair(false,o.str()); } hosts_list test_hosts; if (!test_hosts.compile(hosts_string)) { std::ostringstream o; o << "<Error> failed parsing list of test hosts \"" << hosts_string << "\" - " << test_hosts.message; return std::make_pair(false,o.str()); } for (auto& s: test_hosts.hosts) { hosts.push_back(s);} write_clear_script(); // This writes a shell script to run the clear_hung_ivy_threads executable on all involved hosts - handy if you are a developer and you mess things up if (hosts.size() == 0) { std::ostringstream o; o << "<Error> ivy engine startup failed - No hosts specified." << std::endl; return std::make_pair(false,o.str()); } JSON_select select; if (!select.compile_JSON_select(select_available_test_LUNs)) { std::ostringstream o; o << "<Error> ivy engine startup failure - invalid select expression for available test LUNs \"" << select_available_test_LUNs << "\" - error message follows: " << select.error_message << std::endl; log(masterlogfile,o.str()); std::cout << o.str(); kill_subthreads_and_exit(); } { std::ostringstream o; o << "select " << select << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); } if ( (!select.contains_attribute("serial_number")) && (!select.contains_attribute("vendor")) ) { std::ostringstream o; o << "<Error> ivy engine startup failure - select for available test LUNs \"" << select_available_test_LUNs << "\" - must specify either \"serial_number\" or \"vendor\"." << std::endl; return std::make_pair(false,o.str()); } for ( auto& host : hosts ) { if (routine_logging) { std::cout << "Starting thread for " << host << std::endl; } log( masterlogfile, std::string("Starting thread for ") + host + std::string("\n") ); pipe_driver_subthread* p_pipe_driver_subthread = new pipe_driver_subthread ( host, outputFolderRoot, testName, testFolder+std::string("/logs") ); host_subthread_pointers[host] = p_pipe_driver_subthread; threads.push_back(std::thread(invokeThread,p_pipe_driver_subthread)); } ofstream ahl(testFolder+std::string("/all_discovered_LUNs.txt")); ofstream ahl_csv(testFolder+std::string("/all_discovered_LUNs.csv")); { std::ostringstream o; o << std::endl << "Note:" << std::endl << "Sometimes the ssh command to start up ivydriver on a test host can take a long time when waiting for DNS timeouts. " << "This can be speeded up by editing /etc/nsswitch.conf / resolv.conf to use /etc/hosts first, or options for the sshd daemon can be edited; search for \"ssh login timeout\"." << std::endl << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); } bool first_host {true}; for (auto& pear : host_subthread_pointers) { { std::unique_lock<std::mutex> u_lk(pear.second->master_slave_lk); std::chrono::system_clock::time_point leftnow {std::chrono::system_clock::now()}; if ( pear.second->master_slave_cv.wait_until ( u_lk, leftnow + std::chrono::seconds(ivy_ssh_timeout), [&pear]() { return pear.second->startupComplete || pear.second->dead; } ) ) { if (pear.second->startupSuccessful) { std::ostringstream o; o << "pipe_driver_subthread successful fireup for host " << pear.first << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); } else { std::ostringstream o; o << "Aborting - unsuccessful startup for ivydriver on host " << pear.first << ". Reason: \"" << pear.second->commandErrorMessage << "\"." << std::endl; o << std::endl << "Usually if try to run ivy again, it will work the next time. Don\'t know why this happens." << std::endl; o << std::endl << "Run the \"clear_hung_ivy_threads.sh\" script to clear any hung ivy / ivydriver / ivy_cmddev threads on the ivy master host and all test hosts." << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); u_lk.unlock(); kill_subthreads_and_exit(); // doesn't return } } else { std::ostringstream o; o << "Aborting - timeout waiting " << timeout_seconds << " seconds for pipe_driver_subthread fireup for host " << pear.first << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); u_lk.unlock(); kill_subthreads_and_exit(); // doesn't return } } {ostringstream o; o << "Subthread for \"" << pear.first << "\" posted startupComplete." << std::endl; std::cout << o.str(); log(masterlogfile,o.str());} for (auto& pLUN : pear.second->thisHostLUNs.LUNpointers) { allDiscoveredLUNs.LUNpointers.push_back(pLUN); ahl << pLUN->toString() << std::endl; } //*debug*/{ostringstream o; o << "Loop for \"" << pear.first << "\" about to copy on sample LUN." << std::endl; std::cout << o.str(); log(masterlogfile,o.str());} //*debug*/{ostringstream o; o << "Loop for \"" << pear.first << "\" HostSampleLUN=" << pear.second->HostSampleLUN.toString() << std::endl; std::cout << o.str(); log(masterlogfile,o.str());} TheSampleLUN.copyOntoMe(&pear.second->HostSampleLUN); if (first_host) {ahl_csv << pear.second->lun_csv_header;} ahl_csv << pear.second->lun_csv_body.str(); first_host = false; } ahl.close(); ahl_csv.close(); allThreadsSentTheLUNsTheySee=true; /*debug*/ if (routine_logging){ ostringstream o; o <<"allDiscoveredLUNs contains:"<<std::endl<< allDiscoveredLUNs.toString() <<std::endl; //std::cout<<o.str(); log(masterlogfile,o.str()); } else { ostringstream o; o <<"Discovered " << allDiscoveredLUNs.LUNpointers.size() << " LUNs on the test hosts." << std::endl; //std::cout<<o.str(); log(masterlogfile,o.str()); } if (0 == allDiscoveredLUNs.LUNpointers.size()) { std::ostringstream o; o << ivyscript_line_number << ": \"" << ivyscript_line << "\"" << std::endl; o << "No LUNs were found on any of the hosts." << std::endl; std::cout << o.str(); log (masterlogfile,o.str()); ivyscriptIfstream.close(); kill_subthreads_and_exit(); } allDiscoveredLUNs.split_out_command_devices_into(commandDeviceLUNs); /*debug*/ { ostringstream o; o <<"commandDeviceLUNs contains:"<<std::endl<< commandDeviceLUNs.toString() <<std::endl; //std::cout<<o.str(); log(masterlogfile,o.str()); } for (LUN* pLUN : allDiscoveredLUNs.LUNpointers) { if (select.matches(pLUN)) { if ( pLUN->attribute_value_matches("ldev", "FF:FF" ) && pLUN->attribute_value_matches("ldev_type", "" ) && ( pLUN->attribute_value_matches("product", "DISK-SUBSYSTEM") || pLUN->attribute_value_matches("product","OPEN-V") ) ) { ; // Ignore "phantom LUNs" that have been deleted on the subsystem but for which a /dev/sdxxx entry still exists. } else if ( pLUN->attribute_value_matches("ldev", "FF:FF") && pLUN->attribute_value_matches("ldev_type", "phantom LUN") ) { ; // Ignore "phantom LUNs" that have been deleted on the subsystem but for which a /dev/sdxxx entry still exists. } else { availableTestLUNs.LUNpointers.push_back(pLUN); } } } if ( 0 == availableTestLUNs.LUNpointers.size() ) { std::ostringstream o; o << "No LUNs matched [hosts] statement [select] clause." << std::endl; //p_Select->display("",o); std::cout << o.str(); log(masterlogfile,o.str()); kill_subthreads_and_exit(); } for (auto& pLUN : availableTestLUNs.LUNpointers) pLUN -> createNicknames(); // Now we create the Subsystem objects for (auto& pLUN : availableTestLUNs.LUNpointers) { std::string serial_number = pLUN->attribute_value(std::string("serial_number")); trim(serial_number); Subsystem* pSubsystem; auto subsystemIt = subsystems.find(serial_number); if (subsystems.end() == subsystemIt) { std::string product = pLUN->attribute_value("product"); trim(product); if (0 == product.compare(std::string("OPEN-V"))) { pSubsystem = new Hitachi_RAID_subsystem(serial_number, pLUN); } else { pSubsystem = new Subsystem(serial_number); } subsystems[serial_number] = pSubsystem; } else { pSubsystem = (*subsystemIt).second; } pSubsystem->add(pLUN); } // For each subsystem that the available test LUNs map to, // for the first available real-time interface, // start up the appropriate subthread to drive that interface. // Which means - for each Hitachi RAID subsystem, use the first command device // in commandDeviceLUNs that leads to that subsystem and start an // pipe_driver_subthread, which fires up the ivy_cmddev executable remotely via ssh // communicating via stdin/stdout pipes the same way we communicate with remote ivydriver instances. for (auto& pear : subsystems) { Subsystem* pSubsystem = pear.second; if (0 == std::string("Hitachi RAID").compare(pSubsystem->type())) { Hitachi_RAID_subsystem* pRAIDsubsystem {(Hitachi_RAID_subsystem*) pSubsystem}; bool have_cmd_dev_this_subsystem = false; if (m_s.use_command_device) for (auto& pL : commandDeviceLUNs.LUNpointers) // for each command device LUN as candidate to match as first command device for this subsystem. { if (0 == pSubsystem->serial_number.compare(pL->attribute_value("serial_number"))) // we have found the first command device for this subsystem { // fire up command device thread std::string cmd_dev_description; { std::ostringstream o; o << "command device for " << pL->attribute_value("Hitachi_product") << " " << pL->attribute_value("HDS_product") << " serial number " << pSubsystem->serial_number << " on host = " << pL->attribute_value("ivyscript_hostname") << ", subsystem port = " << pL->attribute_value("port") << ", LDEV = " << pL->attribute_value("LDEV") << std::endl; cmd_dev_description = o.str(); } std::string cmddevLDEV = pL->attribute_value("LDEV"); if (cmddevLDEV.length() >= 5 && ':' == cmddevLDEV[2]) cmddevLDEV.erase(2,1); std::ostringstream o; o << "Connecting to " << cmd_dev_description << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); pipe_driver_subthread* p_pipe_driver_subthread = new pipe_driver_subthread( pL->attribute_value("ivyscript_hostname") , outputFolderRoot , testName, testFolder+std::string("/logs") ); p_pipe_driver_subthread->pCmdDevLUN = pL; // Seeing this set tells the subthread it's running a command device. p_pipe_driver_subthread->p_Hitachi_RAID_subsystem = pRAIDsubsystem; pRAIDsubsystem->command_device_description = cmd_dev_description; pRAIDsubsystem->pRMLIBthread=p_pipe_driver_subthread; command_device_subthread_pointers[pSubsystem->serial_number] = p_pipe_driver_subthread; ivymaster_RMLIB_threads.push_back(std::thread(invokeThread,p_pipe_driver_subthread)); // Note: I wasn't sure, since std::thread is not copyable but is moveable, how to handle // the case when the command device doesn't start up correctly. So in the interest of // time, I left any unsuccessful-startup command device threads in moribund state // in ivymaster_RMLIB_threads to be joined when ivy shuts down. // wait with timeout for thread status to show "startupComplete", i.e. waiting for command; { std::unique_lock<std::mutex> u_lk(p_pipe_driver_subthread->master_slave_lk); std::chrono::system_clock::time_point leftnow {std::chrono::system_clock::now()}; if ( p_pipe_driver_subthread->master_slave_cv.wait_until ( u_lk, leftnow + std::chrono::seconds(ivy_ssh_timeout), [&p_pipe_driver_subthread](){return p_pipe_driver_subthread->startupComplete || p_pipe_driver_subthread->dead;} ) ) { if (p_pipe_driver_subthread->startupSuccessful) { std::ostringstream o; o << "ivy_cmddev pipe_driver_subthread successful fireup for subsystem " << pSubsystem->serial_number << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); haveCmdDev = true; have_cmd_dev_this_subsystem = true; } else { std::ostringstream o; o << "Unsuccessful startup for command device = " << cmd_dev_description << std::endl << "Reason - " << p_pipe_driver_subthread->commandErrorMessage << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); pRAIDsubsystem->command_device_description.clear(); pRAIDsubsystem->pRMLIBthread=nullptr; } } else { std::ostringstream o; o << "Timeout waiting " << timeout_seconds << " seconds for for startup of command device = " << cmd_dev_description << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); pRAIDsubsystem->command_device_description.clear(); pRAIDsubsystem->pRMLIBthread=nullptr; } } if (!have_cmd_dev_this_subsystem) continue; // This command device did not start up OK - try the next one if we have another to the same subsystem. // Maybe there is a command device perhaps on a different host that does have RMLIB installed, // does have a valid license key installed, and does have the ivy_cmddev executable. // Command device reports startup complete // gather config ivytime config_gather_start; config_gather_start.setToNow(); std::chrono::system_clock::time_point start_getconfig_time_point { std::chrono::system_clock::now() }; { std::unique_lock<std::mutex> u_lk(p_pipe_driver_subthread->master_slave_lk); p_pipe_driver_subthread->commandString = std::string("get config"); p_pipe_driver_subthread->command=true; p_pipe_driver_subthread->commandComplete=false; p_pipe_driver_subthread->commandSuccess=false; p_pipe_driver_subthread->commandErrorMessage.clear(); { std::ostringstream o; o << "Posted \"get config\" to thread for subsystem serial " << pRAIDsubsystem->serial_number << " managed on host " << p_pipe_driver_subthread->ivyscript_hostname << '.' << std::endl; log(masterlogfile,o.str()); } } p_pipe_driver_subthread->master_slave_cv.notify_all(); { std::unique_lock<std::mutex> u_lk(p_pipe_driver_subthread->master_slave_lk); if ( p_pipe_driver_subthread->master_slave_cv.wait_until ( u_lk, start_getconfig_time_point + std::chrono::seconds(get_config_timeout_seconds /* see ivydefines.h */), [&p_pipe_driver_subthread](){ return p_pipe_driver_subthread->commandComplete || p_pipe_driver_subthread->dead; } ) ) { if (!p_pipe_driver_subthread->commandSuccess) { std::ostringstream o; o << "\"get config\" to thread for subsystem serial " << pRAIDsubsystem->serial_number << " managed on host " << p_pipe_driver_subthread->ivyscript_hostname << " failed. Aborting." << std::endl; log(masterlogfile,o.str()); kill_subthreads_and_exit(); } ivytime config_gather_complete; config_gather_complete.setToNow(); pRAIDsubsystem->config_gather_time = config_gather_complete - config_gather_start; { std::ostringstream o; o << "\"get config\" reported complete with duration " << pRAIDsubsystem->config_gather_time.format_as_duration_HMMSSns() << " by thread for subsystem serial " << pRAIDsubsystem->serial_number << " managed on host " << p_pipe_driver_subthread->ivyscript_hostname << '.' << std::endl; log(masterlogfile,o.str()); } } else // timeout { std::ostringstream o; o << "Aborting - timeout waiting " << get_config_timeout_seconds << " seconds for pipe_driver_subthread for subsystem " << pRAIDsubsystem->serial_number << " managed on host " << p_pipe_driver_subthread->ivyscript_hostname << " to complete a \"get config\"." << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); u_lk.unlock(); kill_subthreads_and_exit(); } } // post process after gather to augment available test LUN attributes with LDEV attributes from the config gather for (auto pLUN : availableTestLUNs.LUNpointers) { if ( 0 == std::string("Hitachi RAID").compare(pSubsystem->type()) && 0 == pSubsystem->serial_number.compare(pLUN->attribute_value("serial_number"))) { // for each test LUN, if we are processing a Hitachi RAID subsystem with a given serial number and the test LUN serial_number matches the subsystem // 1) if CLPR is non-empty add the <CLPR,serial_number> pair to cooldown_WP_watch_set // 2) copy the LDEV's RMLIB attributes to the test LUN. // put a suffix "_RMLIB" if the test LUN already has the attribute name (from the SCSI Inquiry LUN lister tool" showluns.sh"). if (0 < pLUN->attribute_value("CLPR").length()) { cooldown_WP_watch_set.insert(std::make_pair(pLUN->attribute_value("CLPR"),pSubsystem)); } // Copy the LDEV attributes obtained from a command device to the corresponding available test LUN, // appending "_RMLIB" upon attribute name collisions. // NOTE - in the LUN object all map keys are first translated to lower case upon insertion. // It is advised to use the LUN's own functions to do lookups, attribute value matching, etc. // as then you won't need to worry about case sensitivity of attribute names. // NOTE2 - The data returned by ivy_cmddev for a "get config" command has attribute names like LDEV // in upper case and the LDEV attribute values like 00:FF are provided in upper case. if (!pLUN->contains_attribute_name(std::string("ldev"))) { std::ostringstream o; o << "Aborting - available test LUN matched on Hitachi RAID subsystem serial number " << pSubsystem->serial_number << ", but the LUN did have the \"ldev\" attribute." << std::endl << " LUN =" << pLUN->toString() << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); kill_subthreads_and_exit(); } std::string LUN_ldev = pLUN->attribute_value(std::string("ldev")); if (0==LUN_ldev.size()) { std::ostringstream o; o << "Aborting - available test LUN matched on Hitachi RAID subsystem serial number " << pSubsystem->serial_number << ", but the LUN's \"ldev\" attribute value was the null string." << std::endl << " LUN =" << pLUN->toString() << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); kill_subthreads_and_exit(); } auto subsystemLDEVit = pRAIDsubsystem->configGatherData.data.find(std::string("LDEV")); if (pRAIDsubsystem->configGatherData.data.end() == subsystemLDEVit) { std::ostringstream o; o << "Aborting - available test LUN matched on Hitachi RAID subsystem serial number " << pSubsystem->serial_number << ", but the subsystem configuration data from a command device didn\'t have \"LDEV\" element type." << std::endl << " LUN =" << pLUN->toString() << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); kill_subthreads_and_exit(); } std::string cmddev_LDEV = toUpper(LUN_ldev); auto subsystemLDEVinstanceIt = (*subsystemLDEVit).second.find(cmddev_LDEV); if ((*subsystemLDEVit).second.end() == subsystemLDEVinstanceIt) { std::ostringstream o; o << "Aborting - available test LUN matched on Hitachi RAID subsystem serial number " << pSubsystem->serial_number << ", but the subsystem didn\'t have the \"LDEV\" instance \"" << cmddev_LDEV << "\"." << std::endl << " LUN =" << pLUN->toString() << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); kill_subthreads_and_exit(); } for (auto& pear : (*subsystemLDEVinstanceIt).second) { // To reduce clutter, upon an attribute name collision, meaning that both the SCSI Inquiry-based LUN Lister tool like showluns.sh // and the RMLIB API-based command device data returned an attribute name that when translated to lower case was the same, // then if the attribute values from either side are the same when translated to lower case and with leading/trailing // blanks removed, we don't alter the existing attribute value, and the RMLIB API value is discarded. // If RMLIB returned a substantially different value for the same metric, then we create a new attribute name // by appending "_rmlib" and store the RMLIB value there. // pear is std::map<std::string,metric_value> std::string attribute_name = toLower(pear.first); std::string attribute_value = pear.second.string_value(); std::string trimmed_attribute_value = toLower(attribute_value); trim(trimmed_attribute_value); if (pLUN->contains_attribute_name(attribute_name)) { std::string trimmed_LUN_value = toLower(pLUN->attribute_value(attribute_name)); trim(trimmed_LUN_value); if (0 != trimmed_attribute_value.compare(trimmed_LUN_value)) { attribute_name += std::string("_rmlib"); pLUN->set_attribute(attribute_name, attribute_value); } } else { pLUN->set_attribute(attribute_name, attribute_value); } } } } // end of augmenting available test LUN attributes with config info from the command device break; // out of loop over command device LUNs as candidates to be first command device for this subsystem } // if we found the first command device for this subsystem } // for each command device LUN as candidate to match as first command device for this subsystem. // back to Hitachi RAID subsystem type if (!have_cmd_dev_this_subsystem) { std::ostringstream o; o << "No command device found for subsystem " << pSubsystem->serial_number << std::endl; std::cout << o.str(); log(masterlogfile,o.str()); } else { pRAIDsubsystem->configGatherData.print_csv_file_set ( testFolder, pRAIDsubsystem->serial_number+std::string(".config") ); } } // end for Hitachi RAID subsystem } // end for each subsystem for (auto& pLUN : availableTestLUNs.LUNpointers) { TheSampleLUN.copyOntoMe(pLUN); // add nicknames, and command device LDEV attributes to TheSampleLUN. } for (auto& pLUN : availableTestLUNs.LUNpointers) // populate test config thumbnail data structures { available_LUNs_thumbnail.add(pLUN); } { std::ostringstream o; o << available_LUNs_thumbnail; bool saw_command_device {false}; for (auto& pear : subsystems) { if (0 == std::string("Hitachi RAID").compare(pear.second->type())) { Hitachi_RAID_subsystem* pRAID = (Hitachi_RAID_subsystem*) pear.second; if (pRAID->command_device_description.size() > 0) { saw_command_device = true; o << pRAID->command_device_description; } } } if (saw_command_device) o << std::endl; log(masterlogfile,o.str()); std::cout << o.str(); ofstream ofs(testFolder+std::string("/available_test_config.txt")); ofs << o.str(); ofs.close(); } if (routine_logging) { ostringstream o; o << "After adding subsystem config attributes, availableTestLUNs contains:" << std::endl << availableTestLUNs.toString() << std::endl; // std::cout << o.str(); log(masterlogfile,o.str()); } else { ostringstream o; o <<"availableTestLUNs contains " << availableTestLUNs.LUNpointers.size() << " LUNs." << std::endl; //std::cout<<o.str(); log(masterlogfile,o.str()); } ofstream atlcsv(testFolder+std::string("/available_test_LUNs.csv")); availableTestLUNs.print_csv_file(atlcsv); atlcsv.close(); if (cooldown_WP_watch_set.size() == 0) { std::ostringstream o; o << "No command devices for RAID_subsystem LDEVs, so cooldown_by_wp settings will not have any effect." << std::endl; log(masterlogfile,o.str()); std::cout << o.str(); } else { std::ostringstream o; o << "Cooldown_WP_watch_set contains:"; for (auto& pear : cooldown_WP_watch_set) o << " < " << pear.first << ", " << pear.second->serial_number << " >"; o << std::endl << std::endl; log(masterlogfile,o.str()); std::cout << o.str(); } haveHosts = true; return std::make_pair(true,std::string("hello, whirled!")); }
ScopeMusicStopper::ScopeMusicStopper(Subsystem& subsystem, Music *music) : subsystem(subsystem), music(music) { subsystem.stop_music(); }