Feature/color objects color command (#302)
* added BACnetColorCommand and BACnetxyColor encoding and unit testing * Added Color object and unit testing. * Added Color Temperature object and Unit test * Fix BVLC unit test warning. * add port Makefile for extra types * added RGB to and from CIE xy utility in sys folder, and add unit tests. * added cmake-win32 target * Change RP and RPM to use known property decoder. Add color object RP and RPM decoding and printing Fix RPM print for new reserved range above 4194303 Change default protocol-revision to 24 for Color object * Integrate Color and Color Temperature objects into demo apps Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
@@ -0,0 +1,917 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date June 2022
|
||||
* @brief Color objects, customize for your use
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* The Color object is an object with a present-value that
|
||||
* uses an x,y color single precision floating point data type.
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/cov.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/abort.h"
|
||||
#include "bacnet/lighting.h"
|
||||
#include "bacnet/reject.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/sys/keylist.h"
|
||||
/* me! */
|
||||
#include "color_object.h"
|
||||
|
||||
struct object_data {
|
||||
bool Changed : 1;
|
||||
bool Write_Enabled : 1;
|
||||
BACNET_XY_COLOR Present_Value;
|
||||
BACNET_XY_COLOR Tracking_Value;
|
||||
BACNET_COLOR_COMMAND Color_Command;
|
||||
BACNET_COLOR_OPERATION_IN_PROGRESS In_Progress;
|
||||
BACNET_XY_COLOR Default_Color;
|
||||
uint32_t Default_Fade_Time;
|
||||
BACNET_COLOR_TRANSITION Transition;
|
||||
const char *Object_Name;
|
||||
const char *Description;
|
||||
};
|
||||
/* Key List for storing the object data sorted by instance number */
|
||||
static OS_Keylist Object_List;
|
||||
/* callback for present value writes */
|
||||
static color_write_present_value_callback Color_Write_Present_Value_Callback;
|
||||
|
||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||
static const int Color_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_TRACKING_VALUE,
|
||||
PROP_COLOR_COMMAND, PROP_IN_PROGRESS, PROP_DEFAULT_COLOR,
|
||||
PROP_DEFAULT_FADE_TIME, -1 };
|
||||
|
||||
static const int Color_Properties_Optional[] = { PROP_DESCRIPTION,
|
||||
PROP_TRANSITION, -1 };
|
||||
|
||||
static const int Color_Properties_Proprietary[] = { -1 };
|
||||
|
||||
/**
|
||||
* Returns the list of required, optional, and proprietary properties.
|
||||
* Used by ReadPropertyMultiple service.
|
||||
*
|
||||
* @param pRequired - pointer to list of int terminated by -1, of
|
||||
* BACnet required properties for this object.
|
||||
* @param pOptional - pointer to list of int terminated by -1, of
|
||||
* BACnet optkional properties for this object.
|
||||
* @param pProprietary - pointer to list of int terminated by -1, of
|
||||
* BACnet proprietary properties for this object.
|
||||
*/
|
||||
void Color_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary)
|
||||
{
|
||||
if (pRequired) {
|
||||
*pRequired = Color_Properties_Required;
|
||||
}
|
||||
if (pOptional) {
|
||||
*pOptional = Color_Properties_Optional;
|
||||
}
|
||||
if (pProprietary) {
|
||||
*pProprietary = Color_Properties_Proprietary;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a given Color instance is valid
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
*
|
||||
* @return true if the instance is valid, and false if not
|
||||
*/
|
||||
bool Color_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the number of Color objects
|
||||
*
|
||||
* @return Number of Color objects
|
||||
*/
|
||||
unsigned Color_Count(void)
|
||||
{
|
||||
return Keylist_Count(Object_List);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the object instance-number for a given 0..N index
|
||||
* of Color objects where N is Color_Count().
|
||||
*
|
||||
* @param index - 0..N where N is Color_Count()
|
||||
*
|
||||
* @return object instance-number for the given index
|
||||
*/
|
||||
uint32_t Color_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return Keylist_Key(Object_List, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, determines a 0..N index
|
||||
* of Color objects where N is Color_Count().
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
*
|
||||
* @return index for the given instance-number, or Color_Count()
|
||||
* if not valid.
|
||||
*/
|
||||
unsigned Color_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
return Keylist_Index(Object_List, object_instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, determines the present-value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
*
|
||||
* @return present-value of the object
|
||||
*/
|
||||
bool Color_Present_Value(uint32_t object_instance, BACNET_XY_COLOR *value)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
xy_color_copy(value, &pObject->Present_Value);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, sets the present-value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - floating point Color
|
||||
*
|
||||
* @return true if values are within range and present-value is set.
|
||||
*/
|
||||
bool Color_Present_Value_Set(uint32_t object_instance, BACNET_XY_COLOR *value)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
xy_color_copy(&pObject->Present_Value, value);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, sets the present-value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - floating point Color
|
||||
* @param priority - priority-array index value 1..16
|
||||
* @param error_class - the BACnet error class
|
||||
* @param error_code - BACnet Error code
|
||||
*
|
||||
* @return true if values are within range and present-value is set.
|
||||
*/
|
||||
static bool Color_Present_Value_Write(uint32_t object_instance,
|
||||
BACNET_XY_COLOR *value,
|
||||
uint8_t priority,
|
||||
BACNET_ERROR_CLASS *error_class,
|
||||
BACNET_ERROR_CODE *error_code)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject;
|
||||
BACNET_XY_COLOR old_value = { 0.0, 0.0 };
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
(void)priority;
|
||||
if (pObject->Write_Enabled) {
|
||||
xy_color_copy(&old_value, &pObject->Present_Value);
|
||||
xy_color_copy(&pObject->Present_Value, value);
|
||||
if (Color_Write_Present_Value_Callback) {
|
||||
Color_Write_Present_Value_Callback(
|
||||
object_instance, &old_value, value);
|
||||
}
|
||||
status = true;
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
}
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_OBJECT;
|
||||
*error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, determines the present-value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
*
|
||||
* @return present-value of the object
|
||||
*/
|
||||
bool Color_Tracking_Value(uint32_t object_instance, BACNET_XY_COLOR *value)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
xy_color_copy(value, &pObject->Tracking_Value);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, sets the present-value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - floating point Color
|
||||
*
|
||||
* @return true if values are within range and present-value is set.
|
||||
*/
|
||||
bool Color_Tracking_Value_Set(uint32_t object_instance, BACNET_XY_COLOR *value)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
xy_color_copy(&pObject->Tracking_Value, value);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, gets the property value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - color command data
|
||||
* @return true if the property value is copied
|
||||
*/
|
||||
bool Color_Command(uint32_t object_instance, BACNET_COLOR_COMMAND *value)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject && value) {
|
||||
color_command_copy(value, &pObject->Color_Command);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, sets the property value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - color command data
|
||||
* @return true if values are within range and value is set.
|
||||
*/
|
||||
bool Color_Command_Set(uint32_t object_instance, BACNET_COLOR_COMMAND *value)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject && value) {
|
||||
color_command_copy(&pObject->Color_Command, value);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, gets the property value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return property value
|
||||
*/
|
||||
BACNET_COLOR_OPERATION_IN_PROGRESS Color_In_Progress(uint32_t object_instance)
|
||||
{
|
||||
BACNET_COLOR_OPERATION_IN_PROGRESS value =
|
||||
BACNET_COLOR_OPERATION_IN_PROGRESS_MAX;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
value = pObject->In_Progress;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, sets the property value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - BACNET_COLOR_OPERATION_IN_PROGRESS
|
||||
* @return true if values are within range and value is set.
|
||||
*/
|
||||
bool Color_In_Progress_Set(
|
||||
uint32_t object_instance, BACNET_COLOR_OPERATION_IN_PROGRESS value)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
if (value < BACNET_COLOR_OPERATION_IN_PROGRESS_MAX) {
|
||||
pObject->In_Progress = value;
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, determines the present-value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
*
|
||||
* @return present-value of the object
|
||||
*/
|
||||
bool Color_Default_Color(uint32_t object_instance, BACNET_XY_COLOR *value)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
xy_color_copy(value, &pObject->Default_Color);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, sets the present-value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - floating point Color
|
||||
*
|
||||
* @return true if values are within range and present-value is set.
|
||||
*/
|
||||
bool Color_Default_Color_Set(uint32_t object_instance, BACNET_XY_COLOR *value)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
xy_color_copy(&pObject->Default_Color, value);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, gets the property value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return property value
|
||||
*/
|
||||
uint32_t Color_Default_Fade_Time(uint32_t object_instance)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
value = pObject->Default_Fade_Time;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, sets the property value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - BACNET_COLOR_OPERATION_IN_PROGRESS
|
||||
* @return true if values are within range and value is set.
|
||||
*/
|
||||
bool Color_Default_Fade_Time_Set(uint32_t object_instance, uint32_t value)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
if ((value == 0) ||
|
||||
((value >= BACNET_COLOR_FADE_TIME_MIN) &&
|
||||
(value <= BACNET_COLOR_FADE_TIME_MAX))) {
|
||||
pObject->Default_Fade_Time = value;
|
||||
}
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, gets the property value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return property value
|
||||
*/
|
||||
BACNET_COLOR_TRANSITION Color_Transition(uint32_t object_instance)
|
||||
{
|
||||
BACNET_COLOR_TRANSITION value = BACNET_COLOR_OPERATION_IN_PROGRESS_MAX;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
value = pObject->Transition;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, sets the property value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - BACNET_COLOR_TRANSITION
|
||||
* @return true if values are within range and value is set.
|
||||
*/
|
||||
bool Color_Transition_Set(
|
||||
uint32_t object_instance, BACNET_COLOR_TRANSITION value)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
if (value < BACNET_COLOR_TRANSITION_MAX) {
|
||||
pObject->Transition = value;
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, loads the object-name into
|
||||
* a characterstring. Note that the object name must be unique
|
||||
* within this device.
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param object_name - holds the object-name retrieved
|
||||
*
|
||||
* @return true if object-name was retrieved
|
||||
*/
|
||||
bool Color_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject;
|
||||
char name_text[16] = "COLOR-4194303";
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
if (pObject->Object_Name) {
|
||||
status =
|
||||
characterstring_init_ansi(object_name, pObject->Object_Name);
|
||||
} else {
|
||||
snprintf(name_text, sizeof(name_text), "COLOR-%u", object_instance);
|
||||
status = characterstring_init_ansi(object_name, name_text);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, sets the object-name
|
||||
* Note that the object name must be unique within this device.
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param new_name - holds the object-name to be set
|
||||
*
|
||||
* @return true if object-name was set
|
||||
*/
|
||||
bool Color_Name_Set(uint32_t object_instance, char *new_name)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
BACNET_CHARACTER_STRING object_name;
|
||||
BACNET_OBJECT_TYPE found_type = 0;
|
||||
uint32_t found_instance = 0;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject && new_name) {
|
||||
/* All the object names in a device must be unique */
|
||||
characterstring_init_ansi(&object_name, new_name);
|
||||
if (Device_Valid_Object_Name(
|
||||
&object_name, &found_type, &found_instance)) {
|
||||
if ((found_type == OBJECT_COLOR) &&
|
||||
(found_instance == object_instance)) {
|
||||
/* writing same name to same object */
|
||||
status = true;
|
||||
} else {
|
||||
/* duplicate name! */
|
||||
status = false;
|
||||
}
|
||||
} else {
|
||||
status = true;
|
||||
pObject->Object_Name = new_name;
|
||||
Device_Inc_Database_Revision();
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, returns the description
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
*
|
||||
* @return description text or NULL if not found
|
||||
*/
|
||||
char *Color_Description(uint32_t object_instance)
|
||||
{
|
||||
char *name = NULL;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
if (pObject->Description) {
|
||||
name = (char *)pObject->Description;
|
||||
} else {
|
||||
name = "";
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, sets the description
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param new_name - holds the description to be set
|
||||
*
|
||||
* @return true if object-name was set
|
||||
*/
|
||||
bool Color_Description_Set(uint32_t object_instance, char *new_name)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject && new_name) {
|
||||
status = true;
|
||||
pObject->Description = new_name;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* ReadProperty handler for this object. For the given ReadProperty
|
||||
* data, the application_data is loaded or the error flags are set.
|
||||
*
|
||||
* @param rpdata - BACNET_READ_PROPERTY_DATA data, including
|
||||
* requested data and space for the reply, or error response.
|
||||
*
|
||||
* @return number of APDU bytes in the response, or
|
||||
* BACNET_STATUS_ERROR on error.
|
||||
*/
|
||||
int Color_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
uint8_t *apdu = NULL;
|
||||
BACNET_XY_COLOR color_value = { 0.0, 0.0 };
|
||||
BACNET_COLOR_COMMAND color_command = { 0 };
|
||||
|
||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
apdu = rpdata->application_data;
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], rpdata->object_type, rpdata->object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
Color_Object_Name(rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], rpdata->object_type);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
if (Color_Present_Value(rpdata->object_instance, &color_value)) {
|
||||
apdu_len = xy_color_encode(apdu, &color_value);
|
||||
}
|
||||
break;
|
||||
case PROP_TRACKING_VALUE:
|
||||
if (Color_Tracking_Value(rpdata->object_instance, &color_value)) {
|
||||
apdu_len = xy_color_encode(apdu, &color_value);
|
||||
}
|
||||
break;
|
||||
case PROP_COLOR_COMMAND:
|
||||
if (Color_Command(rpdata->object_instance, &color_command)) {
|
||||
apdu_len = color_command_encode(apdu, &color_command);
|
||||
}
|
||||
break;
|
||||
case PROP_IN_PROGRESS:
|
||||
apdu_len = encode_application_enumerated(
|
||||
apdu, Color_In_Progress(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_DEFAULT_COLOR:
|
||||
if (Color_Default_Color(rpdata->object_instance, &color_value)) {
|
||||
apdu_len = xy_color_encode(apdu, &color_value);
|
||||
}
|
||||
break;
|
||||
case PROP_DEFAULT_FADE_TIME:
|
||||
apdu_len = encode_application_unsigned(
|
||||
apdu, Color_Default_Fade_Time(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_TRANSITION:
|
||||
apdu_len = encode_application_enumerated(
|
||||
apdu, Color_Transition(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_DESCRIPTION:
|
||||
characterstring_init_ansi(
|
||||
&char_string, Color_Description(rpdata->object_instance));
|
||||
apdu_len = encode_application_character_string(apdu, &char_string);
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(rpdata->object_property != PROP_EVENT_TIME_STAMPS) &&
|
||||
(rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* WriteProperty handler for this object. For the given WriteProperty
|
||||
* data, the application_data is loaded or the error flags are set.
|
||||
*
|
||||
* @param wp_data - BACNET_WRITE_PROPERTY_DATA data, including
|
||||
* requested data and space for the reply, or error response.
|
||||
*
|
||||
* @return false if an error is loaded, true if no errors
|
||||
*/
|
||||
bool Color_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(
|
||||
wp_data->application_data, wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(wp_data->object_property != PROP_EVENT_TIME_STAMPS) &&
|
||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
/* only array properties can have array options */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_PRESENT_VALUE:
|
||||
status = write_property_type_valid(
|
||||
wp_data, &value, BACNET_APPLICATION_TAG_REAL);
|
||||
if (status) {
|
||||
status = Color_Present_Value_Write(wp_data->object_instance,
|
||||
&value.type.XY_Color, wp_data->priority,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_DESCRIPTION:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a callback used when present-value is written from BACnet
|
||||
* @param cb - callback used to provide indications
|
||||
*/
|
||||
void Color_Write_Present_Value_Callback_Set(
|
||||
color_write_present_value_callback cb)
|
||||
{
|
||||
Color_Write_Present_Value_Callback = cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines a object write-enabled flag state
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return write-enabled status flag
|
||||
*/
|
||||
bool Color_Write_Enabled(uint32_t object_instance)
|
||||
{
|
||||
bool value = false;
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
value = pObject->Write_Enabled;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For a given object instance-number, sets the write-enabled flag
|
||||
* @param object_instance - object-instance number of the object
|
||||
*/
|
||||
void Color_Write_Enable(uint32_t object_instance)
|
||||
{
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
pObject->Write_Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For a given object instance-number, clears the write-enabled flag
|
||||
* @param object_instance - object-instance number of the object
|
||||
*/
|
||||
void Color_Write_Disable(uint32_t object_instance)
|
||||
{
|
||||
struct object_data *pObject;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
pObject->Write_Enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Color object
|
||||
* @param object_instance - object-instance number of the object
|
||||
*/
|
||||
bool Color_Create(uint32_t object_instance)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject = NULL;
|
||||
int index = 0;
|
||||
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (!pObject) {
|
||||
pObject = calloc(1, sizeof(struct object_data));
|
||||
if (pObject) {
|
||||
pObject->Object_Name = NULL;
|
||||
pObject->Present_Value.x_coordinate = 0.0;
|
||||
pObject->Present_Value.y_coordinate = 0.0;
|
||||
pObject->Tracking_Value.x_coordinate = 0.0;
|
||||
pObject->Tracking_Value.y_coordinate = 0.0;
|
||||
pObject->Color_Command.operation = BACNET_COLOR_OPERATION_NONE;
|
||||
pObject->In_Progress = BACNET_COLOR_OPERATION_IN_PROGRESS_IDLE;
|
||||
pObject->Default_Color.x_coordinate = 1.0;
|
||||
pObject->Default_Color.y_coordinate = 1.0;
|
||||
pObject->Default_Fade_Time = 0;
|
||||
pObject->Transition = BACNET_COLOR_TRANSITION_NONE;
|
||||
pObject->Changed = false;
|
||||
pObject->Write_Enabled = false;
|
||||
/* add to list */
|
||||
index = Keylist_Data_Add(Object_List, object_instance, pObject);
|
||||
if (index >= 0) {
|
||||
status = true;
|
||||
Device_Inc_Database_Revision();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an Color object
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return true if the object is deleted
|
||||
*/
|
||||
bool Color_Delete(uint32_t object_instance)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject = NULL;
|
||||
|
||||
pObject = Keylist_Data_Delete(Object_List, object_instance);
|
||||
if (pObject) {
|
||||
free(pObject);
|
||||
status = true;
|
||||
Device_Inc_Database_Revision();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all the Colors and their data
|
||||
*/
|
||||
void Color_Cleanup(void)
|
||||
{
|
||||
struct object_data *pObject;
|
||||
|
||||
if (Object_List) {
|
||||
do {
|
||||
pObject = Keylist_Data_Pop(Object_List);
|
||||
if (pObject) {
|
||||
free(pObject);
|
||||
Device_Inc_Database_Revision();
|
||||
}
|
||||
} while (pObject);
|
||||
Keylist_Delete(Object_List);
|
||||
Object_List = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the Color object data
|
||||
*/
|
||||
void Color_Init(void)
|
||||
{
|
||||
Object_List = Keylist_Create();
|
||||
if (Object_List) {
|
||||
atexit(Color_Cleanup);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date June 2022
|
||||
* @brief Color object, customize for your use
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* The Color object is an object with a present-value that
|
||||
* uses an x,y color single precision floating point data type.
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_COLOR_OBJECT_H
|
||||
#define BACNET_COLOR_OBJECT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacnet_stack_exports.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
/**
|
||||
* @brief Callback for gateway write present value request
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param old_value - BACnetXYColor value prior to write
|
||||
* @param value - BACnetXYColor value of the write
|
||||
*/
|
||||
typedef void (*color_write_present_value_callback)(uint32_t object_instance,
|
||||
BACNET_XY_COLOR *old_value,
|
||||
BACNET_XY_COLOR *value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void Color_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Valid_Instance(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
unsigned Color_Count(void);
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Color_Index_To_Instance(unsigned index);
|
||||
BACNET_STACK_EXPORT
|
||||
unsigned Color_Instance_To_Index(uint32_t object_instance);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Name_Set(uint32_t object_instance, char *new_name);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int Color_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Present_Value_Set(uint32_t object_instance, BACNET_XY_COLOR *value);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Present_Value(uint32_t object_instance, BACNET_XY_COLOR *value);
|
||||
BACNET_STACK_EXPORT
|
||||
void Color_Write_Present_Value_Callback_Set(
|
||||
color_write_present_value_callback cb);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Tracking_Value_Set(uint32_t object_instance, BACNET_XY_COLOR *value);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Tracking_Value(uint32_t object_instance, BACNET_XY_COLOR *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Command(uint32_t object_instance, BACNET_COLOR_COMMAND *value);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Command_Set(uint32_t object_instance, BACNET_COLOR_COMMAND *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Default_Color_Set(uint32_t object_instance, BACNET_XY_COLOR *value);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Default_Color(uint32_t object_instance, BACNET_XY_COLOR *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Color_Default_Fade_Time(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Default_Fade_Time_Set(uint32_t object_instance, uint32_t value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_COLOR_OPERATION_IN_PROGRESS Color_In_Progress(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_In_Progress_Set(
|
||||
uint32_t object_instance, BACNET_COLOR_OPERATION_IN_PROGRESS value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_COLOR_TRANSITION Color_Transition(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Transition_Set(
|
||||
uint32_t object_instance, BACNET_COLOR_TRANSITION value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
char *Color_Description(uint32_t instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Description_Set(uint32_t instance, char *new_name);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Write_Enabled(uint32_t instance);
|
||||
BACNET_STACK_EXPORT
|
||||
void Color_Write_Enable(uint32_t instance);
|
||||
BACNET_STACK_EXPORT
|
||||
void Color_Write_Disable(uint32_t instance);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Create(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Delete(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
void Color_Cleanup(void);
|
||||
BACNET_STACK_EXPORT
|
||||
void Color_Init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date July 2022
|
||||
* @brief Color Temperature object, customize for your use
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* The Color Temperature object is an object with a present-value that
|
||||
* uses an Color Temperature INTEGER type
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_COLOR_TEMPERATURE_H
|
||||
#define BACNET_COLOR_TEMPERATURE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacnet_stack_exports.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
/**
|
||||
* @brief Callback for gateway write present value request
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param old_value - 32-bit value prior to write
|
||||
* @param value - 32-bit value of the write
|
||||
*/
|
||||
typedef void (*color_temperature_write_present_value_callback)(
|
||||
uint32_t object_instance, uint32_t old_value, uint32_t value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void Color_Temperature_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Valid_Instance(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
unsigned Color_Temperature_Count(void);
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Color_Temperature_Index_To_Instance(unsigned index);
|
||||
BACNET_STACK_EXPORT
|
||||
unsigned Color_Temperature_Instance_To_Index(uint32_t object_instance);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Name_Set(uint32_t object_instance, char *new_name);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int Color_Temperature_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Present_Value_Set(
|
||||
uint32_t object_instance, uint32_t value);
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Color_Temperature_Present_Value(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
void Color_Temperature_Write_Present_Value_Callback_Set(
|
||||
color_temperature_write_present_value_callback cb);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Tracking_Value_Set(
|
||||
uint32_t object_instance, uint32_t value);
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Color_Temperature_Tracking_Value(uint32_t object_instance);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Color_Temperature_Min_Pres_Value(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Min_Pres_Value_Set(
|
||||
uint32_t object_instance, uint32_t value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Color_Temperature_Max_Pres_Value(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Max_Pres_Value_Set(
|
||||
uint32_t object_instance, uint32_t value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Command(
|
||||
uint32_t object_instance, BACNET_COLOR_COMMAND *value);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Command_Set(
|
||||
uint32_t object_instance, BACNET_COLOR_COMMAND *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Default_Color_Temperature_Set(
|
||||
uint32_t object_instance, uint32_t value);
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Color_Temperature_Default_Color_Temperature(uint32_t object_instance);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Color_Temperature_Default_Fade_Time(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Default_Fade_Time_Set(
|
||||
uint32_t object_instance, uint32_t value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Color_Temperature_Default_Ramp_Rate(
|
||||
uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Default_Ramp_Rate_Set(
|
||||
uint32_t object_instance, uint32_t value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Color_Temperature_Default_Step_Increment(
|
||||
uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Default_Step_Increment_Set(
|
||||
uint32_t object_instance, uint32_t value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_COLOR_OPERATION_IN_PROGRESS Color_Temperature_In_Progress(
|
||||
uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_In_Progress_Set(
|
||||
uint32_t object_instance, BACNET_COLOR_OPERATION_IN_PROGRESS value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_COLOR_TRANSITION Color_Temperature_Transition(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Transition_Set(
|
||||
uint32_t object_instance, BACNET_COLOR_TRANSITION value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
char *Color_Temperature_Description(uint32_t instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Description_Set(uint32_t instance, char *new_name);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Write_Enabled(uint32_t instance);
|
||||
BACNET_STACK_EXPORT
|
||||
void Color_Temperature_Write_Enable(uint32_t instance);
|
||||
BACNET_STACK_EXPORT
|
||||
void Color_Temperature_Write_Disable(uint32_t instance);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Create(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Delete(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
void Color_Temperature_Cleanup(void);
|
||||
BACNET_STACK_EXPORT
|
||||
void Color_Temperature_Init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -75,6 +75,10 @@
|
||||
#if defined(BACFILE)
|
||||
#include "bacnet/basic/object/bacfile.h"
|
||||
#endif /* defined(BACFILE) */
|
||||
#if (BACNET_PROTOCOL_REVISION >= 24)
|
||||
#include "bacnet/basic/object/color_object.h"
|
||||
#include "bacnet/basic/object/color_temperature.h"
|
||||
#endif
|
||||
|
||||
/* local forward (semi-private) and external prototypes */
|
||||
int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata);
|
||||
@@ -223,6 +227,20 @@ static object_functions_t My_Object_Table[] = {
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
|
||||
#endif
|
||||
#if (BACNET_PROTOCOL_REVISION >= 24)
|
||||
{ OBJECT_COLOR, Color_Init, Color_Count,
|
||||
Color_Index_To_Instance, Color_Valid_Instance,
|
||||
Color_Object_Name, Color_Read_Property,
|
||||
Color_Write_Property, Color_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
|
||||
{ OBJECT_COLOR_TEMPERATURE, Color_Temperature_Init, Color_Temperature_Count,
|
||||
Color_Temperature_Index_To_Instance, Color_Temperature_Valid_Instance,
|
||||
Color_Temperature_Object_Name, Color_Temperature_Read_Property,
|
||||
Color_Temperature_Write_Property, Color_Temperature_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
|
||||
#endif
|
||||
#if defined(BACFILE)
|
||||
{ OBJECT_FILE, bacfile_init, bacfile_count, bacfile_index_to_instance,
|
||||
bacfile_valid_instance, bacfile_object_name, bacfile_read_property,
|
||||
@@ -1312,6 +1330,91 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/** Looks up the common Object and Property, and encodes its Value in an
|
||||
* APDU. Sets the error class and code if request is not appropriate.
|
||||
* @param pObject - object table
|
||||
* @param rpdata [in,out] Structure with the requested Object & Property info
|
||||
* on entry, and APDU message on return.
|
||||
* @return The length of the APDU on success, else BACNET_STATUS_ERROR
|
||||
*/
|
||||
static int Read_Property_Common(
|
||||
struct object_functions *pObject, BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int apdu_len = BACNET_STATUS_ERROR;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
uint8_t *apdu = NULL;
|
||||
#if (BACNET_PROTOCOL_REVISION >= 14)
|
||||
struct special_property_list_t property_list;
|
||||
#endif
|
||||
|
||||
if ((rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
apdu = rpdata->application_data;
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
/* only array properties can have array options */
|
||||
if (rpdata->array_index != BACNET_ARRAY_ALL) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
} else {
|
||||
/* Device Object exception: requested instance
|
||||
may not match our instance if a wildcard */
|
||||
if (rpdata->object_type == OBJECT_DEVICE) {
|
||||
rpdata->object_instance = Object_Instance_Number;
|
||||
}
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], rpdata->object_type, rpdata->object_instance);
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
/* only array properties can have array options */
|
||||
if (rpdata->array_index != BACNET_ARRAY_ALL) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
} else {
|
||||
characterstring_init_ansi(&char_string, "");
|
||||
if (pObject->Object_Name) {
|
||||
(void)pObject->Object_Name(
|
||||
rpdata->object_instance, &char_string);
|
||||
}
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
/* only array properties can have array options */
|
||||
if (rpdata->array_index != BACNET_ARRAY_ALL) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
} else {
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], rpdata->object_type);
|
||||
}
|
||||
break;
|
||||
#if (BACNET_PROTOCOL_REVISION >= 14)
|
||||
case PROP_PROPERTY_LIST:
|
||||
Device_Objects_Property_List(
|
||||
rpdata->object_type, rpdata->object_instance, &property_list);
|
||||
apdu_len = property_list_encode(rpdata,
|
||||
property_list.Required.pList, property_list.Optional.pList,
|
||||
property_list.Proprietary.pList);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
if (pObject->Object_Read_Property) {
|
||||
apdu_len = pObject->Object_Read_Property(rpdata);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/** Looks up the requested Object and Property, and encodes its Value in an
|
||||
* APDU.
|
||||
* @ingroup ObjIntf
|
||||
@@ -1325,9 +1428,6 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int apdu_len = BACNET_STATUS_ERROR;
|
||||
struct object_functions *pObject = NULL;
|
||||
#if (BACNET_PROTOCOL_REVISION >= 14)
|
||||
struct special_property_list_t property_list;
|
||||
#endif
|
||||
|
||||
/* initialize the default return values */
|
||||
rpdata->error_class = ERROR_CLASS_OBJECT;
|
||||
@@ -1336,22 +1436,14 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
if (pObject != NULL) {
|
||||
if (pObject->Object_Valid_Instance &&
|
||||
pObject->Object_Valid_Instance(rpdata->object_instance)) {
|
||||
if (pObject->Object_Read_Property) {
|
||||
#if (BACNET_PROTOCOL_REVISION >= 14)
|
||||
if ((int)rpdata->object_property == PROP_PROPERTY_LIST) {
|
||||
Device_Objects_Property_List(rpdata->object_type,
|
||||
rpdata->object_instance, &property_list);
|
||||
apdu_len = property_list_encode(rpdata,
|
||||
property_list.Required.pList,
|
||||
property_list.Optional.pList,
|
||||
property_list.Proprietary.pList);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
apdu_len = pObject->Object_Read_Property(rpdata);
|
||||
}
|
||||
}
|
||||
apdu_len = Read_Property_Common(pObject, rpdata);
|
||||
} else {
|
||||
rpdata->error_class = ERROR_CLASS_OBJECT;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
}
|
||||
} else {
|
||||
rpdata->error_class = ERROR_CLASS_OBJECT;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
@@ -1816,6 +1908,9 @@ void Device_Init(object_functions_t *object_table)
|
||||
}
|
||||
pObject++;
|
||||
}
|
||||
#if (BACNET_PROTOCOL_REVISION >= 24)
|
||||
Color_Create(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DeviceGetRRInfo(BACNET_READ_RANGE_DATA *pRequest, /* Info on the request */
|
||||
|
||||
@@ -313,7 +313,7 @@ static union {
|
||||
* @param service_choice Service, see SERVICE_CONFIRMED_X enumeration.
|
||||
* @return true if the service uses a Complex Error function
|
||||
*/
|
||||
bool apdu_complex_error(BACNET_CONFIRMED_SERVICE service_choice)
|
||||
bool apdu_complex_error(uint8_t service_choice)
|
||||
{
|
||||
bool status = false;
|
||||
|
||||
@@ -547,8 +547,8 @@ void apdu_handler(BACNET_ADDRESS *src,
|
||||
uint8_t *service_request = NULL;
|
||||
uint16_t service_request_len = 0;
|
||||
int len = 0; /* counts where we are in PDU */
|
||||
uint32_t error_code = 0;
|
||||
uint32_t error_class = 0;
|
||||
BACNET_ERROR_CODE error_code = ERROR_CODE_SUCCESS;
|
||||
BACNET_ERROR_CLASS error_class = ERROR_CLASS_SERVICES;
|
||||
uint8_t reason = 0;
|
||||
bool server = false;
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ extern "C" {
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool apdu_complex_error(
|
||||
BACNET_CONFIRMED_SERVICE service_choice);
|
||||
uint8_t service_choice);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void apdu_set_error_handler(
|
||||
|
||||
@@ -64,8 +64,9 @@ void rp_ack_print_data(BACNET_READ_PROPERTY_DATA *data)
|
||||
/* FIXME: what if application_data_len is bigger than 255? */
|
||||
/* value? need to loop until all of the len is gone... */
|
||||
for (;;) {
|
||||
len = bacapp_decode_application_data(
|
||||
application_data, (unsigned)application_data_len, &value);
|
||||
len = bacapp_decode_known_property(
|
||||
application_data, (unsigned)application_data_len, &value,
|
||||
data->object_type, data->object_property);
|
||||
if (first_value && (len < application_data_len)) {
|
||||
first_value = false;
|
||||
#if PRINT_ENABLED
|
||||
|
||||
@@ -115,17 +115,20 @@ int rpm_ack_decode_service_request(
|
||||
value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE));
|
||||
rpm_property->value = value;
|
||||
while (value && (apdu_len > 0)) {
|
||||
if (IS_CONTEXT_SPECIFIC(*apdu)) {
|
||||
len = bacapp_decode_context_data(apdu, apdu_len, value,
|
||||
rpm_property->propertyIdentifier);
|
||||
} else {
|
||||
len = bacapp_decode_application_data(
|
||||
apdu, apdu_len, value);
|
||||
}
|
||||
len = bacapp_decode_known_property(
|
||||
apdu, (unsigned)apdu_len, value,
|
||||
rpm_object->object_type,
|
||||
rpm_property->propertyIdentifier);
|
||||
/* If len == 0 then it's an empty structure, which is OK. */
|
||||
if (len < 0) {
|
||||
/* problem decoding */
|
||||
/* calling function will free the memory */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM Ack: unable to decode! %s:%s\n",
|
||||
bactext_object_type_name(rpm_object->object_type),
|
||||
bactext_property_name(
|
||||
rpm_property->propertyIdentifier));
|
||||
#endif
|
||||
/* note: caller will free the memory */
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
decoded_len += len;
|
||||
@@ -220,11 +223,17 @@ void rpm_ack_print_data(BACNET_READ_ACCESS_DATA *rpm_data)
|
||||
listOfProperties = rpm_data->listOfProperties;
|
||||
while (listOfProperties) {
|
||||
#if PRINT_ENABLED
|
||||
if (listOfProperties->propertyIdentifier < 512) {
|
||||
if ((listOfProperties->propertyIdentifier < 512) ||
|
||||
(listOfProperties->propertyIdentifier > 4194303)) {
|
||||
/* Enumerated values 0-511 and 4194304+ are reserved
|
||||
for definition by ASHRAE.*/
|
||||
fprintf(stdout, " %s: ",
|
||||
bactext_property_name(
|
||||
listOfProperties->propertyIdentifier));
|
||||
} else {
|
||||
/* Enumerated values 512-4194303 may be used
|
||||
by others subject to the procedures and
|
||||
constraints described in Clause 23. */
|
||||
fprintf(stdout, " proprietary %u: ",
|
||||
(unsigned)listOfProperties->propertyIdentifier);
|
||||
}
|
||||
@@ -357,7 +366,7 @@ void handler_read_property_multiple_ack(uint8_t *service_request,
|
||||
rpm_data = rpm_data_free(rpm_data);
|
||||
}
|
||||
} else {
|
||||
#if 1
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM Ack Malformed! Freeing memory...\n");
|
||||
#endif
|
||||
while (rpm_data) {
|
||||
|
||||
@@ -0,0 +1,486 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2022
|
||||
* @brief computes sRGB to and from from CIE xy and brightness
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Public domain algorithms from Philips and W3C
|
||||
*
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "bacnet/basic/sys/color_rgb.h"
|
||||
|
||||
/**
|
||||
* @brief Clamp a double precision value between two limits
|
||||
* @param d - value to be clamped
|
||||
* @param min - minimum value to clamp within
|
||||
* @param max - maximum value to clamp within
|
||||
* @return value clamped between min and max inclusive
|
||||
*/
|
||||
static double clamp(double d, double min, double max)
|
||||
{
|
||||
if (isnan(d)) {
|
||||
return min;
|
||||
} else {
|
||||
const double t = d < min ? min : d;
|
||||
return t > max ? max : t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert sRGB to CIE xy
|
||||
* @param r - R value of sRGB 0..255
|
||||
* @param g - G value of sRGB 0..255
|
||||
* @param b - B value of sRGB 0..255
|
||||
* @param x_coordinate - return x of CIE xy 0.0..1.0
|
||||
* @param y_coordinate - return y of CIE xy 0.0..1.0
|
||||
* @param brightness - return brightness of the CIE xy color 0..255
|
||||
* @note http://en.wikipedia.org/wiki/Srgb
|
||||
*/
|
||||
void color_rgb_to_xy(uint8_t r, uint8_t g, uint8_t b,
|
||||
float *x_coordinate, float *y_coordinate, uint8_t *brightness)
|
||||
{
|
||||
/* Get the RGB values from your color object
|
||||
and convert them to be between 0 and 1.
|
||||
So the RGB color (255, 0, 100) becomes (1.0, 0.0, 0.39) */
|
||||
float red = (float)r;
|
||||
float green = (float)g;
|
||||
float blue = (float)b;
|
||||
|
||||
red /= 255.0f;
|
||||
green /= 255.0f;
|
||||
blue /= 255.0f;
|
||||
|
||||
/* Apply a gamma correction to the RGB values,
|
||||
which makes the color more vivid and more the
|
||||
like the color displayed on the screen of your device.
|
||||
This gamma correction is also applied to the screen
|
||||
of your computer or phone, thus we need this to create
|
||||
the same color on the light as on screen. */
|
||||
red = (red > 0.04045f) ?
|
||||
pow((red + 0.055f) / (1.0f + 0.055f), 2.4f) :
|
||||
(red / 12.92f);
|
||||
green = (green > 0.04045f) ?
|
||||
pow((green + 0.055f) / (1.0f + 0.055f), 2.4f) :
|
||||
(green / 12.92f);
|
||||
blue = (blue > 0.04045f) ?
|
||||
pow((blue + 0.055f) / (1.0f + 0.055f), 2.4f) :
|
||||
(blue / 12.92f);
|
||||
|
||||
/* Convert the RGB values to XYZ using the
|
||||
Wide RGB D65 conversion formula */
|
||||
float X = red * 0.649926f + green * 0.103455f + blue * 0.197109f;
|
||||
float Y = red * 0.234327f + green * 0.743075f + blue * 0.022598f;
|
||||
float Z = red * 0.0000000f + green * 0.053077f + blue * 1.035763f;
|
||||
|
||||
/* Calculate the xy values from the XYZ values */
|
||||
float x = X / (X + Y + Z);
|
||||
float y = Y / (X + Y + Z);
|
||||
|
||||
x = clamp(x, 0.0f, 1.0f);
|
||||
y = clamp(y, 0.0f, 1.0f);
|
||||
|
||||
/* copy to return values if possible */
|
||||
if (x_coordinate) {
|
||||
*x_coordinate = x;
|
||||
}
|
||||
if (y_coordinate) {
|
||||
*y_coordinate = y;
|
||||
}
|
||||
|
||||
/* Use the Y value of XYZ as brightness
|
||||
The Y value indicates the brightness
|
||||
of the converted color. */
|
||||
Y = Y*255.0f;
|
||||
Y = clamp(Y, 0.0f, 255.0f);
|
||||
if (brightness) {
|
||||
*brightness = (uint8_t)Y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert sRGB from CIE xy and brightness
|
||||
* @param red - return R value of sRGB
|
||||
* @param green - return G value of sRGB
|
||||
* @param blue - return B value of sRGB
|
||||
* @param x_coordinate - x of CIE xy
|
||||
* @param y_coordinate - y of CIE xy
|
||||
* @param brightness - brightness of the CIE xy color
|
||||
* @note http://en.wikipedia.org/wiki/Srgb
|
||||
*/
|
||||
void color_rgb_from_xy(uint8_t *red, uint8_t *green, uint8_t *blue,
|
||||
float x_coordinate, float y_coordinate, uint8_t brightness)
|
||||
{
|
||||
/* Calculate XYZ values */
|
||||
float x = x_coordinate;
|
||||
float y = y_coordinate;
|
||||
float z = 1.0f - x - y;
|
||||
float Y = brightness;
|
||||
Y /= 255.0f;
|
||||
float X = (Y / y) * x;
|
||||
float Z = (Y / y) * z;
|
||||
|
||||
/* Convert to RGB using Wide RGB D65 conversion
|
||||
(THIS IS A D50 conversion currently) */
|
||||
float r = X * 1.4628067f - Y * 0.1840623f - Z * 0.2743606f;
|
||||
float g = -X * 0.5217933f + Y * 1.4472381f + Z * 0.0677227f;
|
||||
float b = X * 0.0349342f - Y * 0.0968930f + Z * 1.2884099f;
|
||||
|
||||
/* Apply reverse gamma correction */
|
||||
r = r <= 0.0031308f ? 12.92f * r :
|
||||
(1.0f + 0.055f) * pow(r, (1.0f / 2.4f)) - 0.055f;
|
||||
g = g <= 0.0031308f ? 12.92f * g :
|
||||
(1.0f + 0.055f) * pow(g, (1.0f / 2.4f)) - 0.055f;
|
||||
b = b <= 0.0031308f ? 12.92f * b :
|
||||
(1.0f + 0.055f) * pow(b, (1.0f / 2.4f)) - 0.055f;
|
||||
|
||||
/* Convert the RGB values to your color object
|
||||
The rgb values from the above formulas are
|
||||
between 0.0 and 1.0. */
|
||||
r = r*255.0f;
|
||||
r = clamp(r, 0.0f, 255.0f);
|
||||
g = g*255;
|
||||
g = clamp(g, 0.0f, 255.0f);
|
||||
b = b*255;
|
||||
b = clamp(b, 0.0f, 255.0f);
|
||||
/* copy to return value if possible */
|
||||
if (red) {
|
||||
*red = (uint8_t)r;
|
||||
}
|
||||
if (green) {
|
||||
*green = (uint8_t)g;
|
||||
}
|
||||
if (blue) {
|
||||
*blue = (uint8_t)b;
|
||||
}
|
||||
}
|
||||
|
||||
/* table for converting RGB to and from ASCII color names */
|
||||
struct css_color_rgb {
|
||||
const char *name;
|
||||
uint8_t red;
|
||||
uint8_t green;
|
||||
uint8_t blue;
|
||||
};
|
||||
static struct css_color_rgb CSS_Color_RGB_Table[] = {
|
||||
{"aliceblue", 240, 248, 255},
|
||||
{"antiquewhite", 250, 235, 215},
|
||||
{"aqua", 0, 255, 255},
|
||||
{"aquamarine", 127, 255, 212},
|
||||
{"azure", 240, 255, 255},
|
||||
{"beige", 245, 245, 220},
|
||||
{"bisque", 255, 228, 196},
|
||||
{"black", 0, 0, 0},
|
||||
{"blanchedalmond", 255, 235, 205},
|
||||
{"blue", 0, 0, 255},
|
||||
{"blueviolet", 138, 43, 226},
|
||||
{"brown", 165, 42, 42},
|
||||
{"burlywood", 222, 184, 135},
|
||||
{"cadetblue", 95, 158, 160},
|
||||
{"chartreuse", 127, 255, 0},
|
||||
{"chocolate", 210, 105, 30},
|
||||
{"coral", 255, 127, 80},
|
||||
{"cornflowerblue", 100, 149, 237},
|
||||
{"cornsilk", 255, 248, 220},
|
||||
{"crimson", 220, 20, 60},
|
||||
{"cyan", 0, 255, 255},
|
||||
{"darkblue", 0, 0, 139},
|
||||
{"darkcyan", 0, 139, 139},
|
||||
{"darkgoldenrod", 184, 134, 11},
|
||||
{"darkgray", 169, 169, 169},
|
||||
{"darkgreen", 0, 100, 0},
|
||||
{"darkgrey", 169, 169, 169},
|
||||
{"darkkhaki", 189, 183, 107},
|
||||
{"darkmagenta", 139, 0, 139},
|
||||
{"darkolivegreen", 85, 107, 47},
|
||||
{"darkorange", 255, 140, 0},
|
||||
{"darkorchid", 153, 50, 204},
|
||||
{"darkred", 139, 0, 0},
|
||||
{"darksalmon", 233, 150, 122},
|
||||
{"darkseagreen", 143, 188, 143},
|
||||
{"darkslateblue", 72, 61, 139},
|
||||
{"darkslategray", 47, 79, 79},
|
||||
{"darkslategrey", 47, 79, 79},
|
||||
{"darkturquoise", 0, 206, 209},
|
||||
{"darkviolet", 148, 0, 211},
|
||||
{"deeppink", 255, 20, 147},
|
||||
{"deepskyblue", 0, 191, 255},
|
||||
{"dimgray", 105, 105, 105},
|
||||
{"dimgrey", 105, 105, 105},
|
||||
{"dodgerblue", 30, 144, 255},
|
||||
{"firebrick", 178, 34, 34},
|
||||
{"floralwhite", 255, 250, 240},
|
||||
{"forestgreen", 34, 139, 34},
|
||||
{"fuchsia", 255, 0, 255},
|
||||
{"gainsboro", 220, 220, 220},
|
||||
{"ghostwhite", 248, 248, 255},
|
||||
{"gold", 255, 215, 0},
|
||||
{"goldenrod", 218, 165, 32},
|
||||
{"gray", 128, 128, 128},
|
||||
{"green", 0, 128, 0},
|
||||
{"greenyellow", 173, 255, 47},
|
||||
{"grey", 128, 128, 128},
|
||||
{"honeydew", 240, 255, 240},
|
||||
{"hotpink", 255, 105, 180},
|
||||
{"indianred", 205, 92, 92},
|
||||
{"indigo", 75, 0, 130},
|
||||
{"ivory", 255, 255, 240},
|
||||
{"khaki", 240, 230, 140},
|
||||
{"lavender", 230, 230, 250},
|
||||
{"lavenderblush", 255, 240, 245},
|
||||
{"lawngreen", 124, 252, 0},
|
||||
{"lemonchiffon", 255, 250, 205},
|
||||
{"lightblue", 173, 216, 230},
|
||||
{"lightcoral", 240, 128, 128},
|
||||
{"lightcyan", 224, 255, 255},
|
||||
{"lightgoldenrodyellow", 250, 250, 210},
|
||||
{"lightgray", 211, 211, 211},
|
||||
{"lightgreen", 144, 238, 144},
|
||||
{"lightgrey", 211, 211, 211},
|
||||
{"lightpink", 255, 182, 193},
|
||||
{"lightsalmon", 255, 160, 122},
|
||||
{"lightseagreen", 32, 178, 170},
|
||||
{"lightskyblue", 135, 206, 250},
|
||||
{"lightslategray", 119, 136, 153},
|
||||
{"lightslategrey", 119, 136, 153},
|
||||
{"lightsteelblue", 176, 196, 222},
|
||||
{"lightyellow", 255, 255, 224},
|
||||
{"lime", 0, 255, 0},
|
||||
{"limegreen", 50, 205, 50},
|
||||
{"linen", 250, 240, 230},
|
||||
{"magenta", 255, 0, 255},
|
||||
{"maroon", 128, 0, 0},
|
||||
{"mediumaquamarine", 102, 205, 170},
|
||||
{"mediumblue", 0, 0, 205},
|
||||
{"mediumorchid", 186, 85, 211},
|
||||
{"mediumpurple", 147, 112, 219},
|
||||
{"mediumseagreen", 60, 179, 113},
|
||||
{"mediumslateblue", 123, 104, 238},
|
||||
{"mediumspringgreen", 0, 250, 154},
|
||||
{"mediumturquoise", 72, 209, 204},
|
||||
{"mediumvioletred", 199, 21, 133},
|
||||
{"midnightblue", 25, 25, 112},
|
||||
{"mintcream", 245, 255, 250},
|
||||
{"mistyrose", 255, 228, 225},
|
||||
{"moccasin", 255, 228, 181},
|
||||
{"navajowhite", 255, 222, 173},
|
||||
{"navy", 0, 0, 128},
|
||||
{"navyblue", 0, 0, 128},
|
||||
{"oldlace", 253, 245, 230},
|
||||
{"olive", 128, 128, 0},
|
||||
{"olivedrab", 107, 142, 35},
|
||||
{"orange", 255, 165, 0},
|
||||
{"orangered", 255, 69, 0},
|
||||
{"orchid", 218, 112, 214},
|
||||
{"palegoldenrod", 238, 232, 170},
|
||||
{"palegreen", 152, 251, 152},
|
||||
{"paleturquoise", 175, 238, 238},
|
||||
{"palevioletred", 219, 112, 147},
|
||||
{"papayawhip", 255, 239, 213},
|
||||
{"peachpuff", 255, 218, 185},
|
||||
{"peru", 205, 133, 63},
|
||||
{"pink", 255, 192, 203},
|
||||
{"plum", 221, 160, 221},
|
||||
{"powderblue", 176, 224, 230},
|
||||
{"purple", 128, 0, 128},
|
||||
{"red", 255, 0, 0},
|
||||
{"rosybrown", 188, 143, 143},
|
||||
{"royalblue", 65, 105, 225},
|
||||
{"saddlebrown", 139, 69, 19},
|
||||
{"salmon", 250, 128, 114},
|
||||
{"sandybrown", 244, 164, 96},
|
||||
{"seagreen", 46, 139, 87},
|
||||
{"seashell", 255, 245, 238},
|
||||
{"sienna", 160, 82, 45},
|
||||
{"silver", 192, 192, 192},
|
||||
{"skyblue", 135, 206, 235},
|
||||
{"slateblue", 106, 90, 205},
|
||||
{"slategray", 112, 128, 144},
|
||||
{"slategrey", 112, 128, 144},
|
||||
{"snow", 255, 250, 250},
|
||||
{"springgreen", 0, 255, 127},
|
||||
{"steelblue", 70, 130, 180},
|
||||
{"tan", 210, 180, 140},
|
||||
{"teal", 0, 128, 128},
|
||||
{"thistle", 216, 191, 216},
|
||||
{"tomato", 255, 99, 71},
|
||||
{"turquoise", 64, 224, 208},
|
||||
{"violet", 238, 130, 238},
|
||||
{"wheat", 245, 222, 179},
|
||||
{"white", 255, 255, 255},
|
||||
{"whitesmoke", 245, 245, 245},
|
||||
{"yellow", 255, 255, 0},
|
||||
{"yellowgreen", 154, 205, 50},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Convert sRGB from CIE xy and brightness
|
||||
* @param red - return R value of sRGB
|
||||
* @param green - return G value of sRGB
|
||||
* @param blue - return B value of sRGB
|
||||
* @return name - CSS color name from W3C or "" if not found
|
||||
* @note Official CSS3 colors from w3.org:
|
||||
* https://www.w3.org/TR/2010/PR-css3-color-20101028/#html4
|
||||
* names do not have spaces
|
||||
*/
|
||||
const char * color_rgb_to_ascii(uint8_t red, uint8_t green, uint8_t blue)
|
||||
{
|
||||
const char * name = "";
|
||||
unsigned index = 0;
|
||||
|
||||
while (CSS_Color_RGB_Table[index].name) {
|
||||
if ((red == CSS_Color_RGB_Table[index].red) &&
|
||||
(green == CSS_Color_RGB_Table[index].green) &&
|
||||
(blue == CSS_Color_RGB_Table[index].blue)) {
|
||||
return CSS_Color_RGB_Table[index].name;
|
||||
}
|
||||
index++;
|
||||
};
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert sRGB from CIE xy and brightness
|
||||
* @param red - return R value of sRGB
|
||||
* @param green - return G value of sRGB
|
||||
* @param blue - return B value of sRGB
|
||||
* @param name - CSS color name from W3C
|
||||
* @return index 0..color_rgb_count(), where color_rgb_count() is not found.
|
||||
*/
|
||||
unsigned color_rgb_from_ascii(uint8_t *red, uint8_t *green, uint8_t *blue,
|
||||
const char *name)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
while (CSS_Color_RGB_Table[index].name) {
|
||||
if (strcmp(CSS_Color_RGB_Table[index].name, name) == 0) {
|
||||
if (red) {
|
||||
*red = CSS_Color_RGB_Table[index].red;
|
||||
}
|
||||
if (green) {
|
||||
*green = CSS_Color_RGB_Table[index].green;
|
||||
}
|
||||
if (blue) {
|
||||
*blue = CSS_Color_RGB_Table[index].blue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
};
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert sRGB from CIE xy and brightness
|
||||
* @param red - return R value of sRGB
|
||||
* @param green - return G value of sRGB
|
||||
* @param blue - return B value of sRGB
|
||||
* @return CSS ASCII color name from W3C or NULL if invalid index
|
||||
*/
|
||||
const char *color_rgb_from_index(unsigned target_index, uint8_t *red, uint8_t *green, uint8_t *blue)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
while (CSS_Color_RGB_Table[index].name) {
|
||||
if (target_index == index) {
|
||||
if (red) {
|
||||
*red = CSS_Color_RGB_Table[index].red;
|
||||
}
|
||||
if (green) {
|
||||
*green = CSS_Color_RGB_Table[index].green;
|
||||
}
|
||||
if (blue) {
|
||||
*blue = CSS_Color_RGB_Table[index].blue;
|
||||
}
|
||||
return CSS_Color_RGB_Table[index].name;
|
||||
}
|
||||
index++;
|
||||
};
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the number of sRGB names from CSS3 defines in W3C
|
||||
* @return the number of defined RGB names
|
||||
*/
|
||||
unsigned color_rgb_count(void)
|
||||
{
|
||||
unsigned count = 0;
|
||||
|
||||
while (CSS_Color_RGB_Table[count].name) {
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an RGB color from a color temperature in Kelvin.
|
||||
* @note This is a rough approximation based on the formula
|
||||
* provided by T. Helland
|
||||
* http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
|
||||
*/
|
||||
void color_rgb_from_temperature(
|
||||
uint16_t temperature_kelvin,
|
||||
uint8_t *r, uint8_t *g, uint8_t *b)
|
||||
{
|
||||
float red = 0, green = 0, blue = 0;
|
||||
|
||||
if (temperature_kelvin < 1000) {
|
||||
temperature_kelvin = 1000;
|
||||
} else if (temperature_kelvin > 40000) {
|
||||
temperature_kelvin = 40000;
|
||||
}
|
||||
temperature_kelvin /= 100;
|
||||
|
||||
/* Calculate Red */
|
||||
if (temperature_kelvin <= 66) {
|
||||
/* Red values below 6600 K are always 255 */
|
||||
red = 255.0;
|
||||
} else {
|
||||
red = temperature_kelvin - 60;
|
||||
red = 329.698727446 * pow(red, -0.1332047592);
|
||||
red = clamp(red, 0.0, 255.0);
|
||||
}
|
||||
/* Calculate Green */
|
||||
if (temperature_kelvin <= 66) {
|
||||
/* Green values below 6600 K */
|
||||
green = temperature_kelvin;
|
||||
green = 99.4708025861 * log(green) - 161.1195681661;
|
||||
} else {
|
||||
green = temperature_kelvin - 60;
|
||||
green = 288.1221695283 * pow(green, -0.0755148492);
|
||||
}
|
||||
green = clamp(green, 0.0, 255.0);
|
||||
/* Calculate Blue */
|
||||
if (temperature_kelvin >= 66) {
|
||||
/* Blue values above 6600 K */
|
||||
blue = 255.0;
|
||||
} else if (temperature_kelvin <= 19) {
|
||||
/* Blue values below 1900 K */
|
||||
blue = 0.0;
|
||||
} else {
|
||||
blue = temperature_kelvin - 10;
|
||||
blue = 138.5177312231 * log(blue) - 305.0447927307;
|
||||
blue = clamp(blue, 0, 255);
|
||||
}
|
||||
if (r) {
|
||||
*r = red;
|
||||
}
|
||||
if (g) {
|
||||
*g = green;
|
||||
}
|
||||
if (b) {
|
||||
*b = blue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2022
|
||||
* @brief API Color sRGB to CIE xy and brightness
|
||||
*/
|
||||
#ifndef COLOR_RGB_H
|
||||
#define COLOR_RGB_H
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacnet_stack_exports.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void color_rgb_to_xy(uint8_t r, uint8_t g, uint8_t b,
|
||||
float *x_coordinate, float *y_coordinate, uint8_t *brightness);
|
||||
BACNET_STACK_EXPORT
|
||||
void color_rgb_from_xy(uint8_t *red, uint8_t *green, uint8_t *blue,
|
||||
float x_coordinate, float y_coordinate, uint8_t brightness);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
const char * color_rgb_to_ascii(uint8_t red, uint8_t green, uint8_t blue);
|
||||
BACNET_STACK_EXPORT
|
||||
unsigned color_rgb_from_ascii(uint8_t *red, uint8_t *green, uint8_t *blue,
|
||||
const char *name);
|
||||
BACNET_STACK_EXPORT
|
||||
const char *color_rgb_from_index(unsigned target_index, uint8_t *red,
|
||||
uint8_t *green, uint8_t *blue);
|
||||
BACNET_STACK_EXPORT
|
||||
unsigned color_rgb_count(void);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void color_rgb_from_temperature(
|
||||
uint16_t temperature_kelvin,
|
||||
uint8_t *r, uint8_t *g, uint8_t *b);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
Reference in New Issue
Block a user