Repository for import to local machine

Dependencies:   DMBasicGUI DMSupport

ServiceInterval.cpp

Committer:
jmitc91516
Date:
2017-07-31
Revision:
8:26e49e6955bd
Parent:
1:a5258871b33d

File content as of revision 8:26e49e6955bd:

/*
    These classes do what the name implies - handle service intervals. These are measured
    either in instrument cycles (i.e. runs), or time - in the latter case, the interval
    is always twelve months.
    
    Each ServiceInterval object deals with one (and only one) service interval, allowing the caller
    to specify its description (a text string).
    
    Note that the caller must 'poll' the ServiceInterval object to find out whether or not 
    it has expired - it does not raise an interrupt, or call a callback function,
    or do anything unsolicited.
*/

#include "ServiceInterval.h"
#include "SettingsHandler.h"
#include "EasyGUITouchAreaIndices.h"
#include "GCRealTimeClock.h"


// Static members

// Have to manually make sure we have the correct number of NULLs here - we do not want any invalid pointer values
ServiceInterval* ServiceInterval::serviceIntervalArray[SERVICE_INTERVAL_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; 

// Static member functions

/*
    Sets up all our service intervals.
    
    This is effectively a 'static constructor' - 
    but it has to be called manually before the ServiceInterval objects can be used
    *******************************************************************************
*/
void ServiceInterval::SetupAllServiceIntervals(void)
{
    // For the service intervals based on numbers of cycles, set some default values for the cycle counts
    CyclesServiceInterval* cyclesServiceInterval = new CyclesServiceInterval("Column", COMPONENT_1_SERVICED);
    cyclesServiceInterval->SetDurationInNumberOfInstrumentCycles(2); // *** Unrealistic value - testing only ***
    serviceIntervalArray[0] = cyclesServiceInterval;

    cyclesServiceInterval = new CyclesServiceInterval("Liner", COMPONENT_2_SERVICED);
    cyclesServiceInterval->SetDurationInNumberOfInstrumentCycles(3); // *** Unrealistic value - testing only ***
    serviceIntervalArray[1] = cyclesServiceInterval;

    cyclesServiceInterval = new CyclesServiceInterval("Septa", COMPONENT_3_SERVICED);
    cyclesServiceInterval->SetDurationInNumberOfInstrumentCycles(4); // *** Unrealistic value - testing only ***
    serviceIntervalArray[2] = cyclesServiceInterval;
    
    // TODO: fill the rest of the array with TwelveMonthServiceInterval objects,
    // one for each component that actually does require to be replaced every twelve months
    serviceIntervalArray[3] = new TwelveMonthServiceInterval("Detector", COMPONENT_4_SERVICED);
    serviceIntervalArray[4] = new TwelveMonthServiceInterval("O-ring", COMPONENT_5_SERVICED);
    serviceIntervalArray[5] = new TwelveMonthServiceInterval("Amplifier fan", COMPONENT_6_SERVICED);
    
    // If we add more ServiceInterval objects (of either derived class), need to increase SERVICE_INTERVAL_COUNT enum value - see header
}

/*
    Start all the service intervals
*/
void ServiceInterval::StartAllServiceIntervals(void)
{
    for (int index = 0; index < SERVICE_INTERVAL_COUNT; ++index) {
        if(serviceIntervalArray[index] != NULL) {
            serviceIntervalArray[index]->Start();
        }
    }
}


/*
    Returns a pointer to one of the service interval objects, 
    specified by its index, or NULL if there is no such interval
    (e.g. the index is invalid).
    
    Args: the index of the service interval required. The allowed range 
          is from zero to (count of service intervals - 1).
          Call GetServiceIntervalCount to find out how many service intervals there are
          
    Returns a pointer to the specified service interval object,
    or NULL if there is no such interval.

    Caller must check for NULL.
    **************************
*/
ServiceInterval* ServiceInterval::GetServiceInterval(int serviceIntervalIndex)
{
    if((serviceIntervalIndex >= 0) && (serviceIntervalIndex < SERVICE_INTERVAL_COUNT)) {
        return serviceIntervalArray[serviceIntervalIndex];
    }
    
    // 'else' index invalid
    return NULL;
}

/*
    Tell all our service interval objects that the instrument 
    has performed another cycle
*/
void ServiceInterval::TellAllServiceIntervalsInstrumentHasCycled(void)
{
    for (int index = 0; index < SERVICE_INTERVAL_COUNT; ++index) {
        if(serviceIntervalArray[index] != NULL) {
            serviceIntervalArray[index]->InstrumentHasCycled();
        }
    }
}

/*
    Tells the caller if at least one service interval has expired.
    (It is then up to the caller to find out how many have expired, 
    and which ones, by calling the 'GetNextExpiredServiceInterval' function.)
    
    No args.
    
    Returns true if at least one service interval has expired, false if not.    
*/
bool ServiceInterval::AtLeastOneServiceIntervalHasExpired(void)
{
    for (int index = 0; index < SERVICE_INTERVAL_COUNT; ++index) {
        if(serviceIntervalArray[index] != NULL) {
            if(serviceIntervalArray[index]->HasExpired()) {
                return true;
            }
        }
    }
    
    // 'else' - none have expired
    return false;
}

/*
    Given a pointer to a ServiceInterval object that has expired, this function looks for 
    the next ServiceInterval object that has expired, and returns a pointer to it,
    or NULL if there are no more expired service intervals.
    
    If the pointer passed to it - i.e. 'thisExpiredInterval' - is NULL, this function
    starts at the beginning of the array. Note that it does not check 
    that 'thisExpiredInterval' (if not NULL) actually has expired.
    
    The idea is that you first call this function with 'thisExpiredInterval' set to NULL,
    then (if it did not return NULL) you call it again, passing it the ServiceInterval pointer
    it returned, and you keep on doing this until it returns NULL. The ServiceInterval pointers
    that it returned during this sequence, are the ones that have expired.
    
    Args: a pointer to the 'current' expired ServiceInterval - it starts its search
          at the next object after that. If this is NULL, it starts
          at the beginning of the array.
          
    Returns a pointer to the next ServiceInterval that has expired, or NULL if there are no more.
    *** Caller must check for NULL (obviously) ***
    
*/
ServiceInterval* ServiceInterval::GetNextExpiredServiceInterval(ServiceInterval* thisExpiredInterval)
{
    int startIndex = 0;
    
    if(thisExpiredInterval != NULL) {
        for (int index = 0; index < SERVICE_INTERVAL_COUNT; ++index) {
            if(serviceIntervalArray[index] != NULL) {
                if(serviceIntervalArray[index] == thisExpiredInterval) {
                    startIndex = index + 1;
                    break;
                }
            }
        }
    }
    
    for (int index = startIndex; index < SERVICE_INTERVAL_COUNT; ++index) {
        if(serviceIntervalArray[index] != NULL) {
            if(serviceIntervalArray[index]->HasExpired()) {
                return serviceIntervalArray[index];
            }
        }
    }

    return NULL;
}

/*
    Saves the states of all our ServiceInterval objects to QSPI settings,
    so that they can be restored later.
*/
void ServiceInterval::SaveAllServiceIntervalsToQSPISettings(void)
{
    for (int index = 0; index < SERVICE_INTERVAL_COUNT; ++index) {
        if(serviceIntervalArray[index] != NULL) {
            serviceIntervalArray[index]->SaveToQSPISettings();
        }
    }
}

/*
    Restores the states of all our ServiceInterval objects from QSPI settings,
    to their values at the last 'SaveAllServiceIntervalsToQSPISettings' call.
*/
void ServiceInterval::ReadAllServiceIntervalsFromQSPISettings(void)
{
    for (int index = 0; index < SERVICE_INTERVAL_COUNT; ++index) {
        if(serviceIntervalArray[index] != NULL) {
            serviceIntervalArray[index]->ReadFromQSPISettings();
        }
    }
}

/*
    Tells the caller whether or not a particular touch area is a 
    "this component has been serviced" touch area.
    
    Args: the index of the touch area in question
    
    Returns true if the specified touch area corresponds
    to the "this component has been serviced" touch area index
    of any of our ServiceInterval objects.
*/
bool ServiceInterval::IsServicedTouchArea(int touchAreaIndex)
{
    for (int index = 0; index < SERVICE_INTERVAL_COUNT; ++index) {
        if(serviceIntervalArray[index] != NULL) {
            if(serviceIntervalArray[index]->servicedTouchArea == touchAreaIndex) {
                return true;
            }
        }
    }
    
    // 'else' - no matching touch area found
    return false;
}

/*
    If a specified touch area corresponds to one of our 
    "this component has been serviced" touch areas,
    deals with it by restarting that component's service interval
    
    Args: the index of the touch area in question
    
    Returns true if the specified touch area corresponded
    to the "this component has been serviced" touch area index
    of any of our ServiceInterval objects, and we have therefore
    restarted it - false if not, and we have therefore done nothing
*/
bool ServiceInterval::DealWithServicedTouchArea(int touchAreaIndex)
{
    for (int index = 0; index < SERVICE_INTERVAL_COUNT; ++index) {
        if(serviceIntervalArray[index] != NULL) {
            if(serviceIntervalArray[index]->servicedTouchArea == touchAreaIndex) {
                
                serviceIntervalArray[index]->Start();
                
                return true;
            }
        }
    }
    
    // 'else' - no matching touch area found
    return false;
}

// End of static member functions


// ServiceInterval class members
// *****************************

/*
    Create a new ServiceInterval object, with the specified description.
    Note that the service interval does not start its 'countdown' at this point.
    
    Args: the interval description, as a null-terminated string
*/
ServiceInterval::ServiceInterval(char *intervalDescription, int touchAreaIndex)
{
    // Do not use up more memory than we need for the description
    int descriptionLength = strlen(intervalDescription);
    description = new char[descriptionLength + 1];
    strcpy(description, intervalDescription);
    
    intervalHasStarted = false;
    
    servicedTouchArea = touchAreaIndex;
}

/*
    Destroy this service interval cleanly - free up allocated memory
*/
ServiceInterval::~ServiceInterval()
{
    if(description != NULL) {
        delete [] description;
        
        description = NULL;
    }
}


/*
    Tells the caller how long the description is, so that the caller can make sure it has enough memory
    for its own copy of the description.
    
    Note that the length value returned does *not* include the terminating zero byte.
    
    No arguments
    
    Returns the number of characters in the description string, not including the terminating zero byte.
*/
int ServiceInterval::GetDescriptionLength(void)
{
    return strlen(description);
}

/*
    Returns the interval description, as a null-terminated string,
    in a buffer provided by the caller.
    
    Args: a pointer to the buffer which is to contain the description.
          Note that it is up to the caller to make sure that this 
          is long enough to contain the description, plus 
          the null terminator
*/
void ServiceInterval::GetDescription(char *buffer)
{
    strcpy(buffer, description);
}


// CyclesServiceInterval class members
// ***********************************

/*
    CyclesServiceInterval keeps its description in the base class
*/
CyclesServiceInterval::CyclesServiceInterval(char *intervalDescription, int touchAreaIndex) : ServiceInterval(intervalDescription, touchAreaIndex)
{
    durationInNumberOfInstrumentCycles = 0;
    countOfRemainingCycles = -1; // Not yet set
    intervalHasExpired = false;
}

/*
    Sets the service interval duration to the number of instrument cycles (i.e. runs) specified.
    Note that this function does not start the 'countdown' - the 'Start' function does that.
    
    Args: the number of cycles that represents a new service interval
*/
void CyclesServiceInterval::SetDurationInNumberOfInstrumentCycles(int newDurationInNumberOfInstrumentCycles)
{
    durationInNumberOfInstrumentCycles = newDurationInNumberOfInstrumentCycles;
}


/*
    Start the next service interval now.
    
    No arguments, no return code
*/
void CyclesServiceInterval::Start(void)
{
    countOfRemainingCycles = durationInNumberOfInstrumentCycles;
    intervalHasExpired = false;
    
    intervalHasStarted = true;
}


/*
    Caller is telling us that the instrument either is about to perform another cycle,
    or has just completed another cycle - it does not matter which, as long 
    as this function is called once, and only once, per cycle.
    
    This function then works out if this service interval has now expired - 
    caller must call this class' 'HasExpired()' member function (see header file)
    to find out.
    
    No arguments, no return code
*/   
void CyclesServiceInterval::InstrumentHasCycled(void)
{
    if(intervalHasStarted && (!intervalHasExpired)) {
        
        --countOfRemainingCycles;
        
        if(countOfRemainingCycles <= 0) {
            
            // Make sure the cycle count has a legal value
            countOfRemainingCycles = 0;
            
            intervalHasExpired = true;
            
            intervalHasStarted = false;
        }
    }
}

/*
    Save the current state of this service interval to QSPI settings,
    so that we can later read the state from QSPI, and restore it 
    exactly as it was
    
    No arguments, no return code
*/
void CyclesServiceInterval::SaveToQSPISettings(void)
{
    int descriptionLength = GetDescriptionLength();
    char descriptionBuff[descriptionLength + 10];
    GetDescription(descriptionBuff);
    
    char settingNameBuff[descriptionLength + 100];
    
    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".intervalHasStarted");
    SettingsHandler::PutIntegerValueToQSPISettings(settingNameBuff, intervalHasStarted ? 1 : 0);
    
    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".durationInNumberOfInstrumentCycles");
    SettingsHandler::PutIntegerValueToQSPISettings(settingNameBuff, durationInNumberOfInstrumentCycles);

    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".countOfRemainingCycles");
    SettingsHandler::PutIntegerValueToQSPISettings(settingNameBuff, countOfRemainingCycles);
    
    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".intervalHasExpired");
    SettingsHandler::PutIntegerValueToQSPISettings(settingNameBuff, intervalHasExpired ? 1 : 0);
}

