diff --git a/demo/object/mso.c b/demo/object/mso.c index 87759b1..4f92c5a 100644 --- a/demo/object/mso.c +++ b/demo/object/mso.c @@ -1,6 +1,8 @@ /************************************************************************** * -* Copyright (C) 2006 Steve Karg +* Copyright (C) 2012 Steve Karg +* +* Copyright (C) 2013 Patrick Grimm * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -23,39 +25,46 @@ * *********************************************************************/ -/* Multi-state Output Objects - customize for your use */ +/* Multi-state Output Objects */ #include #include #include +#include + #include "bacdef.h" #include "bacdcode.h" #include "bacenum.h" #include "bacapp.h" +#include "bactext.h" +#include "cov.h" #include "config.h" /* the custom stuff */ -#include "rp.h" -#include "wp.h" -#include "mso.h" +#include "device.h" #include "handlers.h" +#include "mso.h" +#include "ucix.h" -#ifndef MAX_MULTISTATE_OUTPUTS -#define MAX_MULTISTATE_OUTPUTS 4 +/* number of demo objects */ +#ifndef MAX_MULTI_STATE_OUTPUTS +//#define MAX_MULTI_STATE_OUTPUTS 65535 +#define MAX_MULTI_STATE_OUTPUTS 512 #endif +unsigned max_multi_state_outputs_int = 512; /* When all the priorities are level null, the present value returns */ /* the Relinquish Default value */ -#define MULTISTATE_RELINQUISH_DEFAULT 0 +#define MULTI_STATE_RELINQUISH_DEFAULT 0 /* NULL part of the array */ -#define MULTISTATE_NULL (255) -/* how many states? 1 to 254 states, 0 is not allowed */ -#define MULTISTATE_NUMBER_OF_STATES (254) -/* Here is our Priority Array.*/ -static uint8_t - Multistate_Output_Level[MAX_MULTISTATE_OUTPUTS][BACNET_MAX_PRIORITY]; -/* Writable out-of-service allows others to play with our Present Value */ -/* without changing the physical output */ -static bool Multistate_Output_Out_Of_Service[MAX_MULTISTATE_OUTPUTS]; +#define MULTI_STATE_NULL (255) + +/* we choose to have a NULL level in our system represented by */ +/* a particular value. When the priorities are not in use, they */ +/* will be relinquished (i.e. set to the NULL level). */ +#define MULTI_STATE_LEVEL_NULL 255 + + +MULTI_STATE_OUTPUT_DESCR MSO_Descr[MAX_MULTI_STATE_OUTPUTS]; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Multistate_Output_Properties_Required[] = { @@ -67,13 +76,23 @@ static const int Multistate_Output_Properties_Required[] = { PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_NUMBER_OF_STATES, - PROP_PRIORITY_ARRAY, - PROP_RELINQUISH_DEFAULT, -1 }; static const int Multistate_Output_Properties_Optional[] = { PROP_DESCRIPTION, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + PROP_STATE_TEXT, +#if defined(INTRINSIC_REPORTING) + PROP_ALARM_VALUES, + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, +#endif -1 }; @@ -81,6 +100,8 @@ static const int Multistate_Output_Properties_Proprietary[] = { -1 }; +struct uci_context *ctx; + void Multistate_Output_Property_Lists( const int **pRequired, const int **pOptional, @@ -95,82 +116,265 @@ void Multistate_Output_Property_Lists( return; } - +/* + * Things to do when starting up the stack for Multistate Output. + * Should be called whenever we reset the device or power it up + */ void Multistate_Output_Init( void) { - unsigned i, j; + unsigned i, j, k, l; static bool initialized = false; - + char name[64]; + const char *uciname; + int ucidisable; + uint8_t ucivalue; + char description[64]; + const char *ucidescription; + const char *ucidescription_default; + const char *idx_c; + char idx_cc[64]; + char *ucistate_default[254]; + int ucistate_n_default = 0; + char *ucialarmstate_default[254]; + int ucialarmstate_n_default = 0; + int ucinc_default; + int ucinc; + int ucievent_default; + int ucievent; + int ucitime_delay_default; + int ucitime_delay; + fprintf(stderr, "Multistate_Output_Init\n"); if (!initialized) { initialized = true; - - /* initialize all the analog output priority arrays to NULL */ - for (i = 0; i < MAX_MULTISTATE_OUTPUTS; i++) { + ctx = ucix_init("bacnet_mo"); + if(!ctx) + fprintf(stderr, "Failed to load config file"); + + ucidescription_default = ucix_get_option(ctx, "bacnet_mo", "default", + "description"); + ucistate_n_default = ucix_get_list(ucistate_default, ctx, + "bacnet_mo", "default", "state"); + ucialarmstate_n_default = ucix_get_list(ucialarmstate_default, ctx, + "bacnet_mo", "default", "alarmstate"); + ucinc_default = ucix_get_option_int(ctx, "bacnet_mo", "default", + "nc", -1); + ucievent_default = ucix_get_option_int(ctx, "bacnet_mo", "default", + "event", -1); + ucitime_delay_default = ucix_get_option_int(ctx, "bacnet_mo", "default", + "time_delay", -1); + + for (i = 0; i < MAX_MULTI_STATE_OUTPUTS; i++) { + char *ucistate[254]; + int ucistate_n = 0; + char *ucialarmstate[254]; + int ucialarmstate_n = 0; + char *state[254]; + int state_n = 0; + char *alarmstate[254]; + int alarmstate_n = 0; + memset(&MSO_Descr[i], 0x00, sizeof(MULTI_STATE_OUTPUT_DESCR)); + /* initialize all the analog output priority arrays to NULL */ for (j = 0; j < BACNET_MAX_PRIORITY; j++) { - Multistate_Output_Level[i][j] = MULTISTATE_NULL; + MSO_Descr[i].Priority_Array[j] = MULTI_STATE_LEVEL_NULL; + } + sprintf(idx_cc,"%d",i); + idx_c = idx_cc; + uciname = ucix_get_option(ctx, "bacnet_mo", idx_c, "name"); + ucidisable = ucix_get_option_int(ctx, "bacnet_mo", idx_c, + "disable", 0); + if ((uciname != 0) && (ucidisable == 0)) { + MSO_Descr[i].Disable=false; + max_multi_state_outputs_int = i+1; + sprintf(name, "%s", uciname); + ucix_string_copy(MSO_Descr[i].Object_Name, + sizeof(MSO_Descr[i].Object_Name), name); + ucidescription = ucix_get_option(ctx, "bacnet_mo", + idx_c, "description"); + if (ucidescription != 0) { + sprintf(description, "%s", ucidescription); + } else if (ucidescription_default != 0) { + sprintf(description, "%s %lu", ucidescription_default, + (unsigned long) i); + } else { + sprintf(description, "MV%lu no uci section configured", + (unsigned long) i); + } + ucix_string_copy(MSO_Descr[i].Object_Description, + sizeof(MSO_Descr[i].Object_Description), description); + + ucivalue = ucix_get_option_int(ctx, "bacnet_mo", idx_c, + "value", 1); + MSO_Descr[i].Priority_Array[15] = ucivalue; + MSO_Descr[i].Relinquish_Default = 1; //TODO read uci + + ucistate_n = ucix_get_list(ucistate, ctx, + "bacnet_mo", idx_c, "state"); + if (ucistate_n == 0) { + state_n = ucistate_n_default; + for (j = 0; j < state_n; j++) { + state[j] = ""; + state[j] = ucistate_default[j]; + } + } else { + state_n = ucistate_n; + for (j = 0; j < state_n; j++) { + state[j] = ucistate[j]; + } + } + MSO_Descr[i].number_of_states = state_n; + + ucialarmstate_n = ucix_get_list(ucialarmstate, ctx, + "bacnet_mo", idx_c, "alarmstate"); + if (ucialarmstate_n == 0) { + alarmstate_n = ucialarmstate_n_default; + for (j = 0; j < alarmstate_n; j++) { + alarmstate[j] = ucialarmstate_default[j]; + } + } else { + alarmstate_n = ucialarmstate_n; + for (j = 0; j < alarmstate_n; j++) { + alarmstate[j] = ucialarmstate[j]; + } + } + l = 0; + for (j = 0; j < state_n; j++) { + if (state[j]) { + sprintf(MSO_Descr[i].State_Text[j], "%s", state[j]); + } else { + sprintf(MSO_Descr[i].State_Text[j], "STATUS: %i", j); + } + for (k = 0; k < alarmstate_n; k++) { + if (strcmp(state[j],alarmstate[k]) == 0) { + MSO_Descr[i].Alarm_Values[l] = j+1; + l++; + } + } + } + MSO_Descr[i].number_of_alarmstates = l; +#if defined(INTRINSIC_REPORTING) + ucinc = ucix_get_option_int(ctx, "bacnet_mo", idx_c, + "nc", ucinc_default); + ucievent = ucix_get_option_int(ctx, "bacnet_mo", idx_c, + "event", ucievent_default); + ucitime_delay = ucix_get_option_int(ctx, "bacnet_mo", idx_c, + "time_delay", ucitime_delay_default); + MSO_Descr[i].Event_State = EVENT_STATE_NORMAL; + /* notification class not connected */ + if (ucinc > -1) MSO_Descr[i].Notification_Class = ucinc; + else MSO_Descr[i].Notification_Class = BACNET_MAX_INSTANCE; + if (ucievent > -1) MSO_Descr[i].Event_Enable = ucievent; + else MSO_Descr[i].Event_Enable = 0; + if (ucitime_delay > -1) MSO_Descr[i].Time_Delay = ucitime_delay; + else MSO_Descr[i].Time_Delay = 0; + /* initialize Event time stamps using wildcards + and set Acked_transitions */ + for (j = 0; j < MAX_BACNET_EVENT_TRANSITION; j++) { + datetime_wildcard_set(&MSO_Descr[i].Event_Time_Stamps[j]); + MSO_Descr[i].Acked_Transitions[j].bIsAcked = true; + } + + /* Set handler for GetEventInformation function */ + handler_get_event_information_set(OBJECT_MULTI_STATE_OUTPUT, + Multistate_Output_Event_Information); + /* Set handler for AcknowledgeAlarm function */ + handler_alarm_ack_set(OBJECT_MULTI_STATE_OUTPUT, + Multistate_Output_Alarm_Ack); + /* Set handler for GetAlarmSummary Service */ + handler_get_alarm_summary_set(OBJECT_MULTI_STATE_OUTPUT, + Multistate_Output_Alarm_Summary); + + MSO_Descr[i].Notify_Type = NOTIFY_ALARM; +#endif + } else { + MSO_Descr[i].Disable=true; } } + fprintf(stderr, "max_multi_state_outputs: %i\n", max_multi_state_outputs_int); + ucix_cleanup(ctx); } - return; } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need validate that the */ -/* given instance exists */ -bool Multistate_Output_Valid_Instance( - uint32_t object_instance) -{ - if (object_instance < MAX_MULTISTATE_OUTPUTS) - return true; - - return false; -} - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then count how many you have */ -unsigned Multistate_Output_Count( - void) -{ - return MAX_MULTISTATE_OUTPUTS; -} - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need to return the instance */ -/* that correlates to the correct index */ -uint32_t Multistate_Output_Index_To_Instance( - unsigned index) -{ - return index; -} - /* we simply have 0-n object instances. Yours might be */ /* more complex, and then you need to return the index */ /* that correlates to the correct instance number */ unsigned Multistate_Output_Instance_To_Index( uint32_t object_instance) { - unsigned index = MAX_MULTISTATE_OUTPUTS; + unsigned index = max_multi_state_outputs_int; - if (object_instance < MAX_MULTISTATE_OUTPUTS) + if (object_instance < max_multi_state_outputs_int) index = object_instance; return index; } -static uint32_t Multistate_Output_Present_Value( +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Multistate_Output_Index_To_Instance( + unsigned index) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + if (CurrentMSO->Disable == false) { + return index; + } else { + return false; + } + } else { + return false; + } +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Multistate_Output_Count( + void) +{ + return max_multi_state_outputs_int; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Multistate_Output_Valid_Instance( uint32_t object_instance) { - uint32_t value = MULTISTATE_RELINQUISH_DEFAULT; - unsigned index = 0; + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned index = 0; /* offset from instance lookup */ + index = Multistate_Output_Instance_To_Index(object_instance); + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + if (CurrentMSO->Disable == false) { + return true; + } else { + return false; + } + } + + return false; +} + +uint32_t Multistate_Output_Present_Value( + uint32_t object_instance) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + uint32_t value = MULTI_STATE_RELINQUISH_DEFAULT; + unsigned index = 0; /* offset from instance lookup */ unsigned i = 0; index = Multistate_Output_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_OUTPUTS) { + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + /* When all the priorities are level null, the present value returns */ + /* the Relinquish Default value */ + value = CurrentMSO->Relinquish_Default; for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - if (Multistate_Output_Level[index][i] != MULTISTATE_NULL) { - value = Multistate_Output_Level[index][i]; + if (CurrentMSO->Priority_Array[i] != MULTI_STATE_LEVEL_NULL) { + value = CurrentMSO->Priority_Array[i]; break; } } @@ -179,26 +383,408 @@ static uint32_t Multistate_Output_Present_Value( return value; } -/* note: the object name must be unique within this device */ -bool Multistate_Output_Object_Name( +bool Multistate_Output_Present_Value_Set( uint32_t object_instance, - BACNET_CHARACTER_STRING * object_name) + uint32_t value, + uint8_t priority) { - static char text_string[32] = ""; /* okay for single thread */ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned index = 0; /* offset from instance lookup */ bool status = false; - if (object_instance < MAX_MULTISTATE_OUTPUTS) { - sprintf(text_string, "MULTISTATE OUTPUT %u", object_instance); - status = characterstring_init_ansi(object_name, text_string); + index = Multistate_Output_Instance_To_Index(object_instance); + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + if ((value > 0) && (value <= CurrentMSO->number_of_states)) { + CurrentMSO->Present_Value = (uint8_t) value; + CurrentMSO->Priority_Array[priority - 1] = (uint8_t) value; + status = true; + //if (priority && (priority <= BACNET_MAX_PRIORITY) && + //(priority != 6 /* reserved */ )) { + //CurrentMSO->Priority_Array[priority - 1] = (uint8_t) value; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + //status = true; + //} + } + } + return status; +} + +bool Multistate_Output_Out_Of_Service( + uint32_t object_instance) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned index = 0; /* offset from instance lookup */ + bool value = false; + + index = Multistate_Output_Instance_To_Index(object_instance); + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + value = CurrentMSO->Out_Of_Service; + } + + return value; +} + +void Multistate_Output_Out_Of_Service_Set( + uint32_t object_instance, + bool value) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned index = 0; + + index = Multistate_Output_Instance_To_Index(object_instance); + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + CurrentMSO->Out_Of_Service = value; + } + + return; +} + +static char *Multistate_Output_Description( + uint32_t object_instance) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned index = 0; /* offset from instance lookup */ + char *pName = NULL; /* return value */ + + index = Multistate_Output_Instance_To_Index(object_instance); + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + pName = CurrentMSO->Object_Description; + } + + return pName; +} + +bool Multistate_Output_Description_Set( + uint32_t object_instance, + char *new_name) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = Multistate_Output_Instance_To_Index(object_instance); + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + status = true; + if (new_name) { + for (i = 0; i < sizeof(CurrentMSO->Object_Description); i++) { + CurrentMSO->Object_Description[i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(CurrentMSO->Object_Description); i++) { + CurrentMSO->Object_Description[i] = 0; + } + } } return status; } +static bool Multistate_Output_Description_Write( + uint32_t object_instance, + BACNET_CHARACTER_STRING *char_string, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned index = 0; /* offset from instance lookup */ + size_t length = 0; + uint8_t encoding = 0; + bool status = false; /* return value */ + const char *idx_c; + char idx_cc[64]; + + index = Multistate_Output_Instance_To_Index(object_instance); + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + length = characterstring_length(char_string); + if (length <= sizeof(CurrentMSO->Object_Description)) { + encoding = characterstring_encoding(char_string); + if (encoding == CHARACTER_UTF8) { + status = characterstring_ansi_copy( + CurrentMSO->Object_Description, + sizeof(CurrentMSO->Object_Description), + char_string); + if (!status) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else { + sprintf(idx_cc,"%d",index); + idx_c = idx_cc; + if(ctx) { + ucix_add_option(ctx, "bacnet_mo", idx_c, + "description", char_string->value); + } else { + fprintf(stderr, + "Failed to open config file bacnet_mo\n"); + } + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } + + return status; +} + +/* note: the object name must be unique within this device */ +bool Multistate_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned index = 0; /* offset from instance lookup */ + bool status = false; + + index = Multistate_Output_Instance_To_Index(object_instance); + + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + status = characterstring_init_ansi(object_name, CurrentMSO->Object_Name); + } + + return status; +} + +/* note: the object name must be unique within this device */ +bool Multistate_Output_Name_Set( + uint32_t object_instance, + char *new_name) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = Multistate_Output_Instance_To_Index(object_instance); + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + status = true; + /* FIXME: check to see if there is a matching name */ + if (new_name) { + for (i = 0; i < sizeof(CurrentMSO->Object_Name); i++) { + CurrentMSO->Object_Name[i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(CurrentMSO->Object_Name); i++) { + CurrentMSO->Object_Name[i] = 0; + } + } + } + + return status; +} + +static bool Multistate_Output_Object_Name_Write( + uint32_t object_instance, + BACNET_CHARACTER_STRING *char_string, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned index = 0; /* offset from instance lookup */ + size_t length = 0; + uint8_t encoding = 0; + bool status = false; /* return value */ + const char *idx_c; + char idx_cc[64]; + + index = Multistate_Output_Instance_To_Index(object_instance); + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + length = characterstring_length(char_string); + if (length <= sizeof(CurrentMSO->Object_Name)) { + encoding = characterstring_encoding(char_string); + if (encoding == CHARACTER_UTF8) { + status = characterstring_ansi_copy( + CurrentMSO->Object_Name, + sizeof(CurrentMSO->Object_Name), + char_string); + if (!status) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else { + sprintf(idx_cc,"%d",index); + idx_c = idx_cc; + if(ctx) { + ucix_add_option(ctx, "bacnet_mo", idx_c, + "name", char_string->value); + } else { + fprintf(stderr, + "Failed to open config file bacnet_mo\n"); + } + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } + + return status; +} + + +static char *Multistate_Output_State_Text( + uint32_t object_instance, + uint32_t state_index) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned index = 0; /* offset from instance lookup */ + char *pName = NULL; /* return value */ + + index = Multistate_Output_Instance_To_Index(object_instance); + if (index < max_multi_state_outputs_int) + CurrentMSO = &MSO_Descr[index]; + else + //return BACNET_STATUS_ERROR; + return pName; + //TODO return ERROR Code + + if ((index < max_multi_state_outputs_int) && (state_index > 0) && + (state_index <= CurrentMSO->number_of_states)) { + state_index--; + pName = CurrentMSO->State_Text[state_index]; + } + + return pName; +} + +bool Multistate_Output_State_Text_Set( + uint32_t object_instance, + uint32_t state_index, + char *new_name) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = Multistate_Output_Instance_To_Index(object_instance); + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + } else + return BACNET_STATUS_ERROR; + + index = Multistate_Output_Instance_To_Index(object_instance); + if ((index < max_multi_state_outputs_int) && (state_index > 0) && + (state_index <= CurrentMSO->number_of_states)) { + state_index--; + status = true; + if (new_name) { + for (i = 0; i < sizeof(CurrentMSO->State_Text[state_index]); i++) { + CurrentMSO->State_Text[state_index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(CurrentMSO->State_Text[state_index]); i++) { + CurrentMSO->State_Text[state_index][i] = 0; + } + } + } + + return status;; +} + +static bool Multistate_Output_State_Text_Write( + uint32_t object_instance, + uint32_t state_index, + BACNET_CHARACTER_STRING * char_string, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned int object_index = 0; /* offset from instance lookup */ + size_t length = 0; + uint8_t encoding = 0; + bool status = false; /* return value */ + const char *idx_c; + char idx_cc[64]; + char ucialarmstate[254][64]; + int ucialarmstate_n = 0; + int j, alarm_value; + + object_index = Multistate_Output_Instance_To_Index(object_instance); + if (object_index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[object_index]; + sprintf(idx_cc,"%d",object_index); + idx_c = idx_cc; + } else + return false; + + if ((object_index < max_multi_state_outputs_int) && (state_index > 0) && + (state_index <= CurrentMSO->number_of_states)) { + state_index--; + length = characterstring_length(char_string); + if (length <= sizeof(CurrentMSO->State_Text[state_index])) { + encoding = characterstring_encoding(char_string); + if (encoding == CHARACTER_UTF8) { + status = + characterstring_ansi_copy(CurrentMSO->State_Text[state_index], + sizeof(CurrentMSO->State_Text[state_index]), char_string); + ucix_set_list(ctx, "bacnet_mo", idx_c, "state", + CurrentMSO->State_Text, CurrentMSO->number_of_states); + ucialarmstate_n = CurrentMSO->number_of_alarmstates; + for (j = 0; j < ucialarmstate_n; j++) { + alarm_value = CurrentMSO->Alarm_Values[j]; + length = sizeof(CurrentMSO->State_Text[state_index]); + sprintf(ucialarmstate[j], "%s", + CurrentMSO->State_Text[alarm_value-1]); + } + ucix_set_list(ctx, "bacnet_mo", idx_c, "alarmstate", + ucialarmstate, ucialarmstate_n); + if (!status) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + + return status; +} + + /* return apdu len, or BACNET_STATUS_ERROR on error */ int Multistate_Output_Read_Property( BACNET_READ_PROPERTY_DATA * rpdata) { + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; int len = 0; int apdu_len = 0; /* return value */ BACNET_BIT_STRING bit_string; @@ -214,74 +800,265 @@ int Multistate_Output_Read_Property( return 0; } apdu = rpdata->application_data; + + object_index = Multistate_Output_Instance_To_Index(rpdata->object_instance); + if (object_index < max_multi_state_outputs_int) + CurrentMSO = &MSO_Descr[object_index]; + else + return BACNET_STATUS_ERROR; + if (CurrentMSO->Disable) + return BACNET_STATUS_ERROR; + switch (rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: apdu_len = encode_application_object_id(&apdu[0], OBJECT_MULTI_STATE_OUTPUT, rpdata->object_instance); break; - /* note: Name and Description don't have to be the same. - You could make Description writable and different */ + case PROP_OBJECT_NAME: - case PROP_DESCRIPTION: Multistate_Output_Object_Name(rpdata->object_instance, &char_string); apdu_len = encode_application_character_string(&apdu[0], &char_string); break; + + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Multistate_Output_Description(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: apdu_len = encode_application_enumerated(&apdu[0], OBJECT_MULTI_STATE_OUTPUT); break; + case PROP_PRESENT_VALUE: present_value = Multistate_Output_Present_Value(rpdata->object_instance); apdu_len = encode_application_unsigned(&apdu[0], present_value); break; + case PROP_STATUS_FLAGS: /* note: see the details in the standard on how to use these */ bitstring_init(&bit_string); +#if defined(INTRINSIC_REPORTING) + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, + CurrentMSO->Event_State ? true : false); +#else bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); +#endif bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + if (Multistate_Output_Out_Of_Service(rpdata->object_instance)) { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + true); + } else { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + false); + } apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; + case PROP_EVENT_STATE: /* note: see the details in the standard on how to use this */ +#if defined(INTRINSIC_REPORTING) + apdu_len = + encode_application_enumerated(&apdu[0], + CurrentMSO->Event_State); +#else apdu_len = encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); +#endif break; + case PROP_OUT_OF_SERVICE: - object_index = - Multistate_Output_Instance_To_Index(rpdata->object_instance); - state = Multistate_Output_Out_Of_Service[object_index]; + state = CurrentMSO->Out_Of_Service; apdu_len = encode_application_boolean(&apdu[0], state); break; + case PROP_PRIORITY_ARRAY: /* Array element zero is the number of elements in the array */ - if (rpdata->array_index == 0) + if (rpdata->array_index == 0) { apdu_len = encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); /* if no index was specified, then try to encode the entire list */ /* into one packet. */ - else if (rpdata->array_index == BACNET_ARRAY_ALL) { - object_index = - Multistate_Output_Instance_To_Index - (rpdata->object_instance); + } else if (rpdata->array_index == BACNET_ARRAY_ALL) { for (i = 0; i < BACNET_MAX_PRIORITY; i++) { /* FIXME: check if we have room before adding it to APDU */ - if (Multistate_Output_Level[object_index][i] == - MULTISTATE_NULL) + if (CurrentMSO->Priority_Array[i] == MULTI_STATE_LEVEL_NULL) { len = encode_application_null(&apdu[apdu_len]); - else { - present_value = - Multistate_Output_Level[object_index][i]; + } else { + present_value = CurrentMSO->Priority_Array[i]; len = encode_application_unsigned(&apdu[apdu_len], present_value); } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) { + apdu_len += len; + } else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (rpdata->array_index <= BACNET_MAX_PRIORITY) { + if (CurrentMSO->Priority_Array[rpdata->array_index - 1] + == MULTI_STATE_LEVEL_NULL) + apdu_len = encode_application_null(&apdu[0]); + else { + present_value = + CurrentMSO->Priority_Array[rpdata->array_index - 1]; + apdu_len = + encode_application_unsigned(&apdu[0],present_value); + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + + case PROP_RELINQUISH_DEFAULT: + present_value = CurrentMSO->Relinquish_Default; + apdu_len = encode_application_unsigned(&apdu[0], present_value); + break; + + case PROP_NUMBER_OF_STATES: + apdu_len = + encode_application_unsigned(&apdu[apdu_len], + CurrentMSO->number_of_states); + break; + + case PROP_STATE_TEXT: + if (rpdata->array_index == 0) { + /* Array element zero is the number of elements in the array */ + apdu_len = + encode_application_unsigned(&apdu[0], + CurrentMSO->number_of_states); + } else if (rpdata->array_index == BACNET_ARRAY_ALL) { + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + for (i = 0; i < CurrentMSO->number_of_states; i++) { + characterstring_init_ansi(&char_string, + CurrentMSO->State_Text[i]); + + /* FIXME: this might go beyond MAX_APDU length! */ + len = + encode_application_character_string(&apdu[apdu_len], + &char_string); + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) { + apdu_len += len; + } else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (rpdata->array_index <= CurrentMSO->number_of_states) { + characterstring_init_ansi(&char_string, + Multistate_Output_State_Text(rpdata->object_instance, + rpdata->array_index)); + apdu_len = + encode_application_character_string(&apdu[0], + &char_string); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + +#if defined(INTRINSIC_REPORTING) + case PROP_ALARM_VALUES: + for (i = 0; i < CurrentMSO->number_of_alarmstates; i++) { + len = + encode_application_unsigned(&apdu[apdu_len], + CurrentMSO->Alarm_Values[i]); + apdu_len += len; + } + break; + + case PROP_TIME_DELAY: + apdu_len = + encode_application_unsigned(&apdu[0], CurrentMSO->Time_Delay); + break; + + case PROP_NOTIFICATION_CLASS: + apdu_len = + encode_application_unsigned(&apdu[0], + CurrentMSO->Notification_Class); + break; + + case PROP_EVENT_ENABLE: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + (CurrentMSO->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true : + false); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + (CurrentMSO->Event_Enable & EVENT_ENABLE_TO_FAULT) ? true : + false); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + (CurrentMSO->Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true : + false); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_ACKED_TRANSITIONS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + CurrentMSO-> + Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + CurrentMSO->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + CurrentMSO->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_NOTIFY_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + CurrentMSO->Notify_Type ? NOTIFY_EVENT : NOTIFY_ALARM); + break; + + case PROP_EVENT_TIME_STAMPS: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], + MAX_BACNET_EVENT_TRANSITION); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 0; i < MAX_BACNET_EVENT_TRANSITION; i++) {; + len = + encode_opening_tag(&apdu[apdu_len], + TIME_STAMP_DATETIME); + len += + encode_application_date(&apdu[apdu_len + len], + &CurrentMSO->Event_Time_Stamps[i].date); + len += + encode_application_time(&apdu[apdu_len + len], + &CurrentMSO->Event_Time_Stamps[i].time); + len += + encode_closing_tag(&apdu[apdu_len + len], + TIME_STAMP_DATETIME); + /* add it if we have room */ if ((apdu_len + len) < MAX_APDU) apdu_len += len; @@ -292,48 +1069,36 @@ int Multistate_Output_Read_Property( break; } } + } else if (rpdata->array_index <= MAX_BACNET_EVENT_TRANSITION) { + apdu_len = + encode_opening_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); + apdu_len += + encode_application_date(&apdu[apdu_len], + &CurrentMSO->Event_Time_Stamps[rpdata->array_index].date); + apdu_len += + encode_application_time(&apdu[apdu_len], + &CurrentMSO->Event_Time_Stamps[rpdata->array_index].time); + apdu_len += + encode_closing_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); } else { - object_index = - Multistate_Output_Instance_To_Index - (rpdata->object_instance); - if (rpdata->array_index <= BACNET_MAX_PRIORITY) { - if (Multistate_Output_Level[object_index] - [rpdata->array_index - 1] == MULTISTATE_NULL) - apdu_len = encode_application_null(&apdu[0]); - else { - present_value = Multistate_Output_Level[object_index] - [rpdata->array_index - 1]; - apdu_len = - encode_application_unsigned(&apdu[0], - present_value); - } - } else { - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; - apdu_len = BACNET_STATUS_ERROR; - } + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; } + break; +#endif - break; - case PROP_RELINQUISH_DEFAULT: - present_value = MULTISTATE_RELINQUISH_DEFAULT; - apdu_len = encode_application_enumerated(&apdu[0], present_value); - break; - case PROP_NUMBER_OF_STATES: - apdu_len = - encode_application_unsigned(&apdu[apdu_len], - MULTISTATE_NUMBER_OF_STATES); - break; - default: + + 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_STATE_TEXT) && - (rpdata->object_property != PROP_PRIORITY_ARRAY) && + 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; @@ -347,12 +1112,22 @@ int Multistate_Output_Read_Property( bool Multistate_Output_Write_Property( BACNET_WRITE_PROPERTY_DATA * wp_data) { + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; bool status = false; /* return value */ unsigned int object_index = 0; + int object_type = 0; + uint32_t object_instance = 0; unsigned int priority = 0; - uint32_t level = 0; + uint8_t level = MULTI_STATE_LEVEL_NULL; int len = 0; + int element_len = 0; BACNET_APPLICATION_DATA_VALUE value; + uint32_t max_states = 0; + uint32_t array_index = 0; + ctx = ucix_init("bacnet_mo"); + const char index_c[32] = ""; + const char *idx_c; + char idx_cc[64]; /* decode the some of the request */ len = @@ -365,39 +1140,63 @@ bool Multistate_Output_Write_Property( wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; return false; } - if ((wp_data->object_property != PROP_STATE_TEXT) && - (wp_data->object_property != PROP_PRIORITY_ARRAY) && - (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; + + object_index = Multistate_Output_Instance_To_Index(wp_data->object_instance); + if (object_index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[object_index]; + sprintf(idx_cc,"%d",object_index); + idx_c = idx_cc; + } else return false; - } + switch (wp_data->object_property) { + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + /* All the object names in a device must be unique */ + if (Device_Valid_Object_Name(&value.type.Character_String, + &object_type, &object_instance)) { + if ((object_type == wp_data->object_type) && + (object_instance == wp_data->object_instance)) { + /* writing same name to same object */ + status = true; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_DUPLICATE_NAME; + } + } else { + status = Multistate_Output_Object_Name_Write( + wp_data->object_instance, + &value.type.Character_String, + &wp_data->error_class, + &wp_data->error_code); + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_DESCRIPTION: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + status = Multistate_Output_Description_Write( + wp_data->object_instance, + &value.type.Character_String, + &wp_data->error_class, + &wp_data->error_code); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; case PROP_PRESENT_VALUE: if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { - priority = wp_data->priority; /* Command priority 6 is reserved for use by Minimum On/Off algorithm and may not be used for other purposes in any object. */ - if (priority && (priority <= BACNET_MAX_PRIORITY) && - (priority != 6 /* reserved */ ) && - (value.type.Unsigned_Int > 0) && - (value.type.Unsigned_Int <= MULTISTATE_NUMBER_OF_STATES)) { - level = value.type.Unsigned_Int; - object_index = - Multistate_Output_Instance_To_Index - (wp_data->object_instance); - priority--; - Multistate_Output_Level[object_index][priority] = - (uint8_t) level; - /* Note: you could set the physical output here if we - are the highest priority. - However, if Out of Service is TRUE, then don't set the - physical output. This comment may apply to the - main loop (i.e. check out of service before changing output) */ + if (Multistate_Output_Present_Value_Set(wp_data->object_instance, + value.type.Unsigned_Int, wp_data->priority)) { status = true; - } else if (priority == 6) { + } else if (wp_data->priority == 6) { /* Command priority 6 is reserved for use by Minimum On/Off algorithm and may not be used for other purposes in any object. */ @@ -412,15 +1211,11 @@ bool Multistate_Output_Write_Property( WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL, &wp_data->error_class, &wp_data->error_code); if (status) { - level = MULTISTATE_NULL; - object_index = - Multistate_Output_Instance_To_Index - (wp_data->object_instance); + level = MULTI_STATE_LEVEL_NULL; priority = wp_data->priority; if (priority && (priority <= BACNET_MAX_PRIORITY)) { priority--; - Multistate_Output_Level[object_index][priority] = - (uint8_t) level; + CurrentMSO->Priority_Array[priority] = level; /* Note: you could set the physical output here to the next highest priority, or to the relinquish default if no priorities are set. @@ -435,39 +1230,656 @@ bool Multistate_Output_Write_Property( } } break; + case PROP_OUT_OF_SERVICE: status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, &wp_data->error_class, &wp_data->error_code); if (status) { - object_index = - Multistate_Output_Instance_To_Index - (wp_data->object_instance); - Multistate_Output_Out_Of_Service[object_index] = - value.type.Boolean; + Multistate_Output_Out_Of_Service_Set(wp_data->object_instance, + value.type.Boolean); } break; + + case PROP_STATE_TEXT: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + if (wp_data->array_index == 0) { + /* Array element zero is the number of + elements in the array. We have a fixed + size array, so we are read-only. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else if (wp_data->array_index == BACNET_ARRAY_ALL) { + max_states = max_multi_state_outputs_int; + array_index = 1; + element_len = len; + do { + if (element_len) { + status = + Multistate_Output_State_Text_Write(wp_data-> + object_instance, array_index, + &value.type.Character_String, + &wp_data->error_class, &wp_data->error_code); + } + max_states--; + array_index++; + if (max_states) { + element_len = + bacapp_decode_application_data(&wp_data-> + application_data[len], + wp_data->application_data_len - len, &value); + if (element_len < 0) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = + ERROR_CODE_VALUE_OUT_OF_RANGE; + break; + } + len += element_len; + } + } while (max_states); + } else { + max_states = max_multi_state_outputs_int; + if (wp_data->array_index <= max_states) { + status = + Multistate_Output_State_Text_Write(wp_data-> + object_instance, wp_data->array_index, + &value.type.Character_String, + &wp_data->error_class, &wp_data->error_code); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + + case PROP_RELINQUISH_DEFAULT: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + CurrentMSO->Relinquish_Default = value.type.Unsigned_Int; + } + break; + +#if defined(INTRINSIC_REPORTING) + case PROP_ALARM_VALUES: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (wp_data->array_index == 0) { + /* Array element zero is the number of + elements in the array. We have a fixed + size array, so we are read-only. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else if (wp_data->array_index == BACNET_ARRAY_ALL) { + int j, alarm_value; + int array_index = 0; + char ucialarmstate[254][64]; + int ucialarmstate_n = 0; + for (j = 0; j < CurrentMSO->number_of_states; j++) { + array_index++; + if (wp_data->application_data[array_index] > 0) { + alarm_value = wp_data->application_data[array_index]; + CurrentMSO->Alarm_Values[j] = alarm_value; + sprintf(ucialarmstate[j], "%s", + CurrentMSO->State_Text[alarm_value-1]); + ucialarmstate_n++; + } + array_index++; + } + CurrentMSO->number_of_alarmstates = ucialarmstate_n; + ucix_set_list(ctx, "bacnet_mo", idx_c, "alarmstate", + ucialarmstate, ucialarmstate_n); + } + } + case PROP_TIME_DELAY: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentMSO->Time_Delay = value.type.Unsigned_Int; + CurrentMSO->Remaining_Time_Delay = CurrentMSO->Time_Delay; + ucix_add_option_int(ctx, "bacnet_mo", index_c, "time_delay", value.type.Unsigned_Int); + } + break; + + case PROP_NOTIFICATION_CLASS: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentMSO->Notification_Class = value.type.Unsigned_Int; + ucix_add_option_int(ctx, "bacnet_mo", index_c, "nc", value.type.Unsigned_Int); + } + break; + + case PROP_EVENT_ENABLE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BIT_STRING, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + if (value.type.Bit_String.bits_used == 3) { + CurrentMSO->Event_Enable = value.type.Bit_String.value[0]; + ucix_add_option_int(ctx, "bacnet_mo", index_c, "event", value.type.Bit_String.value[0]); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + } + } + break; + + case PROP_NOTIFY_TYPE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + switch ((BACNET_NOTIFY_TYPE) value.type.Enumerated) { + case NOTIFY_EVENT: + CurrentMSO->Notify_Type = 1; + break; + case NOTIFY_ALARM: + CurrentMSO->Notify_Type = 0; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + break; + } + } + break; +#endif case PROP_OBJECT_IDENTIFIER: - case PROP_OBJECT_NAME: case PROP_OBJECT_TYPE: case PROP_STATUS_FLAGS: case PROP_EVENT_STATE: case PROP_NUMBER_OF_STATES: - case PROP_DESCRIPTION: case PROP_PRIORITY_ARRAY: - case PROP_RELINQUISH_DEFAULT: wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; break; +#if defined(INTRINSIC_REPORTING) + case PROP_ACKED_TRANSITIONS: + case PROP_EVENT_TIME_STAMPS: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; +#endif default: wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; break; } - + ucix_commit(ctx, "bacnet_mo"); + ucix_cleanup(ctx); return status; } +void Multistate_Output_Intrinsic_Reporting( + uint32_t object_instance) +{ +#if defined(INTRINSIC_REPORTING) + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + BACNET_EVENT_NOTIFICATION_DATA event_data; + BACNET_CHARACTER_STRING msgText; + unsigned int object_index; + uint8_t FromState = 0; + uint8_t ToState; + uint8_t PresentVal = 0; + bool SendNotify = false; + bool tonormal = true; + int i; + + + object_index = Multistate_Output_Instance_To_Index(object_instance); + if (object_index < max_multi_state_outputs_int) + CurrentMSO = &MSO_Descr[object_index]; + else + return; + + if (CurrentMSO->Ack_notify_data.bSendAckNotify) { + /* clean bSendAckNotify flag */ + CurrentMSO->Ack_notify_data.bSendAckNotify = false; + /* copy toState */ + ToState = CurrentMSO->Ack_notify_data.EventState; + +#if PRINT_ENABLED + fprintf(stderr, "Send Acknotification for (%s,%d).\n", + bactext_object_type_name(OBJECT_MULTI_STATE_OUTPUT), object_instance); +#endif /* PRINT_ENABLED */ + + characterstring_init_ansi(&msgText, "AckNotification"); + + /* Notify Type */ + event_data.notifyType = NOTIFY_ACK_NOTIFICATION; + + /* Send EventNotification. */ + SendNotify = true; + } else { + /* actual Present_Value */ + PresentVal = Multistate_Output_Present_Value(object_instance); + FromState = CurrentMSO->Event_State; + switch (CurrentMSO->Event_State) { + case EVENT_STATE_NORMAL: + /* A TO-OFFNORMAL event is generated under these conditions: + (a) the Present_Value must exceed the High_Limit for a minimum + period of time, specified in the Time_Delay property, and + (b) the HighLimitEnable flag must be set in the Limit_Enable property, and + (c) the TO-OFFNORMAL flag must be set in the Event_Enable property. */ + for ( i = 0; i < CurrentMSO->number_of_states; i++) { + if (CurrentMSO->Alarm_Values[i]) { + if ((PresentVal == CurrentMSO->Alarm_Values[i]) && + ((CurrentMSO->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == + EVENT_ENABLE_TO_OFFNORMAL)) { + if (!CurrentMSO->Remaining_Time_Delay) + CurrentMSO->Event_State = EVENT_STATE_FAULT; + else + CurrentMSO->Remaining_Time_Delay--; + break; + } + } + } + /* value of the object is still in the same event state */ + CurrentMSO->Remaining_Time_Delay = CurrentMSO->Time_Delay; + break; + + case EVENT_STATE_FAULT: + for ( i = 0; i < CurrentMSO->number_of_states; i++) { + if (CurrentMSO->Alarm_Values[i]) { + if (PresentVal == CurrentMSO->Alarm_Values[i]) { + tonormal = false; + } + } + } + if ((tonormal) && + ((CurrentMSO->Event_Enable & EVENT_ENABLE_TO_NORMAL) == + EVENT_ENABLE_TO_NORMAL)) { + if (!CurrentMSO->Remaining_Time_Delay) + CurrentMSO->Event_State = EVENT_STATE_NORMAL; + else + CurrentMSO->Remaining_Time_Delay--; + break; + } + /* value of the object is still in the same event state */ + CurrentMSO->Remaining_Time_Delay = CurrentMSO->Time_Delay; + break; + + default: + return; /* shouldn't happen */ + } /* switch (FromState) */ + + ToState = CurrentMSO->Event_State; + + if (FromState != ToState) { + /* Event_State has changed. + Need to fill only the basic parameters of this type of event. + Other parameters will be filled in common function. */ + + switch (ToState) { + case EVENT_STATE_FAULT: + characterstring_init_ansi(&msgText, "Goes to EVENT_STATE_FAULT"); + break; + + case EVENT_STATE_NORMAL: + if (FromState == EVENT_STATE_FAULT) { + characterstring_init_ansi(&msgText, + "Back to normal state from EVENT_STATE_FAULT"); + } + break; + + default: + break; + } /* switch (ToState) */ + +#if PRINT_ENABLED + fprintf(stderr, "Event_State for (%s,%d) goes from %s to %s.\n", + bactext_object_type_name(OBJECT_MULTI_STATE_OUTPUT), + object_instance, + bactext_event_state_name(FromState), + bactext_event_state_name(ToState)); +#endif /* PRINT_ENABLED */ + + /* Notify Type */ + event_data.notifyType = CurrentMSO->Notify_Type; + + /* Send EventNotification. */ + SendNotify = true; + } + } + + + if (SendNotify) { + /* Event Object Identifier */ + event_data.eventObjectIdentifier.type = OBJECT_MULTI_STATE_OUTPUT; + event_data.eventObjectIdentifier.instance = object_instance; + + /* Time Stamp */ + event_data.timeStamp.tag = TIME_STAMP_DATETIME; + Device_getCurrentDateTime(&event_data.timeStamp.value.dateTime); + + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + /* fill Event_Time_Stamps */ + switch (ToState) { + case EVENT_STATE_FAULT: + CurrentMSO->Event_Time_Stamps[TRANSITION_TO_FAULT] = + event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_NORMAL: + CurrentMSO->Event_Time_Stamps[TRANSITION_TO_NORMAL] = + event_data.timeStamp.value.dateTime; + break; + } + } + + /* Notification Class */ + event_data.notificationClass = CurrentMSO->Notification_Class; + + /* Event Type */ + event_data.eventType = EVENT_OUT_OF_RANGE; + + /* Message Text */ + event_data.messageText = &msgText; + + /* Notify Type */ + /* filled before */ + + /* From State */ + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) + event_data.fromState = FromState; + + /* To State */ + event_data.toState = CurrentMSO->Event_State; + + /* Event Values */ + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + /* Value that exceeded a limit. */ + event_data.notificationParams.outOfRange.exceedingValue = + PresentVal; + /* Status_Flags of the referenced object. */ + bitstring_init(&event_data.notificationParams. + outOfRange.statusFlags); + bitstring_set_bit(&event_data.notificationParams. + outOfRange.statusFlags, STATUS_FLAG_IN_ALARM, + CurrentMSO->Event_State ? true : false); + bitstring_set_bit(&event_data.notificationParams. + outOfRange.statusFlags, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&event_data.notificationParams. + outOfRange.statusFlags, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&event_data.notificationParams. + outOfRange.statusFlags, STATUS_FLAG_OUT_OF_SERVICE, + CurrentMSO->Out_Of_Service); + } + + /* add data from notification class */ + Notification_Class_common_reporting_function(&event_data); + + /* Ack required */ + if ((event_data.notifyType != NOTIFY_ACK_NOTIFICATION) && + (event_data.ackRequired == true)) { + switch (event_data.toState) { + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + case EVENT_STATE_OFFNORMAL: + CurrentMSO-> + Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked = + false; + CurrentMSO-> + Acked_Transitions[TRANSITION_TO_OFFNORMAL].Time_Stamp = + event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_FAULT: + CurrentMSO-> + Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked = + false; + CurrentMSO-> + Acked_Transitions[TRANSITION_TO_FAULT].Time_Stamp = + event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_NORMAL: + CurrentMSO-> + Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked = + false; + CurrentMSO-> + Acked_Transitions[TRANSITION_TO_NORMAL].Time_Stamp = + event_data.timeStamp.value.dateTime; + break; + } + } + } +#endif /* defined(INTRINSIC_REPORTING) */ +} + + +#if defined(INTRINSIC_REPORTING) +int Multistate_Output_Event_Information( + unsigned index, + BACNET_GET_EVENT_INFORMATION_DATA * getevent_data) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + bool IsNotAckedTransitions; + bool IsActiveEvent; + int i; + + + /* check index */ + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + /* Event_State not equal to NORMAL */ + IsActiveEvent = (MSO_Descr[index].Event_State != EVENT_STATE_NORMAL); + + /* Acked_Transitions property, which has at least one of the bits + (TO-OFFNORMAL, TO-FAULT, TONORMAL) set to FALSE. */ + IsNotAckedTransitions = + (MSO_Descr[index]. + Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked == + false) | (MSO_Descr[index]. + Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == + false) | (MSO_Descr[index]. + Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == false); + } else + return -1; /* end of list */ + + if ((IsActiveEvent) || (IsNotAckedTransitions)) { + /* Object Identifier */ + getevent_data->objectIdentifier.type = OBJECT_MULTI_STATE_OUTPUT; + getevent_data->objectIdentifier.instance = + Multistate_Output_Index_To_Instance(index); + /* Event State */ + getevent_data->eventState = MSO_Descr[index].Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getevent_data->acknowledgedTransitions); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + MSO_Descr[index]. + Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + MSO_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + MSO_Descr[index].Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + /* Event Time Stamps */ + for (i = 0; i < 3; i++) { + getevent_data->eventTimeStamps[i].tag = TIME_STAMP_DATETIME; + getevent_data->eventTimeStamps[i].value.dateTime = + MSO_Descr[index].Event_Time_Stamps[i]; + } + /* Notify Type */ + getevent_data->notifyType = MSO_Descr[index].Notify_Type; + /* Event Enable */ + bitstring_init(&getevent_data->eventEnable); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_OFFNORMAL, + (MSO_Descr[index].Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true : + false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_FAULT, + (MSO_Descr[index].Event_Enable & EVENT_ENABLE_TO_FAULT) ? true : + false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_NORMAL, + (MSO_Descr[index].Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true : + false); + /* Event Priorities */ + Notification_Class_Get_Priorities(MSO_Descr[index].Notification_Class, + getevent_data->eventPriorities); + + return 1; /* active event */ + } else + return 0; /* no active event at this index */ +} + +int Multistate_Output_Alarm_Ack( + BACNET_ALARM_ACK_DATA * alarmack_data, + BACNET_ERROR_CODE * error_code) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + unsigned int object_index; + + + object_index = + Multistate_Output_Instance_To_Index(alarmack_data-> + eventObjectIdentifier.instance); + + if (object_index < max_multi_state_outputs_int) + CurrentMSO = &MSO_Descr[object_index]; + else { + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return -1; + } + + switch (alarmack_data->eventStateAcked) { + case EVENT_STATE_OFFNORMAL: + if (CurrentMSO-> + Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked == false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare(&CurrentMSO->Acked_Transitions + [TRANSITION_TO_OFFNORMAL].Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + + /* Clean transitions flag. */ + CurrentMSO-> + Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked = true; + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_FAULT: + if (CurrentMSO->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == + false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare(&CurrentMSO->Acked_Transitions + [TRANSITION_TO_NORMAL].Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + + /* Clean transitions flag. */ + CurrentMSO->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked = + true; + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_NORMAL: + if (CurrentMSO->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == + false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare(&CurrentMSO->Acked_Transitions + [TRANSITION_TO_FAULT].Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + + /* Clean transitions flag. */ + CurrentMSO->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked = + true; + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + default: + return -2; + } + + /* Need to send AckNotification. */ + CurrentMSO->Ack_notify_data.bSendAckNotify = true; + CurrentMSO->Ack_notify_data.EventState = alarmack_data->eventStateAcked; + + /* Return OK */ + return 1; +} + +int Multistate_Output_Alarm_Summary( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data) +{ + MULTI_STATE_OUTPUT_DESCR *CurrentMSO; + /* check index */ + if (index < max_multi_state_outputs_int) { + CurrentMSO = &MSO_Descr[index]; + /* Event_State is not equal to NORMAL and + Notify_Type property value is ALARM */ + if ((CurrentMSO->Event_State != EVENT_STATE_NORMAL) && + (CurrentMSO->Notify_Type == NOTIFY_ALARM)) { + /* Object Identifier */ + getalarm_data->objectIdentifier.type = OBJECT_MULTI_STATE_OUTPUT; + getalarm_data->objectIdentifier.instance = + Multistate_Output_Index_To_Instance(index); + /* Alarm State */ + getalarm_data->alarmState = CurrentMSO->Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getalarm_data->acknowledgedTransitions); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + CurrentMSO-> + Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + CurrentMSO->Acked_Transitions[TRANSITION_TO_FAULT]. + bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + CurrentMSO->Acked_Transitions[TRANSITION_TO_NORMAL]. + bIsAcked); + + return 1; /* active alarm */ + } else + return 0; /* no active alarm at this index */ + } else + return -1; /* end of list */ +} +#endif /* defined(INTRINSIC_REPORTING) */ + + #ifdef TEST #include @@ -488,16 +1900,16 @@ bool WPValidateArgType( return false; } -void testMultistateOutput( +void testMultistateInput( Test * pTest) { + BACNET_READ_PROPERTY_DATA rpdata; uint8_t apdu[MAX_APDU] = { 0 }; int len = 0; uint32_t len_value = 0; uint8_t tag_number = 0; uint16_t decoded_type = 0; uint32_t decoded_instance = 0; - BACNET_READ_PROPERTY_DATA rpdata; Multistate_Output_Init(); rpdata.application_data = &apdu[0]; @@ -517,7 +1929,7 @@ void testMultistateOutput( return; } -#ifdef TEST_MULTISTATE_OUTPUT +#ifdef TEST_MULTI_STATE_OUTPUT int main( void) { @@ -536,5 +1948,5 @@ int main( return 0; } -#endif /* TEST_BINARY_INPUT */ +#endif #endif /* TEST */ diff --git a/demo/object/mso.h b/demo/object/mso.h index 28c441d..0717267 100644 --- a/demo/object/mso.h +++ b/demo/object/mso.h @@ -1,6 +1,6 @@ /************************************************************************** * -* Copyright (C) 2005 Steve Karg +* Copyright (C) 2012 Steve Karg * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -22,8 +22,8 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * *********************************************************************/ -#ifndef MSO_H -#define MSO_H +#ifndef MULTI_STATE_OUTPUT_H +#define MULTI_STATE_OUTPUT_H #include #include @@ -31,37 +31,136 @@ #include "bacerror.h" #include "rp.h" #include "wp.h" +#if defined(INTRINSIC_REPORTING) +#include "nc.h" +#include "alarm_ack.h" +#include "getevent.h" +#include "get_alarm_sum.h" +#endif /* INTRINSIC_REPORTING */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ +int max_multi_state_outputs; + + typedef struct multistate_output_descr { + char Object_Name[64]; + char Object_Description[64]; + uint8_t Present_Value; + unsigned Event_State:3; + bool Out_Of_Service; + bool Disable; + uint8_t Units; + char State_Text[254][64]; + int number_of_states; + int number_of_alarmstates; + /* Here is our Priority Array. They are supposed to be Real, but */ + /* we don't have that kind of memory, so we will use a single byte */ + /* and load a Real for returning the value when asked. */ + uint8_t Priority_Array[BACNET_MAX_PRIORITY]; + unsigned Relinquish_Default; +#if defined(INTRINSIC_REPORTING) + uint32_t Time_Delay; + uint32_t Notification_Class; + uint8_t Alarm_Values[254]; + unsigned Event_Enable:3; + unsigned Notify_Type:1; + ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION]; + BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION]; + /* time to generate event notification */ + uint32_t Remaining_Time_Delay; + /* AckNotification informations */ + ACK_NOTIFICATION Ack_notify_data; +#endif /* INTRINSIC_REPORTING */ + } MULTI_STATE_OUTPUT_DESCR; + + void Multistate_Output_Property_Lists( const int **pRequired, const int **pOptional, const int **pProprietary); + bool Multistate_Output_Valid_Instance( uint32_t object_instance); + unsigned Multistate_Output_Count( void); + uint32_t Multistate_Output_Index_To_Instance( unsigned index); + unsigned Multistate_Output_Instance_To_Index( uint32_t object_instance); - bool Multistate_Output_Object_Name( - uint32_t object_instance, - BACNET_CHARACTER_STRING * object_name); - - void Multistate_Output_Init( - void); - int Multistate_Output_Read_Property( BACNET_READ_PROPERTY_DATA * rpdata); bool Multistate_Output_Write_Property( BACNET_WRITE_PROPERTY_DATA * wp_data); + /* optional API */ + bool Multistate_Output_Object_Instance_Add( + uint32_t instance); + + bool Multistate_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + + bool Multistate_Output_Name_Set( + uint32_t object_instance, + char *new_name); + + bool Multistate_Output_Present_Value_Set( + uint32_t object_instance, + uint32_t value, + uint8_t priority); + + uint32_t Multistate_Output_Present_Value( + uint32_t object_instance); + + bool Multistate_Output_Out_Of_Service( + uint32_t object_instance); + + void Multistate_Output_Out_Of_Service_Set( + uint32_t object_instance, + bool value); + + bool Multistate_Output_Description_Set( + uint32_t object_instance, + char *text_string); + + bool Multistate_Output_State_Text_Set( + uint32_t object_instance, + uint32_t state_index, + char *new_name); + + bool Multistate_Output_Max_States_Set( + uint32_t instance, + uint32_t max_states_requested); + + /* note: header of Intrinsic_Reporting function is required + even when INTRINSIC_REPORTING is not defined */ + void Multistate_Output_Intrinsic_Reporting( + uint32_t object_instance); + +#if defined(INTRINSIC_REPORTING) + int Multistate_Output_Event_Information( + unsigned index, + BACNET_GET_EVENT_INFORMATION_DATA * getevent_data); + + int Multistate_Output_Alarm_Ack( + BACNET_ALARM_ACK_DATA * alarmack_data, + BACNET_ERROR_CODE * error_code); + + int Multistate_Output_Alarm_Summary( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data); +#endif + + void Multistate_Output_Init( + void); + #ifdef TEST #include "ctest.h" void testMultistateOutput( diff --git a/demo/object/msv.c b/demo/object/msv.c index f1b4463..dd2311d 100644 --- a/demo/object/msv.c +++ b/demo/object/msv.c @@ -1936,9 +1936,9 @@ int main( Test *pTest; bool rc; - pTest = ct_create("BACnet Multi-state Input", NULL); + pTest = ct_create("BACnet Multi-state Value", NULL); /* individual tests */ - rc = ct_addTestFunction(pTest, testMultistateInput); + rc = ct_addTestFunction(pTest, testMultistateValue); assert(rc); ct_setStream(pTest, stdout);