Пример #1
0
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;
}
Пример #2
0
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
}
Пример #3
0
/**
 * 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();
}
Пример #4
0
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();
	}
}
Пример #5
0
      //-----------------------------------------------------------------------
      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
                        );
      }
Пример #6
0
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());
}
Пример #7
0
/**
 * 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();
}
Пример #8
0
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;
}
Пример #9
0
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(); 
	}
}
Пример #11
0
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());
    }

}
Пример #12
0
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);
}
Пример #13
0
      //-----------------------------------------------------------------------
      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());
      }
Пример #14
0
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
}
Пример #15
0
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);
}
Пример #16
0
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;
  }
}
Пример #17
0
bool CableTrackerSubsystem::isInstanceOf(const Subsystem& s) {
    return Impl::isA(s.getSubsystemGuts());
}
Пример #18
0
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
}
Пример #20
0
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!"));
}
Пример #23
0
ScopeMusicStopper::ScopeMusicStopper(Subsystem& subsystem, Music *music)
    : subsystem(subsystem), music(music)
{
    subsystem.stop_music();
}