/*
    Reads the state of this service interval from QSPI settings.
    
    No arguments, no return code

*/
void CyclesServiceInterval::ReadFromQSPISettings(void)
{
    int descriptionLength = GetDescriptionLength();
    char descriptionBuff[descriptionLength + 10];
    GetDescription(descriptionBuff);
    
    char settingNameBuff[descriptionLength + 100];
    
    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".intervalHasStarted");
    intervalHasStarted = (SettingsHandler::GetIntegerValueFromQSPISettings(settingNameBuff, 0) != 0);
    
    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".durationInNumberOfInstrumentCycles");
    durationInNumberOfInstrumentCycles = SettingsHandler::GetIntegerValueFromQSPISettings(settingNameBuff, 0);

    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".countOfRemainingCycles");
    countOfRemainingCycles = SettingsHandler::GetIntegerValueFromQSPISettings(settingNameBuff, 0);
    
    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".intervalHasExpired");
    intervalHasExpired = (SettingsHandler::GetIntegerValueFromQSPISettings(settingNameBuff, 0) != 0);
}


// TwelveMonthServiceInterval class members
// ****************************************

// Static function

/*
    Gets the current local time as a 'tm' structure. Provided so that we do this 
    the same way everywhere (we may need to apply a correction to the value
    returned by the time() function).
    
    Args: pointer to a 'tm' structure to receive the local time
          *** This must not be NULL ***
          
    No return code.
*/
void TwelveMonthServiceInterval::GetCurrentLocalTime(struct tm *currentLocalTime)
{
    time_t seconds = time(NULL); // Get current time
    *currentLocalTime = *(localtime(&seconds));
}


