void HIVDelayedIntervention::Update(float dt)
    {
        if( !DelayedIntervention::UpdateIndividualsInterventionStatus() )
        {
            return;
        }

        days_remaining -= dt;
        if( days_remaining < 0 )
        {
            expired = true;

            INodeTriggeredInterventionConsumer* broadcaster = nullptr;
            if (s_OK != parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&broadcaster))
            {
                throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent->GetEventContext()->GetNodeEventContext()", "INodeTriggeredInterventionConsumer", "INodeEventContext" );
            }
            if( !broadcast_on_expiration_event.IsUninitialized() )
            {
                broadcaster->TriggerNodeEventObservers( parent->GetEventContext(), broadcast_on_expiration_event );
            }
            LOG_DEBUG_F("broadcast on expiration event\n");

            LOG_DEBUG_F("expiring before delay-triggered intervention\n");
            return;
        }

        remaining_delay_days.Decrement( dt );
    }
    void
    DiagnosticTreatNeg::onPatientDefault()
    {
        LOG_DEBUG_F( "Individual %d got the test but defaulted, receiving Defaulters intervention without waiting for days_to_diagnosis (actually means days_to_intervention) \n", parent->GetSuid().data );

        // Important: Use the instance method to obtain the intervention factory obj instead of static method to cross the DLL boundary
        IGlobalContext *pGC = nullptr;
        const IInterventionFactory* ifobj = nullptr;
        if (s_OK == parent->QueryInterface(GET_IID(IGlobalContext), (void**)&pGC))
        {
            ifobj = pGC->GetInterventionFactory();
        }
        if (!ifobj)
        {
            throw NullPointerException( __FILE__, __LINE__, __FUNCTION__, "parent->GetInterventionFactoryObj()" );
        }


        if( !defaulters_event.IsUninitialized() )
        {
            if( defaulters_event != NO_TRIGGER_STR )
            {
                INodeTriggeredInterventionConsumer* broadcaster = nullptr;

                if (s_OK != parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&broadcaster))

                {

                    throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent->GetEventContext()->GetNodeEventContext()", "INodeTriggeredInterventionConsumer", "INodeEventContext" );
                }
                broadcaster->TriggerNodeEventObserversByString( parent->GetEventContext(), defaulters_event );
            }
        }
        else if( defaulters_config._json.Type() != ElementType::NULL_ELEMENT )
        {
            auto tmp_config = Configuration::CopyFromElement(defaulters_config._json);

            // Distribute the defaulters intervention, right away (do not use the days_to_diagnosis
            IDistributableIntervention *di = const_cast<IInterventionFactory*>(ifobj)->CreateIntervention( tmp_config );

            delete tmp_config;
            tmp_config = nullptr;

            ICampaignCostObserver* pICCO;
            // Now make sure cost of the test-positive intervention is reported back to node
            if (s_OK == parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(ICampaignCostObserver), (void**)&pICCO) )
            {
                di->Distribute( parent->GetInterventionsContext(), pICCO );
                pICCO->notifyCampaignEventOccurred( (IBaseIntervention*)di, (IBaseIntervention*)this, parent );
            }
            else
            {
                throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent->GetEventContext()->GetNodeEventContext()", "ICampaignCostObserver", "INodeEventContext" );
            }
        }
        else
        {
            throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "neither event or config defined" );
        }
    }
    void HIVInterventionsContainer::OnTestForHIV(bool test_result)
    {

        if( test_result && !ever_tested_HIV_positive)
        {
            INodeTriggeredInterventionConsumer* broadcaster = nullptr;
            if (s_OK != parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&broadcaster))
            {
                throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, 
                                               "parent->GetEventContext()->GetNodeEventContext()", 
                                               "INodeTriggeredInterventionConsumer", 
                                               "INodeEventContext" );
            }

            broadcaster->TriggerNodeEventObservers( parent->GetEventContext(), EventTrigger::HIVNewlyDiagnosed );
        }

        ever_tested = true;
        if (test_result)
        {
           ever_tested_HIV_positive = true;
        }
        float t = parent->GetEventContext()->GetNodeEventContext()->GetTime().time;
        time_of_most_recent_test = t;
        time_last_seen_by_healthcare = t;
    }
    bool HIVSimpleDiagnostic::AbortDueToCurrentCascadeState()
    {
        IHIVCascadeOfCare *ihcc = nullptr;
        if ( s_OK != parent->GetInterventionsContext()->QueryInterface(GET_IID(IHIVCascadeOfCare), (void **)&ihcc) )
        {
            throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__,
                                           "parent->GetInterventionsContext()",
                                           "IHIVCascadeOfCare",
                                           "IIndividualHumanInterventionsContext" );
        }

        std::string currentState = ihcc->getCascadeState();

        if ( abortStates.find(currentState) != abortStates.end() )
        {

            // Duplicated from SimpleDiagnostic::positiveTestDistribute
            INodeTriggeredInterventionConsumer* broadcaster = nullptr;
            if (s_OK != parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&broadcaster))
            {
                throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent->GetEventContext()->GetNodeEventContext()", "INodeTriggeredInterventionConsumer", "INodeEventContext" );
            }

            broadcaster->TriggerNodeEventObservers( parent->GetEventContext(), IndividualEventTriggerType::CascadeStateAborted );
            LOG_DEBUG_F("The current cascade state \"%s\" is one of the Abort_States.  Expiring the diagnostic for individual %d.\n", currentState.c_str(), parent->GetSuid().data );
            expired = true;

            return true;
        }
        return false;
    }
    bool
    NodeLevelHealthTriggeredIV::Distribute(
        INodeEventContext *pNodeEventContext,
        IEventCoordinator2 *pEC
    )
    {
        bool was_distributed = BaseNodeIntervention::Distribute(pNodeEventContext, pEC);
        if (was_distributed)
        {
            LOG_DEBUG_F("Distributed Nodelevel health-triggered intervention to NODE: %d\n", pNodeEventContext->GetId().data);

            // QI to register ourself as a NodeLevelHealthTriggeredIV observer
            INodeTriggeredInterventionConsumer * pNTIC = nullptr;
            if (s_OK != pNodeEventContext->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&pNTIC))
            {
                throw QueryInterfaceException(__FILE__, __LINE__, __FUNCTION__, "pNodeEventContext", "INodeTriggeredInterventionConsumer", "INodeEventContext");
            }
            release_assert(pNTIC);
            for (auto &trigger : m_trigger_conditions)
            {
                pNTIC->RegisterNodeEventObserver((IIndividualEventObserver*)this, trigger);
            }
        }
        return was_distributed;
    }
    void CommunityHealthWorkerEventCoordinator::UnregisterForEvents( INodeEventContext* pNEC )
    {
        INodeTriggeredInterventionConsumer* pNTIC = GetNodeTriggeredConsumer( pNEC );

        for( auto& trigger : m_TriggerConditionList )
        {
            pNTIC->UnregisterNodeEventObserver( this, trigger );
        }
    }
