diff --git a/bacnet-stack/demo/object/lc.c b/bacnet-stack/demo/object/lc.c index b660f4e0..d6cdf0f6 100644 --- a/bacnet-stack/demo/object/lc.c +++ b/bacnet-stack/demo/object/lc.c @@ -35,26 +35,26 @@ #include "datetime.h" #include "bacenum.h" #include "config.h" /* the custom stuff */ -#include "lc.h" +#include "lc.h" #include "ao.h" #include "wp.h" - + /* number of demo objects */ #define MAX_LOAD_CONTROLS 4 /* indicates the current load shedding state of the object */ static BACNET_SHED_STATE Present_Value[MAX_LOAD_CONTROLS]; - + /* load control objects are required to support LEVEL */ typedef enum BACnetShedLevelType { BACNET_SHED_TYPE_PERCENT, /* Unsigned */ BACNET_SHED_TYPE_LEVEL, /* Unsigned */ BACNET_SHED_TYPE_AMOUNT /* REAL */ } BACNET_SHED_LEVEL_TYPE; - -#define DEFAULT_VALUE_PERCENT 100 -#define DEFAULT_VALUE_LEVEL 0 -#define DEFAULT_VALUE_AMOUNT 0 + +#define DEFAULT_VALUE_PERCENT 100 +#define DEFAULT_VALUE_LEVEL 0 +#define DEFAULT_VALUE_AMOUNT 0 /* The shed levels for the LEVEL choice of BACnetShedLevel that have meaning for this particular Load Control object. */ @@ -91,8 +91,8 @@ static uint32_t Duty_Window[MAX_LOAD_CONTROLS]; /* indicates and controls whether the Load Control object is currently enabled to respond to load shed requests. */ static bool Load_Control_Enable[MAX_LOAD_CONTROLS]; - -/* indicates when the object receives a write to any of the properties + +/* indicates when the object receives a write to any of the properties Requested_Shed_Level, Shed_Duration, Duty_Window */ static bool Load_Control_Request_Written[MAX_LOAD_CONTROLS]; /* indicates when the object receives a write to Start_Time */ @@ -118,12 +118,12 @@ static char *Shed_Level_Descriptions[MAX_SHED_LEVELS] = { "dim lights 20%", "dim lights 30%" }; -static float Shed_Level_Values[MAX_SHED_LEVELS] = { - 90.0, - 80.0, - 70.0 +static float Shed_Level_Values[MAX_SHED_LEVELS] = { + 90.0, + 80.0, + 70.0 }; - + /* we need to have our arrays initialized before answering any calls */ static bool Load_Control_Initialized = false; @@ -139,7 +139,7 @@ void Load_Control_Init(void) Present_Value[i] = BACNET_SHED_INACTIVE; Requested_Shed_Level[i].type = BACNET_SHED_TYPE_LEVEL; Requested_Shed_Level[i].value.level = 0; - datetime_wildcard_set(&Start_Time[i]); + datetime_wildcard_set(&Start_Time[i]); Shed_Duration[i] = 0; Duty_Window[i] = 0; Load_Control_Enable[i] = true; @@ -257,124 +257,135 @@ struct tm { (uint16_t)tblock->tm_year, (uint8_t)tblock->tm_mon, (uint8_t)tblock->tm_mday, - (uint8_t)tblock->tm_hour, - (uint8_t)tblock->tm_min, + (uint8_t)tblock->tm_hour, + (uint8_t)tblock->tm_min, (uint8_t)tblock->tm_sec, 0); } - -/* convert the shed level request into an Analog Output Present_Value */ -static float Requested_Shed_Level_Value(int object_index) -{ - unsigned shed_level_index = 0; - unsigned i = 0; - float requested_level = 0.0; - - switch (Requested_Shed_Level[object_index].type) { - case BACNET_SHED_TYPE_PERCENT: - requested_level = (float)Requested_Shed_Level[object_index].value.percent; + +/* convert the shed level request into an Analog Output Present_Value */ +static float Requested_Shed_Level_Value(int object_index) +{ + unsigned shed_level_index = 0; + unsigned i = 0; + float requested_level = 0.0; + + switch (Requested_Shed_Level[object_index].type) { + case BACNET_SHED_TYPE_PERCENT: + requested_level = (float)Requested_Shed_Level[object_index].value.percent; break; case BACNET_SHED_TYPE_AMOUNT: - /* Assumptions: wattage is linear with analog output level */ - requested_level = Full_Duty_Baseline[object_index] - Requested_Shed_Level[object_index].value.amount; - requested_level /= Full_Duty_Baseline[object_index]; - requested_level *= 100.0; + /* Assumptions: wattage is linear with analog output level */ + requested_level = Full_Duty_Baseline[object_index] - Requested_Shed_Level[object_index].value.amount; + requested_level /= Full_Duty_Baseline[object_index]; + requested_level *= 100.0; break; case BACNET_SHED_TYPE_LEVEL: - default: + default: for (i = 0; i < MAX_SHED_LEVELS; i++) { - if (Shed_Levels[object_index][i] <= Requested_Shed_Level[object_index].value.level) + if (Shed_Levels[object_index][i] <= Requested_Shed_Level[object_index].value.level) shed_level_index = i; - } + } requested_level = Shed_Level_Values[shed_level_index]; break; - } - - return requested_level; -} - -static void Shed_Level_Copy(BACNET_SHED_LEVEL *dest, BACNET_SHED_LEVEL *src) -{ - if (dest && src) { - dest->type = src->type; - switch (src->type) { - case BACNET_SHED_TYPE_PERCENT: - dest->value.percent = src->value.percent; + } + + return requested_level; +} + +static void Shed_Level_Copy(BACNET_SHED_LEVEL *dest, BACNET_SHED_LEVEL *src) +{ + if (dest && src) { + dest->type = src->type; + switch (src->type) { + case BACNET_SHED_TYPE_PERCENT: + dest->value.percent = src->value.percent; break; case BACNET_SHED_TYPE_AMOUNT: - dest->value.amount = src->value.amount; + dest->value.amount = src->value.amount; break; case BACNET_SHED_TYPE_LEVEL: - default: - dest->value.level = src->value.level; - break; + default: + dest->value.level = src->value.level; + break; } - } -} - -static void Shed_Level_Default_Set(BACNET_SHED_LEVEL *dest, BACNET_SHED_LEVEL_TYPE type) -{ - if (dest) { - dest->type = type; - switch (type) { - case BACNET_SHED_TYPE_PERCENT: - dest->value.percent = 100; + } +} + +static void Shed_Level_Default_Set(BACNET_SHED_LEVEL *dest, BACNET_SHED_LEVEL_TYPE type) +{ + if (dest) { + dest->type = type; + switch (type) { + case BACNET_SHED_TYPE_PERCENT: + dest->value.percent = 100; break; case BACNET_SHED_TYPE_AMOUNT: - dest->value.amount = 0.0; + dest->value.amount = 0.0; break; case BACNET_SHED_TYPE_LEVEL: - default: - dest->value.level = 0; - break; + default: + dest->value.level = 0; + break; } - } -} - -static bool Able_To_Meet_Shed_Request(int object_index) -{ - float level = 0.0; - float requested_level = 0.0; - unsigned priority = 0; - bool status = false; - int object_instance = 0; - - /* This demo is going to use the Analog Outputs as their Load */ - object_instance = object_index; - priority = Analog_Output_Present_Value_Priority(object_instance); - /* we are controlling at Priority 4 - can we control the output? */ - if (priority >= 4) { - /* is the level able to be lowered? */ - requested_level = Requested_Shed_Level_Value(object_index); - level = Analog_Output_Present_Value(object_instance); - if (level >= requested_level) { - status = true; - } - } - - return status; -} - + } +} + +static bool Able_To_Meet_Shed_Request(int object_index) +{ + float level = 0.0; + float requested_level = 0.0; + unsigned priority = 0; + bool status = false; + int object_instance = 0; + + /* This demo is going to use the Analog Outputs as their Load */ + object_instance = object_index; + priority = Analog_Output_Present_Value_Priority(object_instance); + /* we are controlling at Priority 4 - can we control the output? */ + if (priority >= 4) { + /* is the level able to be lowered? */ + requested_level = Requested_Shed_Level_Value(object_index); + level = Analog_Output_Present_Value(object_instance); + if (level >= requested_level) { + status = true; + } + } + + return status; +} + typedef enum load_control_state { SHED_INACTIVE, SHED_REQUEST_PENDING, SHED_NON_COMPLIANT, - SHED_COMPLIANT + SHED_COMPLIANT, + MAX_LOAD_CONTROL_STATE } LOAD_CONTROL_STATE; static LOAD_CONTROL_STATE Load_Control_State[MAX_LOAD_CONTROLS]; +static LOAD_CONTROL_STATE Load_Control_State_Previously[MAX_LOAD_CONTROLS]; + +static void Print_Load_Control_State(int object_index) +{ + char *Load_Control_State_Text[MAX_LOAD_CONTROLS] = { + "SHED_INACTIVE", + "SHED_REQUEST_PENDING", + "SHED_NON_COMPLIANT", + "SHED_COMPLIANT" + }; + + if (object_index < MAX_LOAD_CONTROLS) { + if (Load_Control_State[object_index] < MAX_LOAD_CONTROL_STATE) { + printf("Load Control[%d]=%s\n",object_index, + Load_Control_State_Text[Load_Control_State[object_index]]); + } + } +} void Load_Control_State_Machine(int object_index) { - static bool initialized = false; unsigned i = 0; /* loop counter */ int diff = 0; /* used for datetime comparison */ - if (!initialized) { - initialized = true; - for (i = 0; i < MAX_LOAD_CONTROLS; i++) { - Load_Control_State[i] = SHED_INACTIVE; - } - } - switch (Load_Control_State[object_index]) { case SHED_REQUEST_PENDING: if (Load_Control_Request_Written[object_index]) { @@ -398,8 +409,10 @@ void Load_Control_State_Machine(int object_index) Load_Control_State[object_index] = SHED_INACTIVE; break; } - if (Load_Control_State[object_index] == SHED_INACTIVE) + if (Load_Control_State[object_index] == SHED_INACTIVE) { + printf("Load Control[%d]:Requested Shed Level=Default\n",object_index); break; + } } if (Start_Time_Property_Written[object_index]) { Start_Time_Property_Written[object_index] = false; @@ -408,7 +421,7 @@ void Load_Control_State_Machine(int object_index) Load_Control_State[object_index] = SHED_INACTIVE; break; } - } + } /* cancel because current time is after start time + duration? */ Update_Current_Time(&Current_Time); datetime_copy(&End_Time[object_index], &Start_Time[object_index]); @@ -418,45 +431,47 @@ void Load_Control_State_Machine(int object_index) if (diff < 0) { /* CancelShed */ /* FIXME: stop shedding! i.e. relinquish */ + printf("Load Control[%d]:Current Time is after Start Time + Duration\n",object_index); Load_Control_State[object_index] = SHED_INACTIVE; break; } diff = datetime_compare(&Current_Time, &Start_Time[object_index]); - if (diff < 0) { + if (diff < 0) { /* current time prior to start time */ - /* ReconfigurePending */ - Shed_Level_Copy( - &Expected_Shed_Level[object_index], - &Requested_Shed_Level[object_index]); - Shed_Level_Default_Set( - &Actual_Shed_Level[object_index], - Requested_Shed_Level[object_index].type); - } else if (diff > 0) { + /* ReconfigurePending */ + Shed_Level_Copy( + &Expected_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Shed_Level_Default_Set( + &Actual_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + } else if (diff > 0) { /* current time after to start time */ + printf("Load Control[%d]:Current Time is after Start Time\n",object_index); /* AbleToMeetShed */ - if (Able_To_Meet_Shed_Request(object_index)) { - Shed_Level_Copy( - &Expected_Shed_Level[object_index], - &Requested_Shed_Level[object_index]); - Analog_Output_Present_Value_Set(object_index, - Requested_Shed_Level_Value(object_index), 4); - Shed_Level_Copy( - &Actual_Shed_Level[object_index], - &Requested_Shed_Level[object_index]); + if (Able_To_Meet_Shed_Request(object_index)) { + Shed_Level_Copy( + &Expected_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Analog_Output_Present_Value_Set(object_index, + Requested_Shed_Level_Value(object_index), 4); + Shed_Level_Copy( + &Actual_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); Load_Control_State[object_index] = SHED_COMPLIANT; - } else { - /* CannotMeetShed */ - Shed_Level_Default_Set( - &Expected_Shed_Level[object_index], - Requested_Shed_Level[object_index].type); - Shed_Level_Default_Set( - &Actual_Shed_Level[object_index], - Requested_Shed_Level[object_index].type); + } else { + /* CannotMeetShed */ + Shed_Level_Default_Set( + &Expected_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + Shed_Level_Default_Set( + &Actual_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); Load_Control_State[object_index] = SHED_NON_COMPLIANT; - } + } } break; - case SHED_NON_COMPLIANT: + case SHED_NON_COMPLIANT: Update_Current_Time(&Current_Time); datetime_copy(&End_Time[object_index], &Start_Time[object_index]); datetime_add_minutes(&End_Time[object_index], @@ -464,71 +479,78 @@ void Load_Control_State_Machine(int object_index) diff = datetime_compare(&End_Time[object_index], &Current_Time); if (diff < 0) { /* FinishedUnsuccessfulShed */ + printf("Load Control[%d]:Current Time is after Start Time + Duration\n",object_index); Load_Control_State[object_index] = SHED_INACTIVE; break; } - if (Load_Control_Request_Written[object_index] || - Start_Time_Property_Written[object_index]) { + if (Load_Control_Request_Written[object_index] || + Start_Time_Property_Written[object_index]) { /* UnsuccessfulShedReconfigured */ - Load_Control_Request_Written[object_index] = false; - Start_Time_Property_Written[object_index] = false; - Load_Control_State[object_index] = SHED_REQUEST_PENDING; + printf("Load Control[%d]:Control Property written\n",object_index); + Load_Control_Request_Written[object_index] = false; + Start_Time_Property_Written[object_index] = false; + Load_Control_State[object_index] = SHED_REQUEST_PENDING; break; - } - if (Able_To_Meet_Shed_Request(object_index)) { - /* CanNowComplyWithShed */ - Shed_Level_Copy( - &Expected_Shed_Level[object_index], - &Requested_Shed_Level[object_index]); - Analog_Output_Present_Value_Set(object_index, - Requested_Shed_Level_Value(object_index), 4); - Shed_Level_Copy( - &Actual_Shed_Level[object_index], - &Requested_Shed_Level[object_index]); - Load_Control_State[object_index] = SHED_COMPLIANT; + } + if (Able_To_Meet_Shed_Request(object_index)) { + /* CanNowComplyWithShed */ + printf("Load Control[%d]:Able to meet Shed Request\n",object_index); + Shed_Level_Copy( + &Expected_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Analog_Output_Present_Value_Set(object_index, + Requested_Shed_Level_Value(object_index), 4); + Shed_Level_Copy( + &Actual_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Load_Control_State[object_index] = SHED_COMPLIANT; } break; - case SHED_COMPLIANT: + case SHED_COMPLIANT: Update_Current_Time(&Current_Time); datetime_copy(&End_Time[object_index], &Start_Time[object_index]); datetime_add_minutes(&End_Time[object_index], Shed_Duration[object_index]); diff = datetime_compare(&End_Time[object_index], &Current_Time); if (diff < 0) { - /* FinishedSuccessfulShed */ - datetime_wildcard_set(&Start_Time[i]); + /* FinishedSuccessfulShed */ + printf("Load Control[%d]:Current Time is after Start Time + Duration\n",object_index); + datetime_wildcard_set(&Start_Time[i]); Load_Control_State[object_index] = SHED_INACTIVE; break; - } - if (Load_Control_Request_Written[object_index] || + } + if (Load_Control_Request_Written[object_index] || Start_Time_Property_Written[object_index]) { /* UnsuccessfulShedReconfigured */ - Load_Control_Request_Written[object_index] = false; - Start_Time_Property_Written[object_index] = false; - Load_Control_State[object_index] = SHED_REQUEST_PENDING; + printf("Load Control[%d]:Control Property written\n",object_index); + Load_Control_Request_Written[object_index] = false; + Start_Time_Property_Written[object_index] = false; + Load_Control_State[object_index] = SHED_REQUEST_PENDING; break; - } - if (!Able_To_Meet_Shed_Request(object_index)) { - /* CanNoLongerComplyWithShed */ - Shed_Level_Default_Set( - &Expected_Shed_Level[object_index], - Requested_Shed_Level[object_index].type); - Shed_Level_Default_Set( - &Actual_Shed_Level[object_index], - Requested_Shed_Level[object_index].type); + } + if (!Able_To_Meet_Shed_Request(object_index)) { + /* CanNoLongerComplyWithShed */ + printf("Load Control[%d]:Not able to meet Shed Request\n",object_index); + Shed_Level_Default_Set( + &Expected_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + Shed_Level_Default_Set( + &Actual_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); Load_Control_State[object_index] = SHED_NON_COMPLIANT; - } + } break; case SHED_INACTIVE: - default: + default: if (Start_Time_Property_Written[object_index]) { + printf("Load Control[%d]:Start Time written\n",object_index); Start_Time_Property_Written[object_index] = false; - Shed_Level_Copy( - &Expected_Shed_Level[object_index], - &Requested_Shed_Level[object_index]); - Shed_Level_Default_Set( - &Actual_Shed_Level[object_index], - Requested_Shed_Level[object_index].type); + Shed_Level_Copy( + &Expected_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Shed_Level_Default_Set( + &Actual_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); Load_Control_State[object_index] = SHED_REQUEST_PENDING; } break; @@ -541,10 +563,24 @@ void Load_Control_State_Machine(int object_index) void Load_Control_State_Machine_Handler(void) { unsigned i = 0; + static bool initialized = false; Load_Control_Init(); + if (!initialized) { + initialized = true; + for (i = 0; i < MAX_LOAD_CONTROLS; i++) { + Load_Control_State[i] = SHED_INACTIVE; + Load_Control_State_Previously[i] = SHED_INACTIVE; + } + } for (i = 0; i < MAX_LOAD_CONTROLS; i++) { Load_Control_State_Machine(i); + if (Load_Control_State[i] != Load_Control_State_Previously[i]) { + Print_Load_Control_State(i); + Load_Control_State_Previously[i] = Load_Control_State[i]; + } + + } } @@ -792,7 +828,7 @@ bool Load_Control_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, wp_data->application_data_len, &value, PROP_REQUESTED_SHED_LEVEL); if (value.tag == 0) { - /* percent - Unsigned */ + /* percent - Unsigned */ Requested_Shed_Level[object_index].type = BACNET_SHED_TYPE_PERCENT; Requested_Shed_Level[object_index].value.percent = value.type.Unsigned_Int; @@ -813,9 +849,9 @@ bool Load_Control_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, /* error! */ *error_class = ERROR_CLASS_PROPERTY; *error_code = ERROR_CODE_INVALID_DATA_TYPE; - } - if (status) { - Load_Control_Request_Written[object_index] = true; + } + if (status) { + Load_Control_Request_Written[object_index] = true; } break; case PROP_START_TIME: @@ -905,31 +941,37 @@ bool Load_Control_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, #include #include #include "ctest.h" - + void testLoadControlStateMachine(Test * pTest) -{ - unsigned i = 0, j = 0; +{ + unsigned i = 0, j = 0; Load_Control_Init(); - BACNET_APPLICATION_DATA_VALUE value; - BACNET_ERROR_CLASS error_class; - BACNET_ERROR_CODE error_code; - bool status = false; - BACNET_WRITE_PROPERTY_DATA wp_data; - - /* validate the triggers for each state change */ - for (j = 0; j < 20; j++) { - Load_Control_State_Machine_Handler(); + + BACNET_APPLICATION_DATA_VALUE value; + + BACNET_ERROR_CLASS error_class; + + BACNET_ERROR_CODE error_code; + + bool status = false; + + BACNET_WRITE_PROPERTY_DATA wp_data; + + + /* validate the triggers for each state change */ + for (j = 0; j < 20; j++) { + Load_Control_State_Machine_Handler(); for (i = 0; i < MAX_LOAD_CONTROLS; i++) { - ct_test(pTest, Load_Control_State[i] == SHED_INACTIVE); - } - } - - /**/ - status = Load_Control_Write_Property(&wp_data, &error_class, &error_code); - - - - + ct_test(pTest, Load_Control_State[i] == SHED_INACTIVE); + } + } + + /**/ + status = Load_Control_Write_Property(&wp_data, &error_class, &error_code); + + + + } void testLoadControl(Test * pTest) @@ -969,7 +1011,7 @@ int main(void) pTest = ct_create("BACnet Load Control", NULL); /* individual tests */ rc = ct_addTestFunction(pTest, testLoadControl); - assert(rc); + assert(rc); rc = ct_addTestFunction(pTest, testLoadControlStateMachine); assert(rc);