// Non-static functions

/*
    TwelveMonthServiceInterval keeps its description in the base class
*/
TwelveMonthServiceInterval::TwelveMonthServiceInterval(char *intervalDescription, int touchAreaIndex) : ServiceInterval(intervalDescription, touchAreaIndex)
{
}

/*
    Start the next service interval now.
    
    No arguments, no return code
*/
void TwelveMonthServiceInterval::Start(void)
{
    GetCurrentLocalTime(&startTime);
    
    intervalHasStarted = true;
}
    
/*
    This function does nothing in this class - we are not interested in machine cycles,
    only in elapsed time. Provided so that the caller does not need to know 
    the type of this object before calling this function
*/
void TwelveMonthServiceInterval::InstrumentHasCycled(void)
{
}

/*
    Tell the caller whether or not this service interval has expired - 
    i.e. whether twelve full months (at least) have gone by 
    since the interval started.
*/
bool TwelveMonthServiceInterval::HasExpired(void)
{
    if(intervalHasStarted) {
        struct tm currentTime;
        GetCurrentLocalTime(&currentTime);
        
        int yearsGoneBy = currentTime.tm_year - startTime.tm_year;
        
        if((yearsGoneBy) > 1) {
            // Twelve month service interval has expired
            intervalHasStarted = false;
            return true;
        }
        
        if(yearsGoneBy == 1) {
            int monthsGoneBy = currentTime.tm_mon - startTime.tm_mon + 12;
            
            if(monthsGoneBy > 12) {
                // Twelve month service interval has expired
                intervalHasStarted = false;
                return true;
            }

            if(monthsGoneBy == 12) {
                if(currentTime.tm_mday > startTime.tm_mday) {
                    // Twelve month service interval has expired
                    intervalHasStarted = false;
                    return true;
                }
            }
        }
        
        // If we get here, the twelve month service interval has not expired
    }
    
    // 'else' - either the interval has not expired, or has not even started
    return false;
}