Beispiel #7
0
    void DiagnosticTreatNeg::negativeTestDistribute()
    {

        LOG_DEBUG_F( "Individual %d tested 'negative', receiving negative intervention.\n", parent->GetSuid().data );
        // Important: Use the instance method to obtain the intervention factory obj instead of static method to cross the DLL boundary
        IGlobalContext *pGC = nullptr;
        const IInterventionFactory* ifobj = nullptr;
        if (s_OK == parent->QueryInterface(GET_IID(IGlobalContext), (void**)&pGC))
        {
            ifobj = pGC->GetInterventionFactory();
        }
        if (!ifobj)
        {
            throw NullPointerException( __FILE__, __LINE__, __FUNCTION__, "parent->GetInterventionFactoryObj()" );
        }

        if( use_event_or_config == EventOrConfig::Event )
        {
            INodeTriggeredInterventionConsumer* broadcaster = nullptr;

            if (s_OK != parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&broadcaster))

            {

                throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent->GetEventContext()->GetNodeEventContext()", "INodeTriggeredInterventionConsumer", "INodeEventContext" );
            }
            broadcaster->TriggerNodeEventObservers( parent->GetEventContext(), negative_diagnosis_event );
        }
        else if( negative_diagnosis_config._json.Type() != ElementType::NULL_ELEMENT )
        {
            auto tmp_config = Configuration::CopyFromElement( negative_diagnosis_config._json, "campaign" );

            // Distribute the test-negative intervention
            IDistributableIntervention *di = const_cast<IInterventionFactory*>(ifobj)->CreateIntervention( tmp_config );

            delete tmp_config;
            tmp_config = nullptr;

            ICampaignCostObserver* pICCO;
            // Now make sure cost of the test-positive intervention is reported back to node
            if (s_OK == parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(ICampaignCostObserver), (void**)&pICCO) )
            {
                di->Distribute( parent->GetInterventionsContext(), pICCO );
                pICCO->notifyCampaignEventOccurred( (IBaseIntervention*)di, (IBaseIntervention*)this, parent );
            }
            else
            {
                throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent->GetEventContext()->GetNodeEventContext()", "ICampaignCostObserver", "INodeEventContext" );
            }
        }
        else
        {
            throw GeneralConfigurationException( __FILE__, __LINE__, __FUNCTION__, "neither event or config defined" );
        }
        expired = true;
    }
    void PropertyValueChanger::Update( float dt )
    {
        release_assert( expired == false );
        release_assert( ibc );

        std::string current_prop_value = "";
        if( reversion_timer > 0 )
        {
            reversion_timer -= dt;
            if( reversion_timer <= 0 )
            {
                LOG_DEBUG_F( "Time to revert PropertyValueChanger.\n" );
                probability = 1.0;
            }
        }
        if( probability == 1.0 || action_timer < 0 )
        {
            if( revert )
            {
                // Need to ask individual (parent's parent) for current value of this property
                // TBD: use QI not static cast
                auto props = static_cast<InterventionsContainer*>(ibc)->GetParent()->GetEventContext()->GetProperties();
                current_prop_value = props->find( (std::string) target_property_key )->second;
            }
            ibc->ChangeProperty( target_property_key.c_str(), target_property_value.c_str() );

            //broadcast that the individual changed properties
            INodeTriggeredInterventionConsumer* broadcaster = nullptr;
            if (s_OK != parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&broadcaster))
            {
                throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent->GetEventContext()->GetNodeEventContext()", "INodeTriggeredInterventionConsumer", "INodeEventContext" );
            }
            LOG_DEBUG_F( "Individual %d changed property, broadcasting PropertyChange \n", parent->GetSuid().data );
            broadcaster->TriggerNodeEventObservers( parent->GetEventContext(), IndividualEventTriggerType::PropertyChange );


            if( revert )
            {
                target_property_value = current_prop_value;
                probability = 0.0; // keep it simple for now, reversion is guaranteed
                reversion_timer = revert;
                action_timer= FLT_MAX;
                LOG_DEBUG_F( "Initializing reversion timer to %f\n", reversion_timer );
                revert = 0; // no more reversion from reversion
            }
            else
            {
                expired = true;
            }
        }
        action_timer -= dt;
    }
