diff --git a/src/bacnet/basic/sys/lighting_command.c b/src/bacnet/basic/sys/lighting_command.c index ba4a9249..b3034fa6 100644 --- a/src/bacnet/basic/sys/lighting_command.c +++ b/src/bacnet/basic/sys/lighting_command.c @@ -16,12 +16,142 @@ #include "lighting_command.h" /** - * @brief call the lighting command tracking value callbacks - * @param data - dimmer data structure - * @param old_value - value prior to write - * @param value - value of the write + * @brief compare two floating point values to 3 decimal places + * + * @param x1 - first comparison value + * @param x2 - second comparison value + * @return true if the value is the same to 3 decimal points */ -static void lighting_command_tracking_value_notify( +static bool is_float_equal(float x1, float x2) +{ + return fabs(x1 - x2) < 0.001; +} + +/** + * @brief Clamp the value within the normalized range + * @details The normalized output level 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 normalized range + * @return value clamped within the normalized range of 0.0% to 100.0% + */ +float lighting_command_normalized_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 Clamp the value within the normal ON range + * @details The normal ON output level is specified as the linearized + * percentage (1..100%) of the possible light output range with 1.0% being + * dimmest, and 100.0% being brightest. + * @param value [in] value to clamp within the normalized ON range + * @return value clamped within the normalized ON range of 1.0% to 100.0% + */ +float lighting_command_normalized_on_range_clamp(float value) +{ + float normalized_on_value; + + /* clamp value within 1.0 and 100.0 values */ + if (isless(value, 1.0f)) { + normalized_on_value = 1.0f; + } else if (isgreater(value, 100.0f)) { + normalized_on_value = 100.0f; + } else { + normalized_on_value = value; + } + + return normalized_on_value; +} + +/** + * @brief Adjust Min/Max Actual Value Range + * @details Min_Actual_Value property, of type Real, shall specify + * the physical output level that corresponds to a Present_Value of 1.0%. + * Changing Min_Actual_Value to a value greater than Max_Actual_Value + * shall force Max_Actual_Value to become equal to Min_Actual_Value. + * Min_Actual_Value shall always be a positive number in the + * range 1.0% to 100.0%. + * + * Max_Actual_Value property, of type Real, shall specify the physical + * output level that corresponds to a Present_Value of 100.0%. + * Changing Max_Actual_Value to a value less than Min_Actual_Value + * shall force Min_Actual_Value to become equal to Max_Actual_Value. + * Max_Actual_Value shall always be a positive number in the range + * 1.0% to 100.0%. + */ +static void lighting_command_min_max_value_range_adjust( + struct bacnet_lighting_command_data *data) +{ + float swap_value, min_value, max_value; + + min_value = + lighting_command_normalized_on_range_clamp(data->Min_Actual_Value); + max_value = + lighting_command_normalized_on_range_clamp(data->Max_Actual_Value); + if (isgreater(min_value, max_value)) { + /* swap the configured high and low actual values if they are inverse */ + swap_value = min_value; + min_value = max_value; + max_value = swap_value; + } + data->Min_Actual_Value = min_value; + data->Max_Actual_Value = max_value; +} + +/** + * @brief Calculate the Feedback_Value property value + * + * This property, of type Real, shall indicate the actual value + * of the physical lighting output within the normalized range. + * If the actual value of the physical lighting output in the + * non-normalized range is not off but is less than the + * Min_Actual_Value, then Feedback_Value shall be set to 1.0%. + * If the actual value in the non-normalized range is greater than + * Max_Actual_Value, then Feedback_Value shall be set to 100.0%. + * @param data - dimmer data structure + * @return calculated feedback-value + */ +float lighting_command_normalized_to_physical_value( + float min_value, float max_value, float normalized_value) +{ + float physical_value, x1, x2, x3, y1, y3; + + if (isless(normalized_value, 1.0f)) { + physical_value = 0.0f; + } else if (isgreater(normalized_value, 100.0f)) { + physical_value = max_value; + } else { + x1 = 1.0f; + x2 = normalized_value; + x3 = 100.0f; + y1 = min_value; + y3 = max_value; + physical_value = linear_interpolate(x1, x2, x3, y1, y3); + } + + return physical_value; +} + +/** + * @brief call the lighting command notification callbacks + * @param data - dimmer data structure + * @param old_value - physical value prior to write + * @param value - physical value of the write + */ +static void lighting_command_notify( struct bacnet_lighting_command_data *data, float old_value, float value) { struct lighting_command_notification *head; @@ -175,32 +305,6 @@ 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 @@ -305,36 +409,23 @@ static float lighting_command_operating_range_clamp_fade_nolock( 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)) { + } else if (isgreater(value, data->High_Trim_Value)) { value = lighting_command_trim_fade( - data, value, high_trim, milliseconds); + data, value, data->High_Trim_Value, milliseconds); data->In_Progress = BACNET_LIGHTING_TRIM_ACTIVE; - } else if (isless(value, low_trim)) { - value = - lighting_command_trim_fade(data, value, low_trim, milliseconds); + } else if (isless(value, data->Low_Trim_Value)) { + value = lighting_command_trim_fade( + data, value, data->Low_Trim_Value, 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); + value = lighting_command_normalized_range_clamp(value); } return value; @@ -358,195 +449,6 @@ float lighting_command_operating_range_clamp_fade( return clamped_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 - */ -static float lighting_command_operating_range_clamp_nolock( - struct bacnet_lighting_command_data *data, float value) -{ - return lighting_command_operating_range_clamp_fade_nolock(data, value, 0); -} - -float lighting_command_operating_range_clamp( - struct bacnet_lighting_command_data *data, float value) -{ - float clamped_value; - - if (!data) { - return value; - } - lighting_command_lock(data); - clamped_value = lighting_command_operating_range_clamp_nolock(data, value); - lighting_command_unlock(data); - - return clamped_value; -} - -/** - * @brief Determine if the values are both within the normalized OFF 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. - * The actual range represents the subset of physical output levels - * defined by Min_Actual_Value and Max_Actual_Value - * (or 1.0 to 100.0% if these properties are not present). - * The normalized range is always 0.0 to 100.0% where - * 1.0% = bottom of the actual range and 100.0% = top of the actual range. - * @param data - dimmer data structure - * @param value1 [in] value to check if it is within the normalized OFF range - * @param value2 [in] value to check if it is within the normalized OFF range - * @return true if both values are within the normalized OFF range, false - * otherwise - */ -static bool lighting_command_is_normalized_off_to_off_nolock( - struct bacnet_lighting_command_data *data, float value1, float value2) -{ - float min_value, max_value; - - /* check normalized 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); - if (isgreater(min_value, max_value)) { - /* the high and low configured actual values are swapped */ - min_value = max_value; - } - if (isless(value1, min_value) && isless(value2, min_value)) { - return true; - } - - return false; -} - -/** - * @brief Clamp the value within the normalized ON range 1% to 100%. - * @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. - * The actual range represents the subset of physical output levels - * defined by Min_Actual_Value and Max_Actual_Value - * (or 1.0 to 100.0% if these properties are not present). - * The normalized range is always 0.0 to 100.0% where - * 1.0% = bottom of the actual range and 100.0% = top of the actual range. - * @param data - dimmer data structure - * @param value [in] value to normalize - * @return normalized value within the range defined by Min_Actual_Value - * and Max_Actual_Value - */ -static float lighting_command_normalized_on_range_clamp_nolock( - 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); - if (isgreater(min_value, max_value)) { - /* swap the configured high and low actual values if they are inverse */ - swap_value = min_value; - min_value = max_value; - max_value = swap_value; - } - /* clamp value within actual min/max values */ - if (isgreater(value, max_value)) { - value = max_value; - } else if (isless(value, min_value)) { - value = min_value; - } - - return value; -} - -float lighting_command_normalized_on_range_clamp( - struct bacnet_lighting_command_data *data, float value) -{ - float clamped_value; - - if (!data) { - return value; - } - lighting_command_lock(data); - clamped_value = - lighting_command_normalized_on_range_clamp_nolock(data, value); - lighting_command_unlock(data); - - return clamped_value; -} - -/** - * @brief Normalize the value to the 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. - * The actual range represents the subset of physical output levels - * defined by Min_Actual_Value and Max_Actual_Value - * (or 1.0 to 100.0% if these properties are not present). - * The normalized range is always 0.0 to 100.0% where - * 1.0% = bottom of the actual range and 100.0% = top of the actual range. - * @param data - dimmer data structure - * @param value [in] value to normalize - * @return normalized value within the range defined by - * 0.0%, Min_Actual_Value, and Max_Actual_Value - */ -static float lighting_command_normalized_range_clamp_nolock( - 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, max_value)) { - normalized_value = max_value; - } else if (isless(value, min_value)) { - normalized_value = min_value; - } else { - normalized_value = value; - } - - return normalized_value; -} - -float lighting_command_normalized_range_clamp( - struct bacnet_lighting_command_data *data, float value) -{ - float clamped_value; - - if (!data) { - return value; - } - lighting_command_lock(data); - clamped_value = lighting_command_normalized_range_clamp_nolock(data, value); - lighting_command_unlock(data); - - return clamped_value; -} - /** * @brief Callback for tracking value updates * @param data - dimmer data structure @@ -555,16 +457,25 @@ float lighting_command_normalized_range_clamp( * @param milliseconds - number of milliseconds elapsed */ static void lighting_command_tracking_value_event( - struct bacnet_lighting_command_data *data, float old_value, float value) + struct bacnet_lighting_command_data *data, + float old_value, + float tracking_value) { + float physical_value, old_physical_value; + + lighting_command_min_max_value_range_adjust(data); + physical_value = lighting_command_normalized_to_physical_value( + data->Min_Actual_Value, data->Max_Actual_Value, tracking_value); + old_physical_value = lighting_command_normalized_to_physical_value( + data->Min_Actual_Value, data->Max_Actual_Value, old_value); if (data->Overridden) { - lighting_command_tracking_value_notify(data, old_value, value); + lighting_command_notify(data, old_physical_value, physical_value); if (data->Overridden_Momentary) { data->Overridden = false; } } else if (!data->Out_Of_Service) { data->Overridden_Momentary = false; - lighting_command_tracking_value_notify(data, old_value, value); + lighting_command_notify(data, old_physical_value, physical_value); } else { debug_printf( "Lighting-Command[%lu]-Out-of-Service\n", (unsigned long)data->Key); @@ -586,18 +497,17 @@ static void lighting_command_fade_handler( float target_value; old_value = data->Tracking_Value; - if (lighting_command_is_normalized_off_to_off_nolock( - data, old_value, data->Target_Level)) { + if (isless(old_value, 1.0f) && isless(data->Target_Level, 1.0f)) { /* check for OFF to OFF transition */ data->Tracking_Value = 0.0f; data->In_Progress = BACNET_LIGHTING_IDLE; data->Lighting_Operation = BACNET_LIGHTS_STOP; } else { /* clamp Target value within the Normalized ON Range */ - target_value = lighting_command_normalized_on_range_clamp_nolock( - data, data->Target_Level); + target_value = + lighting_command_normalized_on_range_clamp(data->Target_Level); if ((milliseconds >= data->Fade_Time) || - (!islessgreater(data->Tracking_Value, target_value))) { + (is_float_equal(data->Tracking_Value, target_value))) { /* stop fading */ if (isless(data->Target_Level, 1.0f)) { /* jump target to OFF if below normalized min */ @@ -609,12 +519,12 @@ static void lighting_command_fade_handler( data->Lighting_Operation = BACNET_LIGHTS_STOP; data->Fade_Time = 0; } else { - /* fading */ + /* fading in the normalized ON range */ x1 = 0.0f; x2 = (float)milliseconds; x3 = (float)data->Fade_Time; - if (isless(old_value, data->Min_Actual_Value)) { - y1 = data->Min_Actual_Value; + if (isless(old_value, 1.0f)) { + y1 = 1.0f; } else { y1 = old_value; } @@ -642,8 +552,7 @@ static void lighting_command_fade_handler( * at a particular percent per second defined by ramp-rate. * While the ramp operation is executing, In_Progress shall be set * to RAMP_ACTIVE, and Tracking_Value shall be updated to reflect the current - * progress of the ramp. shall be clamped to - * Min_Actual_Value and Max_Actual_Value. + * progress of the ramp. * * @param data - dimmer data structure * @param milliseconds - number of milliseconds elapsed @@ -655,17 +564,16 @@ static void lighting_command_ramp_handler( operating_value; old_value = data->Tracking_Value; - if (lighting_command_is_normalized_off_to_off_nolock( - data, old_value, data->Target_Level)) { + if (isless(old_value, 1.0f) && isless(data->Target_Level, 1.0f)) { /* check for OFF to OFF transition */ data->Tracking_Value = 0.0f; data->In_Progress = BACNET_LIGHTING_IDLE; data->Lighting_Operation = BACNET_LIGHTS_STOP; } else { /* clamp Target value within the Normalized ON Range */ - target_value = lighting_command_normalized_on_range_clamp_nolock( - data, data->Target_Level); - if (!islessgreater(data->Tracking_Value, target_value)) { + target_value = + lighting_command_normalized_on_range_clamp(data->Target_Level); + if (is_float_equal(data->Tracking_Value, target_value)) { /* stop ramping */ if (isless(data->Target_Level, 1.0f)) { /* jump target to OFF if below normalized min */ @@ -706,9 +614,7 @@ static void lighting_command_ramp_handler( step_value = target_value; data->Lighting_Operation = BACNET_LIGHTS_STOP; } - /* clamp target within min/max, if needed */ - step_value = lighting_command_normalized_on_range_clamp_nolock( - data, step_value); + step_value = lighting_command_normalized_on_range_clamp(step_value); if (data->Lighting_Operation == BACNET_LIGHTS_STOP) { if (isless(data->Target_Level, 1.0f)) { /* jump target to OFF if below normalized min */ @@ -736,31 +642,26 @@ static void lighting_command_ramp_handler( * Updates the object tracking value while stepping * * Commands the dimmer to a value equal to the Tracking_Value - * plus the step-increment. The resulting sum shall be clamped to - * Min_Actual_Value and Max_Actual_Value + * plus the step-increment. If the result of the addition is + * greater than 100.0%, the value shall be set to 100.0%. * * @param data [in] dimmer data */ static void lighting_command_step_up_handler(struct bacnet_lighting_command_data *data) { - float old_value, target_value, operating_value; + float old_value, target_value; old_value = data->Tracking_Value; - if (isgreaterequal(old_value, data->Min_Actual_Value)) { - /* inhibit ON if the value is already OFF */ + if (isgreaterequal(old_value, 1.0f)) { + /* inhibit ON if the value is currently OFF */ target_value = lighting_command_step_up_target_value( data->Tracking_Value, data->Step_Increment); - /* clamp Tracking value within the Normalized ON Range */ + /* clamp Tracking value inclusively within the Operating Range */ data->Tracking_Value = - lighting_command_normalized_on_range_clamp_nolock( - data, target_value); + lighting_command_normalized_on_range_clamp(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_nolock( - data, data->Tracking_Value); - data->Tracking_Value = operating_value; /* notify */ lighting_command_tracking_value_event( data, old_value, data->Tracking_Value); @@ -771,8 +672,7 @@ lighting_command_step_up_handler(struct bacnet_lighting_command_data *data) * Updates the object tracking value while stepping * * Commands the dimmer to a value equal to the Tracking_Value - * plus the step-increment. The resulting sum shall be clamped to - * Min_Actual_Value and Max_Actual_Value + * plus the step-increment. * * @param data [in] dimmer data */ @@ -784,14 +684,13 @@ lighting_command_step_down_handler(struct bacnet_lighting_command_data *data) 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_nolock(data, target_value); + lighting_command_normalized_on_range_clamp(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_nolock( - data, data->Tracking_Value); + operating_value = + lighting_command_normalized_range_clamp(data->Tracking_Value); data->Tracking_Value = operating_value; /* notify */ lighting_command_tracking_value_event( @@ -802,27 +701,32 @@ lighting_command_step_down_handler(struct bacnet_lighting_command_data *data) * Updates the object tracking value while stepping * * Commands the dimmer to a value equal to the Tracking_Value - * plus the step-increment. The resulting sum shall be clamped to - * Min_Actual_Value and Max_Actual_Value + * plus the step-increment. + * If the result of the addition is greater than 100.0%, + * the value shall be set to 100.0%. + * When the Tracking_Value is 0.0%, 1.0% is written + * to the specified slot in the priority array. * * @param data [in] dimmer data */ static void lighting_command_step_on_handler(struct bacnet_lighting_command_data *data) { - float old_value, target_value, operating_value; + float old_value, target_value; old_value = data->Tracking_Value; - target_value = lighting_command_step_up_target_value( - data->Tracking_Value, data->Step_Increment); - data->Tracking_Value = - lighting_command_normalized_range_clamp_nolock(data, target_value); + if (isless(data->Tracking_Value, 1.0f)) { + /* step is ignored when starting at OFF */ + data->Tracking_Value = 1.0f; + } else { + target_value = lighting_command_step_up_target_value( + data->Tracking_Value, data->Step_Increment); + /* clamp Tracking value inclusively within the Normalized Range */ + data->Tracking_Value = + lighting_command_normalized_range_clamp(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_nolock( - data, data->Tracking_Value); - data->Tracking_Value = operating_value; /* notify */ lighting_command_tracking_value_event( data, old_value, data->Tracking_Value); @@ -831,28 +735,28 @@ lighting_command_step_on_handler(struct bacnet_lighting_command_data *data) /** * Updates the object tracking value while stepping * - * Commands the dimmer to a value equal to the Tracking_Value - * plus the step-increment. The resulting sum shall be clamped to - * Min_Actual_Value and Max_Actual_Value + * Commands Present_Value to a value equal to the Tracking_Value + * minus the step-increment at the specified priority. + * The step-down operation is implemented by writing + * the Tracking_Value minus step-increment to the specified + * slot in the priority array. + * If the result of the subtraction is less than 1.0%, + * 0.0% is written to the specified slot in the priority array. * * @param data [in] dimmer data */ static void lighting_command_step_off_handler(struct bacnet_lighting_command_data *data) { - float old_value, target_value, operating_value; + float old_value, target_value; old_value = data->Tracking_Value; target_value = lighting_command_step_down_target_value( data->Tracking_Value, data->Step_Increment); data->Tracking_Value = - lighting_command_normalized_range_clamp_nolock(data, target_value); + lighting_command_normalized_range_clamp(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_nolock( - data, data->Tracking_Value); - data->Tracking_Value = operating_value; /* notify */ lighting_command_tracking_value_event( data, old_value, data->Tracking_Value); @@ -936,11 +840,8 @@ static void lighting_command_blink_handler( } } } - target_value = - lighting_command_normalized_range_clamp_nolock(data, target_value); /* clamp Tracking value inclusively within the Operating Range */ - operating_value = - lighting_command_operating_range_clamp_nolock(data, target_value); + operating_value = lighting_command_normalized_range_clamp(target_value); /* note: The blink-warn notifications shall not be reflected in the tracking value. */ if (data->In_Progress == BACNET_LIGHTING_IDLE) { @@ -959,7 +860,7 @@ static void lighting_command_override_nolock( float old_value; old_value = data->Tracking_Value; - data->Tracking_Value = lighting_command_physical_range_clamp(value); + data->Tracking_Value = lighting_command_normalized_range_clamp(value); lighting_command_tracking_value_event( data, old_value, data->Tracking_Value); } @@ -1003,7 +904,7 @@ void lighting_command_override_set( void lighting_command_override_clear( struct bacnet_lighting_command_data *data, float value) { - float old_value, normalized_value, operating_value; + float old_value; if (!data) { return; @@ -1013,13 +914,9 @@ void lighting_command_override_clear( data->Overridden_Momentary = false; old_value = data->Tracking_Value; /* clamp Tracking value within the Normalized Range */ - normalized_value = - lighting_command_normalized_range_clamp_nolock(data, value); - /* clamp Tracking value inclusively within the Operating Range */ - operating_value = - lighting_command_operating_range_clamp_nolock(data, normalized_value); - data->Tracking_Value = operating_value; - lighting_command_tracking_value_event(data, old_value, operating_value); + data->Tracking_Value = lighting_command_normalized_range_clamp(value); + lighting_command_tracking_value_event( + data, old_value, data->Tracking_Value); lighting_command_unlock(data); } @@ -1190,7 +1087,7 @@ void lighting_command_step( /* configure the lighting operation */ if (((operation == BACNET_LIGHTS_STEP_UP) || (operation == BACNET_LIGHTS_STEP_DOWN)) && - (!islessgreater(data->Tracking_Value, 0.0))) { + (is_float_equal(data->Tracking_Value, 0.0))) { /* If the starting level of Tracking_Value is 0.0%, then this operation is ignored. */ goto done; @@ -1202,23 +1099,19 @@ void lighting_command_step( if (operation == BACNET_LIGHTS_STEP_UP) { target_value = lighting_command_step_up_target_value( data->Tracking_Value, data->Step_Increment); - target_value = lighting_command_normalized_on_range_clamp_nolock( - data, target_value); + target_value = lighting_command_normalized_on_range_clamp(target_value); } else if (operation == BACNET_LIGHTS_STEP_DOWN) { target_value = lighting_command_step_down_target_value( data->Tracking_Value, data->Step_Increment); - target_value = lighting_command_normalized_on_range_clamp_nolock( - data, target_value); + target_value = lighting_command_normalized_on_range_clamp(target_value); } else if (operation == BACNET_LIGHTS_STEP_ON) { target_value = lighting_command_step_up_target_value( data->Tracking_Value, data->Step_Increment); - target_value = - lighting_command_normalized_range_clamp_nolock(data, target_value); + target_value = lighting_command_normalized_range_clamp(target_value); } else if (operation == BACNET_LIGHTS_STEP_OFF) { target_value = lighting_command_step_down_target_value( data->Tracking_Value, data->Step_Increment); - target_value = - lighting_command_normalized_range_clamp_nolock(data, target_value); + target_value = lighting_command_normalized_range_clamp(target_value); } else { goto done; } @@ -1431,24 +1324,43 @@ void lighting_command_toggle_default( /** * @brief Configure the lighting command to apply low or high trim * @param data [in] dimmer data + * @param high_end_trim [in] BACnet lighting high end trim + * @param low_end_trim [in] BACnet lighting low end trim + * @param trim_fade_time [in] BACnet lighting trim fade time */ void lighting_command_trim_set( struct bacnet_lighting_command_data *data, - float High_End_Trim, - float Low_End_Trim, - uint32_t Trim_Fade_Time) + float high_end_trim, + float low_end_trim, + uint32_t trim_fade_time) { + float swap_value, high_trim, low_trim; + if (!data) { return; } lighting_command_lock(data); - /* apply high and low trim */ - data->High_Trim_Value = High_End_Trim; - data->Low_Trim_Value = Low_End_Trim; - data->Trim_Fade_Time = Trim_Fade_Time; + /* clamp range within normalized limits */ + high_trim = lighting_command_normalized_on_range_clamp(high_end_trim); + low_trim = lighting_command_normalized_on_range_clamp(low_end_trim); + /* 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; + } + data->High_Trim_Value = high_trim; + data->Low_Trim_Value = low_trim; + data->Trim_Fade_Time = trim_fade_time; lighting_command_unlock(data); } +/** + * @brief Set the lighting command key + * @param data [in] dimmer data + * @param key [in] BACnet lighting key + */ void lighting_command_key_set( struct bacnet_lighting_command_data *data, uint32_t key) { @@ -1460,6 +1372,11 @@ void lighting_command_key_set( lighting_command_unlock(data); } +/** + * @brief Set the lighting command tracking value callback + * @param data [in] dimmer data + * @param cb [in] BACnet lighting tracking value callback + */ void lighting_command_tracking_value_callback_set( struct bacnet_lighting_command_data *data, lighting_command_tracking_value_callback cb) @@ -1472,6 +1389,11 @@ void lighting_command_tracking_value_callback_set( lighting_command_unlock(data); } +/** + * @brief Get the lighting command in progress + * @param data [in] dimmer data + * @return BACNET_LIGHTING_IN_PROGRESS - lighting command in progress + */ BACNET_LIGHTING_IN_PROGRESS lighting_command_in_progress_get(struct bacnet_lighting_command_data *data) { @@ -1487,6 +1409,11 @@ lighting_command_in_progress_get(struct bacnet_lighting_command_data *data) return in_progress; } +/** + * @brief Set the lighting command in progress + * @param data [in] dimmer data + * @param in_progress [in] BACnet lighting in progress + */ void lighting_command_in_progress_set( struct bacnet_lighting_command_data *data, BACNET_LIGHTING_IN_PROGRESS in_progress) @@ -1499,6 +1426,11 @@ void lighting_command_in_progress_set( lighting_command_unlock(data); } +/** + * @brief Get the lighting command tracking value + * @param data [in] dimmer data + * @return float - lighting command tracking value + */ float lighting_command_tracking_value_get( struct bacnet_lighting_command_data *data) { @@ -1514,6 +1446,11 @@ float lighting_command_tracking_value_get( return value; } +/** + * @brief Set the lighting command tracking value + * @param data [in] dimmer data + * @param value [in] BACnet lighting tracking value + */ void lighting_command_tracking_value_set( struct bacnet_lighting_command_data *data, float value) { @@ -1541,6 +1478,11 @@ void lighting_command_blink_warn_feature_set( lighting_command_unlock(data); } +/** + * @brief Check if the lighting command blink egress is active + * @param data [in] dimmer data + * @return bool - true if the lighting command blink egress is active + */ bool lighting_command_blink_egress_active( struct bacnet_lighting_command_data *data) { @@ -1556,6 +1498,11 @@ bool lighting_command_blink_egress_active( return active; } +/** + * @brief Get the lighting command out of service + * @param data [in] dimmer data + * @return bool - true if the lighting command is out of service + */ bool lighting_command_out_of_service_get( struct bacnet_lighting_command_data *data) { @@ -1571,6 +1518,11 @@ bool lighting_command_out_of_service_get( return value; } +/** + * @brief Set the lighting command out of service + * @param data [in] dimmer data + * @param value [in] BACnet lighting out of service + */ void lighting_command_out_of_service_set( struct bacnet_lighting_command_data *data, bool value) { @@ -1582,6 +1534,11 @@ void lighting_command_out_of_service_set( lighting_command_unlock(data); } +/** + * @brief Get the lighting command last on value + * @param data [in] dimmer data + * @return float - lighting command last on value + */ float lighting_command_last_on_value_get( struct bacnet_lighting_command_data *data) { @@ -1597,6 +1554,11 @@ float lighting_command_last_on_value_get( return value; } +/** + * @brief Set the lighting command last on value + * @param data [in] dimmer data + * @param value [in] BACnet lighting last on value + */ void lighting_command_last_on_value_set( struct bacnet_lighting_command_data *data, float value) { @@ -1608,6 +1570,11 @@ void lighting_command_last_on_value_set( lighting_command_unlock(data); } +/** + * @brief Get the lighting command default on value + * @param data [in] dimmer data + * @return float - lighting command default on value + */ float lighting_command_default_on_value_get( struct bacnet_lighting_command_data *data) { @@ -1623,6 +1590,11 @@ float lighting_command_default_on_value_get( return value; } +/** + * @brief Set the lighting command default on value + * @param data [in] dimmer data + * @param value [in] BACnet lighting default on value + */ void lighting_command_default_on_value_set( struct bacnet_lighting_command_data *data, float value) { @@ -1634,6 +1606,11 @@ void lighting_command_default_on_value_set( lighting_command_unlock(data); } +/** + * @brief Get the lighting command overridden status + * @param data [in] dimmer data + * @return bool - true if the lighting command is overridden + */ bool lighting_command_overridden_status( struct bacnet_lighting_command_data *data) { @@ -1649,6 +1626,27 @@ bool lighting_command_overridden_status( return status; } +/** + * @brief Get the lighting command feedback value + * @param data [in] dimmer data + * @return float - lighting command feedback value + */ +float lighting_command_feedback_value(struct bacnet_lighting_command_data *data) +{ + float feedback_value; + + if (!data) { + return 0.0f; + } + lighting_command_lock(data); + lighting_command_min_max_value_range_adjust(data); + feedback_value = lighting_command_normalized_to_physical_value( + data->Min_Actual_Value, data->Max_Actual_Value, data->Tracking_Value); + lighting_command_unlock(data); + + return feedback_value; +} + /** * @brief Determine if fade, ramp, or warn command is currently executing * @param pObject [in] object to apply the trim values to diff --git a/src/bacnet/basic/sys/lighting_command.h b/src/bacnet/basic/sys/lighting_command.h index b5d37914..e58b368d 100644 --- a/src/bacnet/basic/sys/lighting_command.h +++ b/src/bacnet/basic/sys/lighting_command.h @@ -13,10 +13,10 @@ #include "bacnet/bacdef.h" /** - * @brief Callback for tracking value updates + * @brief Callback for lighting command notifications * @param key - key used to link to specific light - * @param old_value - value prior to write - * @param value - value of the write + * @param old_value - physical value prior to write + * @param value - physical value of the write */ typedef void (*lighting_command_tracking_value_callback)( uint32_t key, float old_value, float value); @@ -165,22 +165,23 @@ BACNET_STACK_EXPORT float lighting_command_ramp_rate_clamp(float ramp_rate); BACNET_STACK_EXPORT float lighting_command_step_increment_clamp(float step_increment); -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); +float lighting_command_normalized_range_clamp(float value); + BACNET_STACK_EXPORT -float lighting_command_normalized_on_range_clamp( - struct bacnet_lighting_command_data *data, float value); +float lighting_command_normalized_on_range_clamp(float value); BACNET_STACK_EXPORT -float lighting_command_physical_range_clamp(float value); +float lighting_command_normalized_to_physical_value( + float min_value, float max_value, float normalized_value); +BACNET_STACK_EXPORT +float lighting_command_feedback_value( + struct bacnet_lighting_command_data *data); BACNET_STACK_EXPORT void lighting_command_trim_set( diff --git a/test/bacnet/basic/sys/lighting_command/src/main.c b/test/bacnet/basic/sys/lighting_command/src/main.c index 63f528ca..164dd6c8 100644 --- a/test/bacnet/basic/sys/lighting_command/src/main.c +++ b/test/bacnet/basic/sys/lighting_command/src/main.c @@ -235,15 +235,23 @@ static void test_lighting_command_unit(void) lighting_command_timer(&data, milliseconds); zassert_true(data.In_Progress == BACNET_LIGHTING_IDLE, NULL); - /* normalized range clamp testing */ + /* normalized range scaling to physical values */ data.Max_Actual_Value = 95.0f; data.Min_Actual_Value = 5.0f; - target_level = lighting_command_normalized_range_clamp(&data, 0.1f); + target_level = lighting_command_normalized_to_physical_value( + data.Min_Actual_Value, data.Max_Actual_Value, 0.1f); zassert_true(is_float_equal(target_level, 0.0f), NULL); - target_level = lighting_command_normalized_range_clamp(&data, 100.0f); + target_level = lighting_command_normalized_to_physical_value( + data.Min_Actual_Value, data.Max_Actual_Value, 100.1f); zassert_true(is_float_equal(target_level, data.Max_Actual_Value), NULL); - target_level = lighting_command_normalized_range_clamp(&data, 1.0f); - zassert_true(is_float_equal(target_level, data.Min_Actual_Value), NULL); + target_level = lighting_command_normalized_to_physical_value( + data.Min_Actual_Value, data.Max_Actual_Value, 50.0f); + zassert_true( + islessequal(target_level, data.Max_Actual_Value), "physical-value=%.2f", + target_level); + zassert_true( + isgreaterequal(target_level, data.Min_Actual_Value), + "physical-value=%.2f", target_level); data.Max_Actual_Value = 100.0f; data.Min_Actual_Value = 1.0f; @@ -306,8 +314,8 @@ static void test_lighting_command_unit(void) lighting_command_timer(&data, milliseconds); zassert_true(data.In_Progress == BACNET_LIGHTING_IDLE, NULL); zassert_true(is_float_equal(Tracking_Value, target_level), NULL); - data.Low_Trim_Value = data.Min_Actual_Value; zassert_true(is_float_equal(data.Last_On_Value, 1.0f), NULL); + data.Low_Trim_Value = 1.0f; /* high trim */ data.High_Trim_Value = 90.0f; target_level = 100.0f; @@ -317,8 +325,8 @@ static void test_lighting_command_unit(void) 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); - data.High_Trim_Value = data.Max_Actual_Value; zassert_true(is_float_equal(data.Last_On_Value, target_level), NULL); + data.High_Trim_Value = 100.0f; /* trim fade time */ target_level = 80.0f; milliseconds = 10; @@ -408,21 +416,21 @@ static void test_lighting_command_unit(void) zassert_true(is_float_equal(target_step, 100.0f), NULL); /* physical range clamping */ - target_level = lighting_command_physical_range_clamp(0.0f); + target_level = lighting_command_normalized_range_clamp(0.0f); zassert_true(is_float_equal(target_level, 0.0f), NULL); - target_level = lighting_command_physical_range_clamp(0.5f); + target_level = lighting_command_normalized_range_clamp(0.5f); zassert_true(is_float_equal(target_level, 0.0f), NULL); - target_level = lighting_command_physical_range_clamp(0.9f); + target_level = lighting_command_normalized_range_clamp(0.9f); zassert_true(is_float_equal(target_level, 0.0f), NULL); - target_level = lighting_command_physical_range_clamp(1.0f); + target_level = lighting_command_normalized_range_clamp(1.0f); zassert_true(is_float_equal(target_level, 1.0f), NULL); - target_level = lighting_command_physical_range_clamp(50.0f); + target_level = lighting_command_normalized_range_clamp(50.0f); zassert_true(is_float_equal(target_level, 50.0f), NULL); - target_level = lighting_command_physical_range_clamp(100.0f); + target_level = lighting_command_normalized_range_clamp(100.0f); zassert_true(is_float_equal(target_level, 100.0f), NULL); - target_level = lighting_command_physical_range_clamp(100.1f); + target_level = lighting_command_normalized_range_clamp(100.1f); zassert_true(is_float_equal(target_level, 100.0f), NULL); - target_level = lighting_command_physical_range_clamp(150.0f); + target_level = lighting_command_normalized_range_clamp(150.0f); zassert_true(is_float_equal(target_level, 100.0f), NULL); /* step UP - inhibit ON */ @@ -483,7 +491,7 @@ static void test_lighting_command_unit(void) is_float_equal(data.Last_On_Value, data.Max_Actual_Value), NULL); /* step DOWN, not off */ target_step = 1.0f; - target_level = data.Min_Actual_Value + target_step; + target_level = 1.0f + target_step; milliseconds = 10; lighting_command_fade_to(&data, target_level, 0); lighting_command_timer(&data, milliseconds); @@ -492,18 +500,17 @@ static void test_lighting_command_unit(void) lighting_command_step(&data, BACNET_LIGHTS_STEP_DOWN, target_step); lighting_command_timer(&data, milliseconds); zassert_true(data.In_Progress == BACNET_LIGHTING_IDLE, NULL); - zassert_true(is_float_equal(Tracking_Value, data.Min_Actual_Value), NULL); - zassert_true( - is_float_equal(data.Last_On_Value, data.Min_Actual_Value), NULL); - /* clamp to min */ + zassert_true(is_float_equal(Tracking_Value, 1.0f), NULL); + zassert_true(is_float_equal(data.Last_On_Value, 1.0f), NULL); + /* clamp to min normal */ target_step = 100.0f; lighting_command_step(&data, BACNET_LIGHTS_STEP_DOWN, target_step); lighting_command_timer(&data, milliseconds); zassert_true(data.In_Progress == BACNET_LIGHTING_IDLE, NULL); - zassert_true(is_float_equal(Tracking_Value, data.Min_Actual_Value), NULL); + zassert_true(is_float_equal(Tracking_Value, 1.0f), NULL); /* step DOWN and off */ target_step = 100.0f; - target_level = data.Min_Actual_Value; + target_level = 1.0f; milliseconds = 10; lighting_command_fade_to(&data, target_level, 0); lighting_command_timer(&data, milliseconds); @@ -513,8 +520,7 @@ static void test_lighting_command_unit(void) lighting_command_timer(&data, milliseconds); zassert_true(data.In_Progress == BACNET_LIGHTING_IDLE, NULL); zassert_true(is_float_equal(Tracking_Value, 0.0f), NULL); - zassert_true( - is_float_equal(data.Last_On_Value, data.Min_Actual_Value), NULL); + zassert_true(is_float_equal(data.Last_On_Value, 1.0f), NULL); /* blink warn - immediate off */ data.Blink.Interval = 0; data.Blink.Duration = 0; @@ -574,8 +580,8 @@ static void test_lighting_command_unit(void) data.In_Progress == BACNET_LIGHTING_RAMP_ACTIVE, "In_Progress=%d", data.In_Progress); zassert_true( - isgreater(data.Tracking_Value, data.Min_Actual_Value), - "Tracking_Value=%f", Tracking_Value); + isgreater(data.Tracking_Value, 1.0f), "Tracking_Value=%f", + Tracking_Value); zassert_true( isless(data.Tracking_Value, data.Max_Actual_Value), "Tracking_Value=%f", Tracking_Value); @@ -583,7 +589,7 @@ static void test_lighting_command_unit(void) } while (data.Lighting_Operation != BACNET_LIGHTS_STOP); /* slower ramp down */ - target_level = data.Min_Actual_Value; + target_level = 1.0f; milliseconds = 33; ramp_rate = 0.1f; do { @@ -594,15 +600,14 @@ static void test_lighting_command_unit(void) data.In_Progress == BACNET_LIGHTING_RAMP_ACTIVE, "In_Progress=%d", data.In_Progress); zassert_true( - isgreater(data.Tracking_Value, data.Min_Actual_Value), - "Tracking_Value=%f", Tracking_Value); + isgreater(data.Tracking_Value, 1.0f), "Tracking_Value=%f", + Tracking_Value); zassert_true( isless(data.Tracking_Value, data.Max_Actual_Value), "Tracking_Value=%f", Tracking_Value); } } while (data.Lighting_Operation != BACNET_LIGHTS_STOP); - zassert_true( - is_float_equal(data.Last_On_Value, data.Min_Actual_Value), NULL); + zassert_true(is_float_equal(data.Last_On_Value, 1.0f), NULL); /* large elapsed timer - ramp up */ target_level = data.Max_Actual_Value; milliseconds = 2000; @@ -615,8 +620,8 @@ static void test_lighting_command_unit(void) data.In_Progress == BACNET_LIGHTING_RAMP_ACTIVE, "In_Progress=%d", data.In_Progress); zassert_true( - isgreater(data.Tracking_Value, data.Min_Actual_Value), - "Tracking_Value=%f", Tracking_Value); + isgreater(data.Tracking_Value, 1.0f), "Tracking_Value=%f", + Tracking_Value); zassert_true( isless(data.Tracking_Value, data.Max_Actual_Value), "Tracking_Value=%f", Tracking_Value);