/*
    Save the current state of this service interval to QSPI settings,
    so that we can later read the state from QSPI, and restore it 
    exactly as it was
    
    No arguments, no return code
*/
void TwelveMonthServiceInterval::SaveToQSPISettings(void)
{
    int descriptionLength = GetDescriptionLength();
    char descriptionBuff[descriptionLength + 10];
    GetDescription(descriptionBuff);
    
    char settingNameBuff[descriptionLength + 100];

    // We are only interested in the year, month and day of the month values in the start time - 
    // we do not use the other values

    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".startTime.tm_year");
    SettingsHandler::PutIntegerValueToQSPISettings(settingNameBuff, startTime.tm_year);

    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".startTime.tm_mon");
    SettingsHandler::PutIntegerValueToQSPISettings(settingNameBuff, startTime.tm_mon);
    
    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".startTime.tm_mday");
    SettingsHandler::PutIntegerValueToQSPISettings(settingNameBuff, startTime.tm_mday);
}

/*
    Read the state of this service interval from QSPI settings
    
    No arguments, no return code
*/
void TwelveMonthServiceInterval::ReadFromQSPISettings(void)
{
    int descriptionLength = GetDescriptionLength();
    char descriptionBuff[descriptionLength + 10];
    GetDescription(descriptionBuff);
    
    char settingNameBuff[descriptionLength + 100];

    // We are only interested in the year, month and day of the month values in the start time - 
    // we do not use the other values

    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".startTime.tm_year");
    startTime.tm_year = SettingsHandler::GetIntegerValueFromQSPISettings(settingNameBuff, 0);

    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".startTime.tm_mon");
    startTime.tm_mon = SettingsHandler::GetIntegerValueFromQSPISettings(settingNameBuff, 0);
    
    sprintf(settingNameBuff, "%s%s", descriptionBuff, ".startTime.tm_mday");
    startTime.tm_mday = SettingsHandler::GetIntegerValueFromQSPISettings(settingNameBuff, 0);
}