Beispiel #9
0
 void AbstractBednet::BroadcastEvent( const EventTrigger& trigger ) const
 {
     if( !trigger.IsUninitialized() )
     {
         INodeTriggeredInterventionConsumer* broadcaster = nullptr;
         if( s_OK != parent->GetEventContext()->GetNodeEventContext()->QueryInterface( GET_IID( INodeTriggeredInterventionConsumer ), (void**)&broadcaster ) )
         {
             throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__,
                 "parent->GetEventContext()->GetNodeEventContext()",
                 "INodeTriggeredInterventionConsumer",
                 "INodeEventContext" );
         }
         broadcaster->TriggerNodeEventObservers( parent->GetEventContext(), trigger );
     }
 }
 void NodeLevelHealthTriggeredIV::Unregister()
 {
     // unregister ourself as a node level health triggered observer
     INodeTriggeredInterventionConsumer * pNTIC = nullptr;
     if (s_OK != parent->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&pNTIC))
     {
         throw QueryInterfaceException(__FILE__, __LINE__, __FUNCTION__, "parent", "INodeTriggeredInterventionConsumer", "INodeEventContext");
     }
     release_assert(pNTIC);
     for (auto &trigger : m_trigger_conditions)
     {
         pNTIC->UnregisterNodeEventObserver( this, trigger );
     }
     SetExpired( true );
 }
    void HIVInterventionsContainer::BroadcastNewHIVInfection()
    {
        //function called when we externally put in HIV infections through AcquireInfectionHIV
        //first get the pointer to the person, parent is the generic individual
        release_assert(parent);

        IIndividualHumanEventContext * HIVEventContext = NULL;
        if (s_OK != parent->QueryInterface(GET_IID(IIndividualHumanEventContext), (void**)&HIVEventContext))
        {
            throw QueryInterfaceException(__FILE__, __LINE__, __FUNCTION__, "parent->GetEventContext()->GetNodeEventContext()", "INodeTriggeredInterventionConsumer", "INodeEventContext");
        }

        INodeTriggeredInterventionConsumer* broadcaster = nullptr;
        if (s_OK != HIVEventContext->GetNodeEventContext()->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&broadcaster))
        {
            throw QueryInterfaceException(__FILE__, __LINE__, __FUNCTION__, "HIVEventContext-->GetNodeEventContext()", "INodeTriggeredInterventionConsumer", "INodeEventContext");
        }
        broadcaster->TriggerNodeEventObservers(parent->GetEventContext(), EventTrigger::NewExternalHIVInfection);
    }
