Adding more functionality to load control object state machine.
This commit is contained in:
+128
-69
@@ -58,7 +58,6 @@ typedef enum BACnetShedLevelType {
|
|||||||
|
|
||||||
/* The shed levels for the LEVEL choice of BACnetShedLevel
|
/* The shed levels for the LEVEL choice of BACnetShedLevel
|
||||||
that have meaning for this particular Load Control object. */
|
that have meaning for this particular Load Control object. */
|
||||||
#define MAX_SHED_LEVELS 3
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
BACNET_SHED_LEVEL_TYPE type;
|
BACNET_SHED_LEVEL_TYPE type;
|
||||||
union {
|
union {
|
||||||
@@ -80,7 +79,6 @@ static BACNET_SHED_LEVEL Actual_Shed_Level[MAX_LOAD_CONTROLS];
|
|||||||
by the Load Control object must be compliant with the requested shed. */
|
by the Load Control object must be compliant with the requested shed. */
|
||||||
static BACNET_DATE_TIME Start_Time[MAX_LOAD_CONTROLS];
|
static BACNET_DATE_TIME Start_Time[MAX_LOAD_CONTROLS];
|
||||||
static BACNET_DATE_TIME End_Time[MAX_LOAD_CONTROLS];
|
static BACNET_DATE_TIME End_Time[MAX_LOAD_CONTROLS];
|
||||||
static BACNET_DATE_TIME Previous_Start_Time[MAX_LOAD_CONTROLS];
|
|
||||||
static BACNET_DATE_TIME Current_Time;
|
static BACNET_DATE_TIME Current_Time;
|
||||||
|
|
||||||
/* indicates the duration of the load shed action,
|
/* indicates the duration of the load shed action,
|
||||||
@@ -93,6 +91,12 @@ static uint32_t Duty_Window[MAX_LOAD_CONTROLS];
|
|||||||
/* indicates and controls whether the Load Control object is
|
/* indicates and controls whether the Load Control object is
|
||||||
currently enabled to respond to load shed requests. */
|
currently enabled to respond to load shed requests. */
|
||||||
static bool Load_Control_Enable[MAX_LOAD_CONTROLS];
|
static bool Load_Control_Enable[MAX_LOAD_CONTROLS];
|
||||||
|
|
||||||
|
/* indicates when the object receives a write to any of the properties
|
||||||
|
Requested_Shed_Level, Shed_Duration, Duty_Window */
|
||||||
|
static bool Load_Control_Property_Written[MAX_LOAD_CONTROLS];
|
||||||
|
/* indicates when the object receives a write to Start_Time */
|
||||||
|
static bool Start_Time_Property_Written[MAX_LOAD_CONTROLS];
|
||||||
|
|
||||||
/* optional: indicates the baseline power consumption value
|
/* optional: indicates the baseline power consumption value
|
||||||
for the sheddable load controlled by this object,
|
for the sheddable load controlled by this object,
|
||||||
@@ -100,9 +104,10 @@ static bool Load_Control_Enable[MAX_LOAD_CONTROLS];
|
|||||||
The units of Full_Duty_Baseline are kilowatts.*/
|
The units of Full_Duty_Baseline are kilowatts.*/
|
||||||
static float Full_Duty_Baseline[MAX_LOAD_CONTROLS];
|
static float Full_Duty_Baseline[MAX_LOAD_CONTROLS];
|
||||||
|
|
||||||
|
#define MAX_SHED_LEVELS 3
|
||||||
/* Represents the shed levels for the LEVEL choice of
|
/* Represents the shed levels for the LEVEL choice of
|
||||||
BACnetShedLevel that have meaning for this particular
|
BACnetShedLevel that have meaning for this particular
|
||||||
Load Control object. */
|
Load Control object. */
|
||||||
static unsigned Shed_Levels[MAX_LOAD_CONTROLS][MAX_SHED_LEVELS];
|
static unsigned Shed_Levels[MAX_LOAD_CONTROLS][MAX_SHED_LEVELS];
|
||||||
|
|
||||||
/* represents a description of the shed levels that the
|
/* represents a description of the shed levels that the
|
||||||
@@ -113,6 +118,12 @@ static char *Shed_Level_Descriptions[MAX_SHED_LEVELS] = {
|
|||||||
"dim lights 20%",
|
"dim lights 20%",
|
||||||
"dim lights 30%"
|
"dim lights 30%"
|
||||||
};
|
};
|
||||||
|
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 */
|
/* we need to have our arrays initialized before answering any calls */
|
||||||
static bool Load_Control_Initialized = false;
|
static bool Load_Control_Initialized = false;
|
||||||
@@ -129,7 +140,6 @@ void Load_Control_Init(void)
|
|||||||
Requested_Shed_Level[i].type = BACNET_SHED_TYPE_LEVEL;
|
Requested_Shed_Level[i].type = BACNET_SHED_TYPE_LEVEL;
|
||||||
Requested_Shed_Level[i].value.level = 0;
|
Requested_Shed_Level[i].value.level = 0;
|
||||||
datetime_wildcard_set(&Start_Time[i]);
|
datetime_wildcard_set(&Start_Time[i]);
|
||||||
datetime_wildcard_set(&Previous_Start_Time[i]);
|
|
||||||
Shed_Duration[i] = 0;
|
Shed_Duration[i] = 0;
|
||||||
Duty_Window[i] = 0;
|
Duty_Window[i] = 0;
|
||||||
Load_Control_Enable[i] = true;
|
Load_Control_Enable[i] = true;
|
||||||
@@ -137,7 +147,7 @@ void Load_Control_Init(void)
|
|||||||
for (j = 0; j < MAX_SHED_LEVELS; j++) {
|
for (j = 0; j < MAX_SHED_LEVELS; j++) {
|
||||||
/* FIXME: fake data for lighting application */
|
/* FIXME: fake data for lighting application */
|
||||||
/* The array shall be ordered by increasing shed amount. */
|
/* The array shall be ordered by increasing shed amount. */
|
||||||
Shed_Levels[i][j] = 90 - (j * (30 / MAX_SHED_LEVELS));
|
Shed_Levels[i][j] = 1 + j;
|
||||||
}
|
}
|
||||||
Expected_Shed_Level[i].type = BACNET_SHED_TYPE_LEVEL;
|
Expected_Shed_Level[i].type = BACNET_SHED_TYPE_LEVEL;
|
||||||
Expected_Shed_Level[i].value.level = 0;
|
Expected_Shed_Level[i].value.level = 0;
|
||||||
@@ -248,6 +258,74 @@ struct tm {
|
|||||||
tblock->tm_hour, tblock->tm_min, tblock->tm_sec, 0);
|
tblock->tm_hour, tblock->tm_min, tblock->tm_sec, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* convert the shed level request into a 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;
|
||||||
|
break;
|
||||||
|
case BACNET_SHED_TYPE_LEVEL:
|
||||||
|
default:
|
||||||
|
for (i = 0; i < MAX_SHED_LEVELS; i++) {
|
||||||
|
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;
|
||||||
|
break;
|
||||||
|
case BACNET_SHED_TYPE_AMOUNT:
|
||||||
|
dest->value.amount = src->value.amount;
|
||||||
|
break;
|
||||||
|
case BACNET_SHED_TYPE_LEVEL:
|
||||||
|
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;
|
||||||
|
break;
|
||||||
|
case BACNET_SHED_TYPE_AMOUNT:
|
||||||
|
dest->value.amount = 0.0;
|
||||||
|
break;
|
||||||
|
case BACNET_SHED_TYPE_LEVEL:
|
||||||
|
default:
|
||||||
|
dest->value.level = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool Able_To_Meet_Shed_Request(int object_index)
|
static bool Able_To_Meet_Shed_Request(int object_index)
|
||||||
{
|
{
|
||||||
float level = 0.0;
|
float level = 0.0;
|
||||||
@@ -256,49 +334,24 @@ static bool Able_To_Meet_Shed_Request(int object_index)
|
|||||||
bool status = false;
|
bool status = false;
|
||||||
int object_instance = 0;
|
int object_instance = 0;
|
||||||
unsigned shed_level_index = 0;
|
unsigned shed_level_index = 0;
|
||||||
|
unsigned i = 0;
|
||||||
|
|
||||||
/* This demo is going to use the Analog Outputs as their Load */
|
/* This demo is going to use the Analog Outputs as their Load */
|
||||||
object_instance = object_index;
|
object_instance = object_index;
|
||||||
/* Assumptions: 1500 watt loads on each Analog Output
|
|
||||||
wattage is linear with analog output level
|
|
||||||
10% reduction is 150 watt reduction
|
|
||||||
Analog Output present value is in percent */
|
|
||||||
priority = Analog_Output_Present_Value_Priority(object_instance);
|
priority = Analog_Output_Present_Value_Priority(object_instance);
|
||||||
/* we are controlling at Priority 4 - can we control the output? */
|
/* we are controlling at Priority 4 - can we control the output? */
|
||||||
if (priority >= 4) {
|
if (priority >= 4) {
|
||||||
/* is the level able to be lowered? */
|
/* is the level able to be lowered? */
|
||||||
|
requested_level = Requested_Shed_Level_Value(object_index);
|
||||||
level = Analog_Output_Present_Value(object_instance);
|
level = Analog_Output_Present_Value(object_instance);
|
||||||
switch (Requested_Shed_Level[object_index].type) {
|
if (level >= requested_level) {
|
||||||
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;
|
|
||||||
break;
|
|
||||||
case BACNET_SHED_TYPE_LEVEL:
|
|
||||||
default:
|
|
||||||
shed_level_index = Requested_Shed_Level[object_index].value.level;
|
|
||||||
if (shed_level_index < MAX_SHED_LEVELS) {
|
|
||||||
/* FIXME: contradiction - Shed_Levels is able to be changed? */
|
|
||||||
/* FIXME: Shed_Levels is unsigned, not REAL like baseline */
|
|
||||||
/* FIXME: where do I get my requested level? */
|
|
||||||
/* Shed_Levels[object_index][shed_level_index]; */
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (level >= requested_level)
|
|
||||||
status = true;
|
status = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef enum load_control_state {
|
typedef enum load_control_state {
|
||||||
SHED_INACTIVE,
|
SHED_INACTIVE,
|
||||||
SHED_REQUEST_PENDING,
|
SHED_REQUEST_PENDING,
|
||||||
@@ -363,48 +416,53 @@ void Load_Control_State_Machine(int object_index)
|
|||||||
diff = datetime_compare(&Current_Time, &Start_Time[object_index]);
|
diff = datetime_compare(&Current_Time, &Start_Time[object_index]);
|
||||||
if (diff < 0) {
|
if (diff < 0) {
|
||||||
/* current time prior to start time */
|
/* current time prior to start time */
|
||||||
/* ReconfigurePending */
|
/* ReconfigurePending */
|
||||||
/* PrepareToShed */
|
Shed_Level_Copy(
|
||||||
/* AbleToMeetShed */
|
&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) {
|
} else if (diff > 0) {
|
||||||
/* current time after to start time */
|
/* current time after to start time */
|
||||||
/* AbleToMeetShed */
|
/* AbleToMeetShed */
|
||||||
/* CannotMeetShed */
|
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]);
|
||||||
|
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);
|
||||||
|
state[object_index] = SHED_NON_COMPLIANT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SHED_NON_COMPLIANT:
|
case SHED_NON_COMPLIANT:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case SHED_COMPLIANT:
|
case SHED_COMPLIANT:
|
||||||
break;
|
break;
|
||||||
case SHED_INACTIVE:
|
case SHED_INACTIVE:
|
||||||
default:
|
default:
|
||||||
diff = datetime_compare(&Previous_Start_Time[object_index],
|
if (Start_Time_Property_Written[object_index]) {
|
||||||
&Start_Time[object_index]);
|
Start_Time_Property_Written[object_index] = false;
|
||||||
if (diff != 0) {
|
Shed_Level_Copy(
|
||||||
datetime_copy(&Previous_Start_Time[object_index],
|
&Expected_Shed_Level[object_index],
|
||||||
&Start_Time[object_index]);
|
&Requested_Shed_Level[object_index]);
|
||||||
/* FIXME: calculate your Expected Shed Level */
|
Shed_Level_Default_Set(
|
||||||
/* FIXME: calculate your Actual Shed Level */
|
&Actual_Shed_Level[object_index],
|
||||||
Expected_Shed_Level[object_index].type =
|
Requested_Shed_Level[object_index].type);
|
||||||
Requested_Shed_Level[object_index].type;
|
|
||||||
switch (Requested_Shed_Level[object_index].type) {
|
|
||||||
case BACNET_SHED_TYPE_PERCENT:
|
|
||||||
Actual_Shed_Level[object_index].value.percent =
|
|
||||||
Expected_Shed_Level[object_index].value.percent =
|
|
||||||
Requested_Shed_Level[object_index].value.percent;
|
|
||||||
break;
|
|
||||||
case BACNET_SHED_TYPE_AMOUNT:
|
|
||||||
Actual_Shed_Level[object_index].value.amount =
|
|
||||||
Expected_Shed_Level[object_index].value.amount =
|
|
||||||
Requested_Shed_Level[object_index].value.amount;
|
|
||||||
break;
|
|
||||||
case BACNET_SHED_TYPE_LEVEL:
|
|
||||||
default:
|
|
||||||
Actual_Shed_Level[object_index].value.level =
|
|
||||||
Expected_Shed_Level[object_index].value.level =
|
|
||||||
Requested_Shed_Level[object_index].value.level;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
state[object_index] = SHED_REQUEST_PENDING;
|
state[object_index] = SHED_REQUEST_PENDING;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -694,6 +752,7 @@ bool Load_Control_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data,
|
|||||||
if (value.tag == BACNET_APPLICATION_TAG_DATE) {
|
if (value.tag == BACNET_APPLICATION_TAG_DATE) {
|
||||||
memcpy(&Start_Time[object_index].date,
|
memcpy(&Start_Time[object_index].date,
|
||||||
&value.type.Date, sizeof(value.type.Date));
|
&value.type.Date, sizeof(value.type.Date));
|
||||||
|
Start_Time_Property_Written[object_index] = true;
|
||||||
status = true;
|
status = true;
|
||||||
} else {
|
} else {
|
||||||
*error_class = ERROR_CLASS_PROPERTY;
|
*error_class = ERROR_CLASS_PROPERTY;
|
||||||
|
|||||||
Reference in New Issue
Block a user