Bugfix/lighting output low high trim fade (#1268)

* Add low and high trim fade functionality to lighting command operations

* Refactor lighting command override functions and fix operating and normalized range.

* Add physical range clamping function and update related clamping logic
This commit is contained in:
Steve Karg
2026-03-20 17:10:10 -05:00
committed by GitHub
parent 457d04a7df
commit 05f81b05ce
5 changed files with 563 additions and 64 deletions
+98 -20
View File
@@ -36,6 +36,8 @@ struct object_data {
uint32_t Egress_Time_Seconds;
uint32_t Default_Fade_Time;
uint32_t Trim_Fade_Time;
float High_End_Trim;
float Low_End_Trim;
float Default_Ramp_Rate;
float Default_Step_Increment;
BACNET_LIGHTING_TRANSITION Transition;
@@ -498,6 +500,57 @@ unsigned Lighting_Output_Present_Value_Priority(uint32_t object_instance)
return priority;
}
/**
* @brief Determine if fade, ramp, or warn command is currently executing
* @param pObject [in] object to apply the trim values to
* @param priority [in] priority of the command
*/
static bool Lighting_Command_In_Progress(struct object_data *pObject)
{
bool in_progress = false;
if (!pObject) {
return in_progress;
}
if ((pObject->Lighting_Command.In_Progress ==
BACNET_LIGHTING_FADE_ACTIVE) ||
(pObject->Lighting_Command.In_Progress ==
BACNET_LIGHTING_RAMP_ACTIVE) ||
(pObject->Lighting_Command.Blink.Duration > 0)) {
in_progress = true;
}
return in_progress;
}
/**
* @brief Configure the lighting command to apply low or high trim
* to the tracking value based on the priority of the command
* @param pObject [in] object to apply the trim values to
* @param priority [in] priority of the command
*/
static void
Lighting_Command_Trim_Apply(struct object_data *pObject, unsigned priority)
{
if (!pObject) {
return;
}
/* If Present_Value is commanded at priority 1 or 2,
High_End_Trim and Low_End_Trim shall not be applied and
the Tracking_Value shall not be clamped. */
if ((priority == 1) || (priority == 2)) {
/* remove any high or low trim */
pObject->Lighting_Command.High_Trim_Value = 100.0f;
pObject->Lighting_Command.Low_Trim_Value = 1.0f;
pObject->Lighting_Command.Trim_Fade_Time = 0;
} else {
/* apply high and low trim */
pObject->Lighting_Command.High_Trim_Value = pObject->High_End_Trim;
pObject->Lighting_Command.Low_Trim_Value = pObject->Low_End_Trim;
pObject->Lighting_Command.Trim_Fade_Time = pObject->Trim_Fade_Time;
}
}
/**
* @brief Set the lighting command if the priority is active
* @param object [in] BACnet object instance
@@ -519,7 +572,9 @@ static void Lighting_Command_Fade_To(
Present_Value_Set(pObject, value, priority);
current_priority = Present_Value_Priority(pObject);
if (priority <= current_priority) {
/* we have priority - configure the Lighting Command */
/* we have priority */
Lighting_Command_Trim_Apply(pObject, priority);
/* configure the Lighting Command */
lighting_command_fade_to(&pObject->Lighting_Command, value, fade_time);
}
}
@@ -545,7 +600,9 @@ static void Lighting_Command_Ramp_To(
Present_Value_Set(pObject, value, priority);
current_priority = Present_Value_Priority(pObject);
if (priority <= current_priority) {
/* we have priority - configure the Lighting Command */
/* we have priority */
Lighting_Command_Trim_Apply(pObject, priority);
/* configure the Lighting Command */
lighting_command_ramp_to(&pObject->Lighting_Command, value, ramp_rate);
}
}
@@ -598,6 +655,7 @@ Lighting_Command_Warn(struct object_data *pObject, unsigned priority)
(Priority_Array_Active(pObject, priority - 1)) &&
(!is_float_equal(Priority_Array_Value(pObject, priority - 1), 0.0)) &&
pObject->Blink_Warn_Enable) {
Lighting_Command_Trim_Apply(pObject, priority);
/* The blink-warn notification shall not occur
if any of the following conditions occur:
(a) The specified priority is not the highest
@@ -662,6 +720,7 @@ Lighting_Command_Warn_Off(struct object_data *pObject, unsigned priority)
}
current_priority = Present_Value_Priority(pObject);
if (priority <= current_priority) {
Lighting_Command_Trim_Apply(pObject, priority);
if ((Priority_Array_Active(pObject, priority - 1)) &&
(!is_float_equal(
Priority_Array_Value(pObject, priority - 1), 0.0)) &&
@@ -732,6 +791,7 @@ Lighting_Command_Warn_Relinquish(struct object_data *pObject, unsigned priority)
}
current_priority = Present_Value_Priority(pObject);
if (priority <= current_priority) {
Lighting_Command_Trim_Apply(pObject, priority);
if ((Priority_Array_Active(pObject, priority - 1)) &&
(!is_float_equal(
Priority_Array_Value(pObject, priority - 1), 0.0)) &&
@@ -813,6 +873,7 @@ static void Lighting_Command_Step_Up_On(
current_priority = Present_Value_Priority(pObject);
if (priority <= current_priority) {
/* we have priority - configure the Lighting Command */
Lighting_Command_Trim_Apply(pObject, priority);
lighting_command_step(
&pObject->Lighting_Command, operation, step_increment);
}
@@ -870,6 +931,7 @@ static void Lighting_Command_Step_Down_Off(
current_priority = Present_Value_Priority(pObject);
if (priority <= current_priority) {
/* we have priority - configure the Lighting Command */
Lighting_Command_Trim_Apply(pObject, priority);
lighting_command_step(
&pObject->Lighting_Command, operation, step_increment);
}
@@ -1398,11 +1460,7 @@ Lighting_Command_Stop(struct object_data *pObject, unsigned priority)
}
current_priority = Present_Value_Priority(pObject);
if (priority == current_priority) {
if ((pObject->Lighting_Command.In_Progress ==
BACNET_LIGHTING_FADE_ACTIVE) ||
(pObject->Lighting_Command.In_Progress ==
BACNET_LIGHTING_RAMP_ACTIVE) ||
(pObject->Lighting_Command.Blink.Duration > 0)) {
if (Lighting_Command_In_Progress(pObject)) {
/* fade, ramp, or warn command is currently
executing at the specified priority */
value = pObject->Lighting_Command.Tracking_Value;
@@ -2552,7 +2610,7 @@ float Lighting_Output_High_End_Trim(uint32_t object_instance)
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
value = pObject->Lighting_Command.High_Trim_Value;
value = pObject->High_End_Trim;
}
return value;
@@ -2572,8 +2630,15 @@ bool Lighting_Output_High_End_Trim_Set(uint32_t object_instance, float value)
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
/* High_End_Trim shall always be a positive number
in the normalized range 1.0% to 100.0%. */
if (isgreaterequal(value, 1.0) && islessequal(value, 100.0)) {
pObject->Lighting_Command.High_Trim_Value = value;
pObject->High_End_Trim = value;
Lighting_Command_Trim_Apply(
pObject, Present_Value_Priority(pObject));
if (!Lighting_Command_In_Progress(pObject)) {
lighting_command_refresh(&pObject->Lighting_Command);
}
status = true;
}
}
@@ -2622,7 +2687,7 @@ float Lighting_Output_Low_End_Trim(uint32_t object_instance)
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
value = pObject->Lighting_Command.Low_Trim_Value;
value = pObject->Low_End_Trim;
}
return value;
@@ -2642,8 +2707,15 @@ bool Lighting_Output_Low_End_Trim_Set(uint32_t object_instance, float value)
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
/* Low_End_Trim shall always be a positive number
in the normalized range 1.0% to 100.0%. */
if (isgreaterequal(value, 1.0) && islessequal(value, 100.0)) {
pObject->Lighting_Command.Low_Trim_Value = value;
pObject->Low_End_Trim = value;
Lighting_Command_Trim_Apply(
pObject, Present_Value_Priority(pObject));
if (!Lighting_Command_In_Progress(pObject)) {
lighting_command_refresh(&pObject->Lighting_Command);
}
status = true;
}
}
@@ -2714,7 +2786,14 @@ bool Lighting_Output_Trim_Fade_Time_Set(
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (value <= 86400000UL) {
/* The range of allowable fade-time values
is 0 ms to 86400000 ms (1 day) inclusive. */
pObject->Trim_Fade_Time = value;
Lighting_Command_Trim_Apply(
pObject, Present_Value_Priority(pObject));
if (!Lighting_Command_In_Progress(pObject)) {
lighting_command_refresh(&pObject->Lighting_Command);
}
status = true;
}
}
@@ -2766,9 +2845,7 @@ bool Lighting_Output_Overridden_Set(uint32_t object_instance, float value)
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Lighting_Command.Overridden_Momentary = false;
pObject->Lighting_Command.Overridden = true;
lighting_command_override(&pObject->Lighting_Command, value);
lighting_command_override_set(&pObject->Lighting_Command, value);
status = true;
}
@@ -2791,10 +2868,8 @@ bool Lighting_Output_Overridden_Clear(uint32_t object_instance)
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Lighting_Command.Overridden = false;
pObject->Lighting_Command.Overridden_Momentary = false;
value = Priority_Array_Next_Value(pObject, 0);
lighting_command_override(&pObject->Lighting_Command, value);
lighting_command_override_clear(&pObject->Lighting_Command, value);
status = true;
}
@@ -2818,9 +2893,7 @@ bool Lighting_Output_Overridden_Momentary(uint32_t object_instance, float value)
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
/* set the override */
pObject->Lighting_Command.Overridden_Momentary = true;
pObject->Lighting_Command.Overridden = true;
lighting_command_override(&pObject->Lighting_Command, value);
lighting_command_override_momentary(&pObject->Lighting_Command, value);
status = true;
}
@@ -3787,6 +3860,11 @@ uint32_t Lighting_Output_Create(uint32_t object_instance)
pObject->Last_Lighting_Command.use_fade_time = false;
pObject->Last_Lighting_Command.use_priority = false;
pObject->Blink_Warn_Enable = false;
pObject->High_End_Trim = 100.0f;
pObject->Lighting_Command.High_Trim_Value = pObject->High_End_Trim;
pObject->Low_End_Trim = 1.0f;
pObject->Lighting_Command.Low_Trim_Value = pObject->Low_End_Trim;
pObject->Trim_Fade_Time = 0;
pObject->Egress_Time_Seconds = 0;
pObject->Default_Fade_Time = 100;
pObject->Default_Ramp_Rate = 100.0;
+266 -34
View File
@@ -163,6 +163,32 @@ float lighting_command_step_increment_clamp(float step_increment)
return step_increment;
}
/**
* @brief Clamp the value within the physical min/max range
* @details The physical output level, or non-normalized range,
* is specified as the linearized percentage (0..100%)
* of the possible light output range with 0.0% being off,
* 1.0% being dimmest, and 100.0% being brightest.
* @param value [in] value to clamp within the physical min/max range
* @return value clamped within the physical min/max range of 0.0% to 100.0%
*/
float lighting_command_physical_range_clamp(float value)
{
float physical_value;
/* clamp value within physical values, if non-zero */
if (isless(value, 1.0f)) {
/* jump target to OFF */
physical_value = 0.0f;
} else if (isgreater(value, 100.0f)) {
physical_value = 100.0f;
} else {
physical_value = value;
}
return physical_value;
}
/**
* @brief Calculate the target value for a step down command
* @param tracking_value [in] current tracking value
@@ -201,6 +227,52 @@ float lighting_command_step_up_target_value(
return target_value;
}
/**
* @brief Calculate the target value for a low or high trim fade operation
* @param data - dimmer data structure
* @param value - target value for the trim fade operation
* @param trim_value - target trim value for the fade operation
* @param milliseconds - number of milliseconds elapsed since the last call
* @return calculated target value for the trim fade operation
*/
static float lighting_command_trim_fade(
struct bacnet_lighting_command_data *data,
float value,
float trim_value,
uint32_t milliseconds)
{
float new_value;
float x1, x2, x3, y1, y3;
if (data) {
if (milliseconds > 0) {
/* fading */
if (milliseconds >= data->Trim_Fade_Time) {
/* end of fading */
data->Trim_Fade_Time = 0;
new_value = trim_value;
} else {
x1 = 0.0f;
x2 = (float)milliseconds;
x3 = (float)data->Trim_Fade_Time;
y1 = value;
y3 = trim_value;
new_value = linear_interpolate(x1, x2, x3, y1, y3);
data->Trim_Fade_Time -= milliseconds;
}
} else {
/* no fading */
data->Trim_Fade_Time = 0;
new_value = trim_value;
}
} else {
/* no fading */
new_value = value;
}
return new_value;
}
/**
* @brief Clamp the value within the operating range between low and high
* end trim values.
@@ -212,23 +284,68 @@ float lighting_command_step_up_target_value(
* output while the Present_Value will reflect the original target value.
* @param data - dimmer data structure
* @param value the value that will be subject to clamping
* @param milliseconds - number of milliseconds elapsed since the last call
* @return value clamped within the operating range defined by the High_End_Trim
* and Low_End_Trim property values
*/
float lighting_command_operating_range_clamp_fade(
struct bacnet_lighting_command_data *data,
float value,
uint16_t milliseconds)
{
float high_trim, low_trim, swap_value;
if (data) {
/* clamp range within physical limits */
high_trim =
lighting_command_physical_range_clamp(data->High_Trim_Value);
low_trim = lighting_command_physical_range_clamp(data->Low_Trim_Value);
/* valid range check for high and low trim values */
if (isgreater(low_trim, high_trim)) {
/* swap the trims if they are inverse */
swap_value = low_trim;
low_trim = high_trim;
high_trim = swap_value;
}
/* clamp value within trim values, if non-zero */
if (isless(value, 1.0f)) {
/* jump target to OFF if below normalized min */
value = 0.0f;
} else if (isgreater(value, high_trim)) {
value = lighting_command_trim_fade(
data, value, high_trim, milliseconds);
data->In_Progress = BACNET_LIGHTING_TRIM_ACTIVE;
} else if (isless(value, low_trim)) {
value =
lighting_command_trim_fade(data, value, low_trim, milliseconds);
data->In_Progress = BACNET_LIGHTING_TRIM_ACTIVE;
}
} else {
/* no data, so just clamp value within physical limits */
value = lighting_command_physical_range_clamp(value);
}
return value;
}
/**
* @brief Clamp the value within the operating range between low and high
* end trim values immediately.
* @details The Operating Range is a subset of the Normalized Range,
* that represents the range of acceptable values for control of the object.
* The Operating Range is defined by the High_End_Trim and Low_End_Trim
* property values. When values are written outside of the Operating Range,
* the Tracking_Value will reflect the actual, clamped normalized light
* output while the Present_Value will reflect the original target value.
* @param data - dimmer data structure
* @param value the value that will be subject to clamping
* @return value clamped within the operating range defined by the High_End_Trim
* and Low_End_Trim property values
*/
float lighting_command_operating_range_clamp(
struct bacnet_lighting_command_data *data, float value)
{
/* clamp value within trim values, if non-zero */
if (isless(value, 1.0f)) {
/* jump target to OFF if below normalized min */
value = 0.0f;
} else if (isgreater(value, data->High_Trim_Value)) {
value = data->High_Trim_Value;
data->In_Progress = BACNET_LIGHTING_TRIM_ACTIVE;
} else if (isless(value, data->Low_Trim_Value)) {
value = data->Low_Trim_Value;
data->In_Progress = BACNET_LIGHTING_TRIM_ACTIVE;
}
return value;
return lighting_command_operating_range_clamp_fade(data, value, 0);
}
/**
@@ -250,11 +367,23 @@ float lighting_command_operating_range_clamp(
float lighting_command_normalized_on_range_clamp(
struct bacnet_lighting_command_data *data, float value)
{
float min_value, max_value, swap_value;
/* clamp range within physical limits */
max_value = lighting_command_physical_range_clamp(data->Max_Actual_Value);
min_value = lighting_command_physical_range_clamp(data->Min_Actual_Value);
/* valid range check for high and low trim values */
if (isgreater(min_value, max_value)) {
/* swap the trims if they are inverse */
swap_value = min_value;
min_value = max_value;
max_value = swap_value;
}
/* clamp value within trim values, if non-zero */
if (isgreater(value, data->Max_Actual_Value)) {
value = data->Max_Actual_Value;
} else if (isless(value, data->Min_Actual_Value)) {
value = data->Min_Actual_Value;
if (isgreater(value, max_value)) {
value = max_value;
} else if (isless(value, min_value)) {
value = min_value;
}
return value;
@@ -280,15 +409,26 @@ float lighting_command_normalized_range_clamp(
struct bacnet_lighting_command_data *data, float value)
{
float normalized_value;
float min_value, max_value, swap_value;
/* clamp range within physical limits */
max_value = lighting_command_physical_range_clamp(data->Max_Actual_Value);
min_value = lighting_command_physical_range_clamp(data->Min_Actual_Value);
/* valid range check for high and low trim values */
if (isgreater(min_value, max_value)) {
/* swap the trims if they are inverse */
swap_value = min_value;
min_value = max_value;
max_value = swap_value;
}
/* clamp value within normalized values, if non-zero */
if (isless(value, 1.0f)) {
/* jump target to OFF if below normalized min */
normalized_value = 0.0f;
} else if (isgreater(value, data->Max_Actual_Value)) {
normalized_value = data->Max_Actual_Value;
} else if (isless(value, data->Min_Actual_Value)) {
normalized_value = data->Min_Actual_Value;
} else if (isgreater(value, max_value)) {
normalized_value = max_value;
} else if (isless(value, min_value)) {
normalized_value = min_value;
} else {
normalized_value = value;
}
@@ -301,19 +441,18 @@ float lighting_command_normalized_range_clamp(
* @param data - dimmer data structure
* @param old_value - value prior to write
* @param value - value of the write
* @param milliseconds - number of milliseconds elapsed
*/
static void lighting_command_tracking_value_event(
struct bacnet_lighting_command_data *data, float old_value, float value)
{
if (data->Overridden) {
value = lighting_command_operating_range_clamp(data, value);
lighting_command_tracking_value_notify(data, old_value, value);
if (data->Overridden_Momentary) {
data->Overridden = false;
}
} else if (!data->Out_Of_Service) {
data->Overridden_Momentary = false;
value = lighting_command_operating_range_clamp(data, value);
lighting_command_tracking_value_notify(data, old_value, value);
} else {
debug_printf(
@@ -336,6 +475,7 @@ static void lighting_command_fade_handler(
float target_value;
old_value = data->Tracking_Value;
/* clamp Tracking value within the Normalized ON Range */
target_value =
lighting_command_normalized_on_range_clamp(data, data->Target_Level);
if ((milliseconds >= data->Fade_Time) ||
@@ -365,6 +505,10 @@ static void lighting_command_fade_handler(
data->Fade_Time -= milliseconds;
data->In_Progress = BACNET_LIGHTING_FADE_ACTIVE;
}
/* clamp Tracking Value inclusively within the Operating Range */
data->Tracking_Value = lighting_command_operating_range_clamp_fade(
data, data->Tracking_Value, milliseconds);
/* notify */
lighting_command_tracking_value_event(
data, old_value, data->Tracking_Value);
}
@@ -387,9 +531,11 @@ static void lighting_command_fade_handler(
static void lighting_command_ramp_handler(
struct bacnet_lighting_command_data *data, uint16_t milliseconds)
{
float old_value, target_value, step_value, steps, ramp_rate;
float old_value, target_value, step_value, steps, ramp_rate,
operating_value;
old_value = data->Tracking_Value;
/* clamp Tracking value within the Normalized ON Range */
target_value =
lighting_command_normalized_on_range_clamp(data, data->Target_Level);
if (!islessgreater(data->Tracking_Value, target_value)) {
@@ -449,6 +595,11 @@ static void lighting_command_ramp_handler(
data->In_Progress = BACNET_LIGHTING_RAMP_ACTIVE;
}
}
/* clamp Tracking_Value inclusively within the Operating Range */
operating_value = lighting_command_operating_range_clamp_fade(
data, data->Tracking_Value, milliseconds);
data->Tracking_Value = operating_value;
/* notify */
lighting_command_tracking_value_event(
data, old_value, data->Tracking_Value);
}
@@ -465,17 +616,23 @@ static void lighting_command_ramp_handler(
static void
lighting_command_step_up_handler(struct bacnet_lighting_command_data *data)
{
float old_value, target_value;
float old_value, target_value, operating_value;
old_value = data->Tracking_Value;
if (isgreaterequal(old_value, data->Min_Actual_Value)) {
/* inhibit ON if the value is already OFF */
target_value = lighting_command_step_up_target_value(
data->Tracking_Value, data->Step_Increment);
/* clamp Tracking value within the Normalized ON Range */
data->Tracking_Value =
lighting_command_normalized_on_range_clamp(data, target_value);
data->In_Progress = BACNET_LIGHTING_IDLE;
data->Lighting_Operation = BACNET_LIGHTS_STOP;
/* clamp Tracking value inclusively within the Operating Range */
operating_value =
lighting_command_operating_range_clamp(data, data->Tracking_Value);
data->Tracking_Value = operating_value;
/* notify */
lighting_command_tracking_value_event(
data, old_value, data->Tracking_Value);
}
@@ -493,15 +650,21 @@ lighting_command_step_up_handler(struct bacnet_lighting_command_data *data)
static void
lighting_command_step_down_handler(struct bacnet_lighting_command_data *data)
{
float old_value, target_value;
float old_value, target_value, operating_value;
old_value = data->Tracking_Value;
target_value = lighting_command_step_down_target_value(
data->Tracking_Value, data->Step_Increment);
/* clamp Tracking value within the Normalized ON Range */
data->Tracking_Value =
lighting_command_normalized_on_range_clamp(data, target_value);
data->In_Progress = BACNET_LIGHTING_IDLE;
data->Lighting_Operation = BACNET_LIGHTS_STOP;
/* clamp Tracking value inclusively within the Operating Range */
operating_value =
lighting_command_operating_range_clamp(data, data->Tracking_Value);
data->Tracking_Value = operating_value;
/* notify */
lighting_command_tracking_value_event(
data, old_value, data->Tracking_Value);
}
@@ -518,7 +681,7 @@ lighting_command_step_down_handler(struct bacnet_lighting_command_data *data)
static void
lighting_command_step_on_handler(struct bacnet_lighting_command_data *data)
{
float old_value, target_value;
float old_value, target_value, operating_value;
old_value = data->Tracking_Value;
target_value = lighting_command_step_up_target_value(
@@ -527,6 +690,11 @@ lighting_command_step_on_handler(struct bacnet_lighting_command_data *data)
lighting_command_normalized_range_clamp(data, target_value);
data->In_Progress = BACNET_LIGHTING_IDLE;
data->Lighting_Operation = BACNET_LIGHTS_STOP;
/* clamp Tracking value inclusively within the Operating Range */
operating_value =
lighting_command_operating_range_clamp(data, data->Tracking_Value);
data->Tracking_Value = operating_value;
/* notify */
lighting_command_tracking_value_event(
data, old_value, data->Tracking_Value);
}
@@ -543,7 +711,7 @@ lighting_command_step_on_handler(struct bacnet_lighting_command_data *data)
static void
lighting_command_step_off_handler(struct bacnet_lighting_command_data *data)
{
float old_value, target_value;
float old_value, target_value, operating_value;
old_value = data->Tracking_Value;
target_value = lighting_command_step_down_target_value(
@@ -552,6 +720,11 @@ lighting_command_step_off_handler(struct bacnet_lighting_command_data *data)
lighting_command_normalized_range_clamp(data, target_value);
data->In_Progress = BACNET_LIGHTING_IDLE;
data->Lighting_Operation = BACNET_LIGHTS_STOP;
/* clamp Tracking value inclusively within the Operating Range */
operating_value =
lighting_command_operating_range_clamp(data, data->Tracking_Value);
data->Tracking_Value = operating_value;
/* notify */
lighting_command_tracking_value_event(
data, old_value, data->Tracking_Value);
}
@@ -583,7 +756,7 @@ lighting_command_step_off_handler(struct bacnet_lighting_command_data *data)
static void lighting_command_blink_handler(
struct bacnet_lighting_command_data *data, uint16_t milliseconds)
{
float old_value, target_value;
float old_value, target_value, operating_value;
old_value = data->Tracking_Value;
/* detect 'end' operation */
@@ -635,16 +808,19 @@ static void lighting_command_blink_handler(
}
}
target_value = lighting_command_normalized_range_clamp(data, target_value);
/* clamp Tracking value inclusively within the Operating Range */
operating_value =
lighting_command_operating_range_clamp(data, target_value);
/* note: The blink-warn notifications shall not be reflected
in the tracking value. */
if (data->In_Progress == BACNET_LIGHTING_IDLE) {
data->Tracking_Value = target_value;
data->Tracking_Value = operating_value;
}
lighting_command_tracking_value_event(data, old_value, target_value);
lighting_command_tracking_value_event(data, old_value, operating_value);
}
/**
* @brief Overrides the current lighting command if overridden is true
* @brief Overrides the current lighting command with the provided value
* @param data [in] dimmer data
*/
void lighting_command_override(
@@ -656,8 +832,63 @@ void lighting_command_override(
return;
}
old_value = data->Tracking_Value;
data->Tracking_Value = value;
lighting_command_tracking_value_event(data, old_value, value);
data->Tracking_Value = lighting_command_physical_range_clamp(value);
lighting_command_tracking_value_event(
data, old_value, data->Tracking_Value);
}
/**
* @brief Overrides the current lighting command with the provided value
* @param data [in] dimmer data
*/
void lighting_command_override_set(
struct bacnet_lighting_command_data *data, float value)
{
if (!data) {
return;
}
data->Overridden = true;
data->Overridden_Momentary = false;
lighting_command_override(data, value);
}
/**
* @brief Clears the override of the current lighting command
* @param data [in] dimmer data
*/
void lighting_command_override_clear(
struct bacnet_lighting_command_data *data, float value)
{
float old_value, normalized_value, operating_value;
if (!data) {
return;
}
data->Overridden = false;
data->Overridden_Momentary = false;
old_value = data->Tracking_Value;
/* clamp Tracking value within the Normalized Range */
normalized_value = lighting_command_normalized_range_clamp(data, value);
/* clamp Tracking value inclusively within the Operating Range */
operating_value =
lighting_command_operating_range_clamp(data, normalized_value);
data->Tracking_Value = operating_value;
lighting_command_tracking_value_event(data, old_value, operating_value);
}
/**
* @brief Overrides the current lighting command with the provided value
* @param data [in] dimmer data
*/
void lighting_command_override_momentary(
struct bacnet_lighting_command_data *data, float value)
{
if (!data) {
return;
}
data->Overridden = true;
data->Overridden_Momentary = true;
lighting_command_override(data, value);
}
/**
@@ -1009,6 +1240,7 @@ void lighting_command_init(struct bacnet_lighting_command_data *data)
data->Max_Actual_Value = 100.0f;
data->Low_Trim_Value = 1.0f;
data->High_Trim_Value = 100.0f;
data->Trim_Fade_Time = 0;
data->Last_On_Value = 100.0f;
data->Default_On_Value = 100.0f;
data->Overridden = false;
+17
View File
@@ -79,6 +79,7 @@ typedef struct bacnet_lighting_command_data {
float Max_Actual_Value;
float High_Trim_Value;
float Low_Trim_Value;
uint32_t Trim_Fade_Time;
float Default_On_Value;
float Last_On_Value;
BACNET_LIGHTING_COMMAND_WARN_DATA Blink;
@@ -135,6 +136,15 @@ void lighting_command_toggle_default(
BACNET_STACK_EXPORT
void lighting_command_override(
struct bacnet_lighting_command_data *data, float value);
BACNET_STACK_EXPORT
void lighting_command_override_set(
struct bacnet_lighting_command_data *data, float value);
BACNET_STACK_EXPORT
void lighting_command_override_clear(
struct bacnet_lighting_command_data *data, float value);
BACNET_STACK_EXPORT
void lighting_command_override_momentary(
struct bacnet_lighting_command_data *data, float value);
BACNET_STACK_EXPORT
float lighting_command_ramp_rate_clamp(float ramp_rate);
@@ -144,11 +154,18 @@ BACNET_STACK_EXPORT
float lighting_command_operating_range_clamp(
struct bacnet_lighting_command_data *data, float value);
BACNET_STACK_EXPORT
float lighting_command_operating_range_clamp_fade(
struct bacnet_lighting_command_data *data,
float value,
uint16_t milliseconds);
BACNET_STACK_EXPORT
float lighting_command_normalized_range_clamp(
struct bacnet_lighting_command_data *data, float value);
BACNET_STACK_EXPORT
float lighting_command_normalized_on_range_clamp(
struct bacnet_lighting_command_data *data, float value);
BACNET_STACK_EXPORT
float lighting_command_physical_range_clamp(float value);
BACNET_STACK_EXPORT
void lighting_command_refresh(struct bacnet_lighting_command_data *data);
+106
View File
@@ -402,6 +402,112 @@ static void testLightingOutput(void)
test_real = Lighting_Output_Tracking_Value(instance);
zassert_true(is_float_equal(test_real, real_value), NULL);
zassert_true(is_float_equal(Test_Tracking_Value, real_value), NULL);
/* high-end-trim, low-end-trim, and trim-fade-time */
Lighting_Output_Present_Value_Relinquish_All(instance);
status = Lighting_Output_Transition_Set(
instance, BACNET_LIGHTING_TRANSITION_NONE);
zassert_true(status, NULL);
status = Lighting_Output_Trim_Fade_Time_Set(instance, 0);
zassert_true(status, NULL);
status = Lighting_Output_High_End_Trim_Set(instance, 90.0f);
zassert_true(status, NULL);
test_real = Lighting_Output_High_End_Trim(instance);
zassert_true(is_float_equal(test_real, 90.0f), NULL);
status = Lighting_Output_Low_End_Trim_Set(instance, 10.0f);
zassert_true(status, NULL);
test_real = Lighting_Output_Low_End_Trim(instance);
zassert_true(is_float_equal(test_real, 10.0f), NULL);
priority = 8;
status = Lighting_Output_Present_Value_Set(instance, 100.0f, priority);
zassert_true(status, NULL);
Lighting_Output_Timer(instance, 10);
in_progress = Lighting_Output_In_Progress(instance);
zassert_equal(
in_progress, BACNET_LIGHTING_TRIM_ACTIVE, "in_progress=%s",
bactext_lighting_in_progress(in_progress));
test_real = Lighting_Output_Tracking_Value(instance);
zassert_true(is_float_equal(test_real, 90.0f), NULL);
Lighting_Output_Timer(instance, 10);
in_progress = Lighting_Output_In_Progress(instance);
zassert_equal(
in_progress, BACNET_LIGHTING_IDLE, "in_progress=%s",
bactext_lighting_in_progress(in_progress));
status = Lighting_Output_Present_Value_Set(instance, 1.0f, priority);
zassert_true(status, NULL);
Lighting_Output_Timer(instance, 10);
in_progress = Lighting_Output_In_Progress(instance);
zassert_equal(
in_progress, BACNET_LIGHTING_TRIM_ACTIVE, "in_progress=%s",
bactext_lighting_in_progress(in_progress));
test_real = Lighting_Output_Tracking_Value(instance);
zassert_true(is_float_equal(test_real, 10.0f), "tracking=%f", test_real);
Lighting_Output_Timer(instance, 10);
in_progress = Lighting_Output_In_Progress(instance);
zassert_equal(
in_progress, BACNET_LIGHTING_IDLE, "in_progress=%s",
bactext_lighting_in_progress(in_progress));
Lighting_Output_Present_Value_Relinquish_All(instance);
status = Lighting_Output_Present_Value_Set(instance, 100.0f, 1);
zassert_true(status, NULL);
Lighting_Output_Timer(instance, 10);
in_progress = Lighting_Output_In_Progress(instance);
zassert_equal(
in_progress, BACNET_LIGHTING_IDLE, "in_progress=%s",
bactext_lighting_in_progress(in_progress));
test_real = Lighting_Output_Tracking_Value(instance);
zassert_true(is_float_equal(test_real, 100.0f), "tracking=%f", test_real);
Lighting_Output_Present_Value_Relinquish_All(instance);
status = Lighting_Output_Present_Value_Set(instance, 80.0f, priority);
zassert_true(status, NULL);
Lighting_Output_Timer(instance, 10);
test_real = Lighting_Output_Tracking_Value(instance);
zassert_true(is_float_equal(test_real, 80.0f), NULL);
unsigned_value = 1000;
status = Lighting_Output_Trim_Fade_Time_Set(instance, unsigned_value);
zassert_true(status, NULL);
test_unsigned = Lighting_Output_Trim_Fade_Time(instance);
zassert_equal(test_unsigned, unsigned_value, NULL);
status = Lighting_Output_Default_Fade_Time_Set(instance, 2000);
zassert_true(status, NULL);
status = Lighting_Output_Transition_Set(
instance, BACNET_LIGHTING_TRANSITION_FADE);
zassert_true(status, NULL);
status = Lighting_Output_Present_Value_Set(instance, 100.0f, priority);
zassert_true(status, NULL);
milliseconds = 500;
Lighting_Output_Timer(instance, milliseconds);
in_progress = Lighting_Output_In_Progress(instance);
zassert_equal(
in_progress, BACNET_LIGHTING_FADE_ACTIVE, "in_progress=%s",
bactext_lighting_in_progress(in_progress));
test_real = Lighting_Output_Tracking_Value(instance);
zassert_true(is_float_equal(test_real, 85.0f), NULL);
Lighting_Output_Timer(instance, milliseconds);
in_progress = Lighting_Output_In_Progress(instance);
zassert_equal(
in_progress, BACNET_LIGHTING_FADE_ACTIVE, "in_progress=%s",
bactext_lighting_in_progress(in_progress));
test_real = Lighting_Output_Tracking_Value(instance);
zassert_true(is_float_equal(test_real, 90.0f), NULL);
Lighting_Output_Timer(instance, milliseconds);
in_progress = Lighting_Output_In_Progress(instance);
zassert_equal(
in_progress, BACNET_LIGHTING_TRIM_ACTIVE, "in_progress=%s",
bactext_lighting_in_progress(in_progress));
test_real = Lighting_Output_Tracking_Value(instance);
zassert_true(is_float_equal(test_real, 92.5f), NULL);
Lighting_Output_Timer(instance, milliseconds);
in_progress = Lighting_Output_In_Progress(instance);
zassert_equal(
in_progress, BACNET_LIGHTING_TRIM_ACTIVE, "in_progress=%s",
bactext_lighting_in_progress(in_progress));
test_real = Lighting_Output_Tracking_Value(instance);
zassert_true(is_float_equal(test_real, 90.0f), NULL);
Lighting_Output_Timer(instance, milliseconds);
in_progress = Lighting_Output_In_Progress(instance);
zassert_equal(
in_progress, BACNET_LIGHTING_IDLE, "in_progress=%s",
bactext_lighting_in_progress(in_progress));
/* feedback value */
status = Lighting_Output_Feedback_Value_Set(instance, 55.5f);
zassert_true(status, NULL);
@@ -267,7 +267,9 @@ static void test_lighting_command_unit(void)
zassert_true(is_float_equal(data.Last_On_Value, 1.0f), NULL);
lighting_command_timer(&data, milliseconds);
zassert_true(data.In_Progress == BACNET_LIGHTING_TRIM_ACTIVE, NULL);
zassert_true(is_float_equal(Tracking_Value, data.Low_Trim_Value), NULL);
zassert_true(
is_float_equal(Tracking_Value, data.Low_Trim_Value),
"Tracking_Value=%f Low_Trim=%f", Tracking_Value, data.Low_Trim_Value);
target_level = 0.0f;
milliseconds = 10;
lighting_command_fade_to(&data, target_level, 0);
@@ -287,13 +289,60 @@ static void test_lighting_command_unit(void)
zassert_true(is_float_equal(Tracking_Value, data.High_Trim_Value), NULL);
data.High_Trim_Value = data.Max_Actual_Value;
zassert_true(is_float_equal(data.Last_On_Value, target_level), NULL);
/* trim fade time */
target_level = 80.0f;
milliseconds = 10;
lighting_command_fade_to(&data, target_level, 0);
lighting_command_timer(&data, milliseconds);
zassert_true(is_float_equal(Tracking_Value, target_level), NULL);
data.High_Trim_Value = 90.0f;
data.Trim_Fade_Time = fade_time;
lighting_command_fade_to(&data, 100.0f, fade_time * 2);
milliseconds = fade_time / 4;
lighting_command_timer(&data, milliseconds);
zassert_true(data.In_Progress == BACNET_LIGHTING_FADE_ACTIVE, NULL);
zassert_true(is_float_equal(Tracking_Value, 82.5f), NULL);
zassert_equal(data.Trim_Fade_Time, fade_time, NULL);
lighting_command_timer(&data, milliseconds);
zassert_true(data.In_Progress == BACNET_LIGHTING_FADE_ACTIVE, NULL);
zassert_true(is_float_equal(Tracking_Value, 85.0f), NULL);
zassert_equal(data.Trim_Fade_Time, fade_time, NULL);
lighting_command_timer(&data, milliseconds);
zassert_true(data.In_Progress == BACNET_LIGHTING_FADE_ACTIVE, NULL);
zassert_true(is_float_equal(Tracking_Value, 87.5f), NULL);
zassert_equal(data.Trim_Fade_Time, fade_time, NULL);
lighting_command_timer(&data, milliseconds);
zassert_true(data.In_Progress == BACNET_LIGHTING_FADE_ACTIVE, NULL);
zassert_true(is_float_equal(Tracking_Value, 90.0f), NULL);
zassert_equal(data.Trim_Fade_Time, fade_time, NULL);
lighting_command_timer(&data, milliseconds);
zassert_true(data.In_Progress == BACNET_LIGHTING_TRIM_ACTIVE, NULL);
zassert_true(is_float_equal(Tracking_Value, 91.875f), NULL);
zassert_equal(data.Trim_Fade_Time, 750, NULL);
lighting_command_timer(&data, milliseconds);
zassert_true(data.In_Progress == BACNET_LIGHTING_TRIM_ACTIVE, NULL);
zassert_true(
is_float_equal(Tracking_Value, 93.056f), "Tracking_Value=%f",
Tracking_Value);
zassert_equal(data.Trim_Fade_Time, 500, NULL);
lighting_command_timer(&data, milliseconds);
zassert_true(data.In_Progress == BACNET_LIGHTING_TRIM_ACTIVE, NULL);
zassert_true(
is_float_equal(Tracking_Value, 93.264f), "Tracking_Value=%f",
Tracking_Value);
zassert_equal(data.Trim_Fade_Time, 250, NULL);
lighting_command_timer(&data, milliseconds);
zassert_true(data.In_Progress == BACNET_LIGHTING_TRIM_ACTIVE, NULL);
zassert_true(is_float_equal(Tracking_Value, data.High_Trim_Value), NULL);
zassert_equal(data.Trim_Fade_Time, 0, NULL);
lighting_command_timer(&data, milliseconds);
zassert_true(data.In_Progress == BACNET_LIGHTING_IDLE, NULL);
data.High_Trim_Value = data.Max_Actual_Value;
/* override */
override_level = 42.0f;
target_level = 100.0f;
milliseconds = 10;
data.Overridden = true;
data.Overridden_Momentary = false;
lighting_command_override(&data, override_level);
lighting_command_override_set(&data, override_level);
lighting_command_timer(&data, milliseconds);
zassert_true(is_float_equal(Tracking_Value, override_level), NULL);
zassert_true(data.In_Progress == BACNET_LIGHTING_IDLE, NULL);
@@ -301,8 +350,7 @@ static void test_lighting_command_unit(void)
lighting_command_timer(&data, milliseconds);
zassert_true(is_float_equal(Tracking_Value, override_level), NULL);
zassert_true(data.In_Progress == BACNET_LIGHTING_IDLE, NULL);
data.Overridden = false;
lighting_command_override(&data, target_level);
lighting_command_override_clear(&data, target_level);
lighting_command_timer(&data, milliseconds);
zassert_true(is_float_equal(Tracking_Value, target_level), NULL);
zassert_true(data.In_Progress == BACNET_LIGHTING_IDLE, NULL);
@@ -310,9 +358,7 @@ static void test_lighting_command_unit(void)
override_level = 42.0f;
target_level = 100.0f;
milliseconds = 10;
data.Overridden = true;
data.Overridden_Momentary = true;
lighting_command_override(&data, override_level);
lighting_command_override_momentary(&data, override_level);
lighting_command_timer(&data, milliseconds);
zassert_true(is_float_equal(Tracking_Value, override_level), NULL);
zassert_true(data.In_Progress == BACNET_LIGHTING_IDLE, NULL);
@@ -331,6 +377,24 @@ static void test_lighting_command_unit(void)
target_step = lighting_command_step_increment_clamp(100.1f);
zassert_true(is_float_equal(target_step, 100.0f), NULL);
/* physical range clamping */
target_level = lighting_command_physical_range_clamp(0.0f);
zassert_true(is_float_equal(target_level, 0.0f), NULL);
target_level = lighting_command_physical_range_clamp(0.5f);
zassert_true(is_float_equal(target_level, 0.0f), NULL);
target_level = lighting_command_physical_range_clamp(0.9f);
zassert_true(is_float_equal(target_level, 0.0f), NULL);
target_level = lighting_command_physical_range_clamp(1.0f);
zassert_true(is_float_equal(target_level, 1.0f), NULL);
target_level = lighting_command_physical_range_clamp(50.0f);
zassert_true(is_float_equal(target_level, 50.0f), NULL);
target_level = lighting_command_physical_range_clamp(100.0f);
zassert_true(is_float_equal(target_level, 100.0f), NULL);
target_level = lighting_command_physical_range_clamp(100.1f);
zassert_true(is_float_equal(target_level, 100.0f), NULL);
target_level = lighting_command_physical_range_clamp(150.0f);
zassert_true(is_float_equal(target_level, 100.0f), NULL);
/* step UP - inhibit ON */
target_step = 1.0f;
target_level = 0.0f;
@@ -602,7 +666,9 @@ static void test_lighting_command_unit(void)
zassert_equal(data.Lighting_Operation, BACNET_LIGHTS_PROPRIETARY_MAX, NULL);
/* null check code coverage */
lighting_command_override(NULL, override_level);
lighting_command_override_set(NULL, override_level);
lighting_command_override_clear(NULL, override_level);
lighting_command_override_momentary(NULL, override_level);
lighting_command_fade_to(NULL, 0.0f, 0);
lighting_command_ramp_to(NULL, 0.0f, 0.0f);
lighting_command_step(NULL, BACNET_LIGHTS_STEP_OFF, 0.0f);