Beispiel #12
0
    void HIVDelayedIntervention::Callback( float dt )
    {
        if( expired || broadcast_event.IsUninitialized() )
        {
            LOG_DEBUG_F("expired or event is unitialized\n");
            return;
        }

        // Duplicated from SimpleDiagnostic::positiveTestDistribute
        INodeTriggeredInterventionConsumer* broadcaster = nullptr;
        if (s_OK != parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&broadcaster))
        {
            throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__,
                                           "parent->GetEventContext()->GetNodeEventContext()",
                                           "INodeTriggeredInterventionConsumer",
                                           "INodeEventContext" );
        }
        broadcaster->TriggerNodeEventObservers( parent->GetEventContext(), broadcast_event );
        LOG_DEBUG_F("broadcast actual event\n");

        expired = true;
        return;
    }
    void HIVInterventionsContainer::GoOffART()
    {
        release_assert( hiv_parent );
        release_assert( hiv_parent->GetHIVSusceptibility() );
        if( hiv_parent->GetHIVInfection() == nullptr )
        {
            LOG_WARN_F( "Individual %d coming off ART without infection!!!\n", parent->GetSuid().data );
            return;
        }

        release_assert( hiv_parent->GetHIVInfection() );
        if( OnArtQuery() == false )
        {
            LOG_DEBUG_F( "Individual %d is not on ART, cannot dropout.\n", parent->GetSuid().data );
            return;
        }

        LOG_DEBUG_F( "EEL: Individual %d dropping off ART now\n", parent->GetSuid().data );
        ART_status = ARTStatus::OFF_BY_DROPOUT;
        full_suppression_timer = INACTIVE_DURATION;
        days_since_most_recent_ART_start = INACTIVE_DURATION;
        hiv_parent->GetHIVInfection()->ApplySuppressionDropout();

        // Make sure ART dropouts do no return to ON_BUT_FAILING
        m_suppression_failure_timer = INACTIVE_DURATION;

        INodeTriggeredInterventionConsumer* broadcaster = nullptr;
        if (s_OK != parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&broadcaster))
        {
            throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, 
                                           "parent->GetEventContext()->GetNodeEventContext()", 
                                           "INodeTriggeredInterventionConsumer",
                                           "INodeEventContext" );
        }

        broadcaster->TriggerNodeEventObservers( parent->GetEventContext(), EventTrigger::StoppedART );
    }
    void HIVInterventionsContainer::GoOnART( bool viral_suppression, float daysToAchieveSuppression )
    {
        INodeTriggeredInterventionConsumer* broadcaster = nullptr;
        if (s_OK != parent->GetEventContext()->GetNodeEventContext()->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&broadcaster))
        {
            throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__,
                                           "parent->GetEventContext()->GetNodeEventContext()",
                                           "INodeTriggeredInterventionConsumer", 
                                           "INodeEventContext" );
        }

        // NOTE: Should determine where GoOnART was called from by listening to other broadcast messages
        if( OnPreART() )
        {
            // broadcast HIVPreARTToART
            broadcaster->TriggerNodeEventObservers( parent->GetEventContext(), EventTrigger::HIVPreARTToART );
        }
        else
        {
            // broadcast HIVPreARTToART
            broadcaster->TriggerNodeEventObservers( parent->GetEventContext(), EventTrigger::HIVNonPreARTToART );
        }

        release_assert( hiv_parent );
        release_assert( hiv_parent->GetHIVSusceptibility() );
        if( hiv_parent->GetHIVInfection() == nullptr )
        {
            LOG_DEBUG_F( "GoOnART called for *uninfected* individual %d.  Not distributing ART!\n", parent->GetSuid().data );
            return;
        }

        release_assert( hiv_parent->GetHIVInfection() );
        if( OnArtQuery() == true )
        {
            // Don't got on ART if already on ART!
            LOG_DEBUG_F( "Individual %d is already on ART.\n", parent->GetSuid().data );
            return;
        }

        if( OnPreART() ) 
        {
            OnEndPreART();
            LOG_DEBUG_F( "Individual %d appears to be in pre-ART while starting ART, changing preART status indicator to false.\n", parent->GetSuid().data );
        }

        OnBeginART();

        if( viral_suppression ) 
        {
            hiv_parent->GetHIVSusceptibility()->ApplyARTOnset();
            hiv_parent->GetHIVInfection()->SetupSuppressedDiseaseTimers();
            ART_status = ARTStatus::ON_ART_BUT_NOT_VL_SUPPRESSED;
            full_suppression_timer = daysToAchieveSuppression;
            days_to_achieve_suppression = daysToAchieveSuppression;
        }
        else
        {
            ART_status = ARTStatus::ON_BUT_ADHERENCE_POOR;
        }

        days_since_most_recent_ART_start = 0.0f;

        broadcaster->TriggerNodeEventObservers( parent->GetEventContext(), EventTrigger::StartedART );
        LOG_DEBUG_F( "Individual %d is now on ART.\n", parent->GetSuid().data );

        // If not going to achieve viral suppression, stop here so as to 1) avoid computing failure and 2) skip maternal transmission mod
        if( !viral_suppression )
            return;

        float prog = hiv_parent->GetHIVInfection()->GetPrognosis();
        // Simple case (prog>11.9 months):
        // |--early(183d)--x----suppression(yrs)----x--failing(9m)--|
        // Shorter case (prog=11.9m):
        // |--early--x---failing---| ... straight from ramp-up to failing, suppression first to go
        // Even Shorter case (prog>183d && <11.9m):
        // |-early-x---failing---| ... straight from ramp-up to failing, ramp-up gets trimmed
        // Shortest case (prog<=9m):
        // |--failing--| ... straight to failing

        float failing_duration = min( (InfectionHIVConfig::AIDS_duration_in_months*DAYSPERYEAR)/MONTHSPERYEAR, prog );
        NO_LESS_THAN( failing_duration, 0.0f); // not really necessary

        float early_duration        = min( days_to_achieve_suppression, prog - failing_duration );
        float suppression_duration  = max( prog - early_duration - failing_duration, 0.0f );

        // set up a timer for ART failure
        LOG_DEBUG_F( "Individual %d getting suppression_duration (time to ART failure) of %f based on prognosis of %f (early = %f, failing = %f).\n",
                        parent->GetSuid().data,
                        suppression_duration,
                        prog,
                        early_duration,
                        failing_duration
                    );
        m_suppression_failure_timer = suppression_duration + early_duration;

    }
    //returns false if didn't get the intervention
    bool NodeLevelHealthTriggeredIV::notifyOnEvent(
        IIndividualHumanEventContext *pIndiv,
        const EventTrigger& trigger
    )
    {
        // ----------------------------------------------------------------------
        // --- Ignore events for nodes that don't qualify due to their properties
        // ----------------------------------------------------------------------
        if( !node_property_restrictions.Qualifies( parent->GetNodeContext()->GetNodeProperties() ) )
        {
            return false;
        }

        IIndividualHuman *p_human = nullptr;
        if (s_OK != pIndiv->QueryInterface(GET_IID(IIndividualHuman), (void**)&p_human))
        {
            throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "pIndiv", "IIndividualHuman", "IIndividualHumanEventContext" );
        }

        bool missed_intervention = false ;
        if( distribute_on_return_home && (trigger == EventTrigger::Emigrating) )
        {
            if( p_human->AtHome() )
            {
                // ------------------------------------------------------------------------------------------------
                // --- If the individual is leaving his node of residence, then we want to keep track that he left
                // --- so that when he returns we can give him the interventions that he missed.
                // ------------------------------------------------------------------------------------------------
                release_assert( event_occurred_while_resident_away.count( pIndiv->GetSuid() ) == 0 );
                event_occurred_while_resident_away.insert( make_pair( pIndiv->GetSuid(), false ) );
            }
            return false ;
        }
        else if( distribute_on_return_home && (trigger == EventTrigger::Immigrating) )
        {
            if( p_human->AtHome() )
            {
                // ------------------------------------------------------------------------------
                // --- If the individual has returned home and they missed the intervention, then
                // --- we want them to get it, assuming they qualify.
                // ------------------------------------------------------------------------------
                release_assert( event_occurred_while_resident_away.count( pIndiv->GetSuid() ) > 0 );
                missed_intervention = event_occurred_while_resident_away[ pIndiv->GetSuid() ] ;
                event_occurred_while_resident_away.erase( pIndiv->GetSuid() );
                if( missed_intervention )
                {
                    LOG_DEBUG_F( "Resident %d came home and intervention was distributed while away.\n", pIndiv->GetSuid().data );
                }
            }

            if( !missed_intervention )
            {
                return false ;
            }
        }
        else // the trigger event
        {
            // --------------------------------------------------------------------------------------------
            // --- If this is one of the non-migrating events that they intervention is listening for
            // --- and this is not a blackout period, then record that the residents that are away missed
            // --- the intervention.
            // --------------------------------------------------------------------------------------------
            if( blackout_time_remaining <= 0.0f )
            {
                for( auto& rEntry : event_occurred_while_resident_away )
                {
                    rEntry.second = true ;
                }
            }
        }

        if( !blackout_event_trigger.IsUninitialized() && (blackout_period > 0.0) )
        {
            if( (event_occured_list[ trigger.GetIndex() ].count( pIndiv->GetSuid().data ) > 0) || (!missed_intervention && (blackout_time_remaining > 0.0f)) )
            {
                INodeTriggeredInterventionConsumer * pNTIC = NULL;
                if (s_OK != parent->QueryInterface(GET_IID(INodeTriggeredInterventionConsumer), (void**)&pNTIC) )
                {
                    throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent", "INodeTriggeredInterventionConsumer", "INodeEventContext" );
                }
                pNTIC->TriggerNodeEventObservers( pIndiv, blackout_event_trigger );
                return false;
            }
        }

        LOG_DEBUG_F("Individual %d experienced event %s, check to see if they pass the conditions before distributing actual_intervention \n",
                    pIndiv->GetInterventionsContext()->GetParent()->GetSuid().data,
                    trigger.c_str()
                   );

        assert( parent );
        assert( parent->GetRng() );

        bool distributed = false;
        if( _di != nullptr )
        {
            //initialize this flag by individual (not by node)
            m_disqualified_by_coverage_only = false;

            if( qualifiesToGetIntervention( pIndiv ) == false )
            {
                LOG_DEBUG_F("Individual failed to qualify for intervention, m_disqualified_by_coverage_only is %d \n", m_disqualified_by_coverage_only);
                if (m_disqualified_by_coverage_only == true)
                {
                    onDisqualifiedByCoverage( pIndiv );
                }
                return false;
            }

            // Query for campaign cost observer interface from INodeEventContext *parent
            ICampaignCostObserver *iCCO;
            if (s_OK != parent->QueryInterface(GET_IID(ICampaignCostObserver), (void**)&iCCO))
            {
                throw QueryInterfaceException( __FILE__, __LINE__, __FUNCTION__, "parent", "ICampaignCostObserver", "INodeEventContext" );
            }

            // Huge performance win by cloning instead of configuring.
            IDistributableIntervention *di = _di->Clone();
            release_assert( di );
            di->AddRef();

            distributed = di->Distribute( pIndiv->GetInterventionsContext(), iCCO );
            if( distributed )
            {
                std::string classname = GetInterventionClassName();
                LOG_DEBUG_F("A Node level health-triggered intervention (%s) was successfully distributed to individual %d\n",
                            classname.c_str(),
                            pIndiv->GetInterventionsContext()->GetParent()->GetSuid().data
                           );
            }
            else
            {
                LOG_DEBUG_F( "Intervention not distributed?\n" );
            }
            di->Release();
        }
        else
        {
            release_assert( _ndi );

            // Huge performance win by cloning instead of configuring.
            INodeDistributableIntervention *ndi = _ndi->Clone();
            release_assert( ndi );
            ndi->AddRef();

            distributed =  ndi->Distribute( parent, nullptr );

            if( distributed )
            {
                std::string classname = GetInterventionClassName();
                LOG_INFO_F("Distributed '%s' intervention to node %d\n", classname.c_str(), parent->GetExternalId() );
            }
            ndi->Release();
        }

        if( distributed )
        {
            if( blackout_on_first_occurrence )
            {
                blackout_time_remaining = blackout_period ;
            }
            else
            {
                notification_occured = true ;
            }
            event_occured_list[ trigger.GetIndex() ].insert( pIndiv->GetSuid().data ); 
        }

        return distributed;
    }