Created descriptor for analog-value objects,

- writable Relinquish_Default and Units
- added properties required for intrinsic reporting.

Intrinsic reporting for analog-value. First commit. 
Not everything finished (inter alia Time_Delay, Event_Time_Stamps, ACK_Required doesn't work now).

Initialize all objects in Device_Init function.
Added function to read current time.
Defined enum BACNET_LIMIT_ENABLE.
Started cache address.
This commit is contained in:
k001a
2011-07-05 19:44:36 +00:00
parent ccf1335979
commit 19a77ba46e
8 changed files with 919 additions and 101 deletions
+473 -42
View File
@@ -1,6 +1,7 @@
/************************************************************************** /**************************************************************************
* *
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net> * Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the
@@ -28,13 +29,17 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include "bacdef.h" #include "bacdef.h"
#include "bacdcode.h" #include "bacdcode.h"
#include "bacenum.h" #include "bacenum.h"
#include "bacapp.h" #include "bacapp.h"
#include "config.h" /* the custom stuff */ #include "config.h" /* the custom stuff */
#include "wp.h" #include "wp.h"
#include "rp.h"
#include "nc.h"
#include "av.h" #include "av.h"
#include "device.h"
#include "handlers.h" #include "handlers.h"
#ifndef MAX_ANALOG_VALUES #ifndef MAX_ANALOG_VALUES
@@ -45,16 +50,10 @@
/* a particular value. When the priorities are not in use, they */ /* a particular value. When the priorities are not in use, they */
/* will be relinquished (i.e. set to the NULL level). */ /* will be relinquished (i.e. set to the NULL level). */
#define ANALOG_LEVEL_NULL 255 #define ANALOG_LEVEL_NULL 255
/* When all the priorities are level null, the present value returns */
/* the Relinquish Default value */
#define ANALOG_RELINQUISH_DEFAULT 0
/* Here is our Priority Array. They are supposed to be Real, but */ ANALOG_VALUE_DESCR AV_Descr[MAX_ANALOG_VALUES];
/* 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. */
static uint8_t Analog_Value_Level[MAX_ANALOG_VALUES][BACNET_MAX_PRIORITY];
/* Writable out-of-service allows others to play with our Present Value */
/* without changing the physical output */
static bool Analog_Value_Out_Of_Service[MAX_ANALOG_VALUES];
/* These three arrays are used by the ReadPropertyMultiple handler */ /* These three arrays are used by the ReadPropertyMultiple handler */
static const int Analog_Value_Properties_Required[] = { static const int Analog_Value_Properties_Required[] = {
@@ -73,6 +72,18 @@ static const int Analog_Value_Properties_Optional[] = {
PROP_DESCRIPTION, PROP_DESCRIPTION,
PROP_PRIORITY_ARRAY, PROP_PRIORITY_ARRAY,
PROP_RELINQUISH_DEFAULT, PROP_RELINQUISH_DEFAULT,
#if defined(INTRINSIC_REPORTING)
PROP_TIME_DELAY,
PROP_NOTIFICATION_CLASS,
PROP_HIGH_LIMIT,
PROP_LOW_LIMIT,
PROP_DEADBAND,
PROP_LIMIT_ENABLE,
PROP_EVENT_ENABLE,
PROP_ACKED_TRANSITIONS,
PROP_NOTIFY_TYPE,
PROP_EVENT_TIME_STAMPS,
#endif
-1 -1
}; };
@@ -100,11 +111,16 @@ void Analog_Value_Init(
{ {
unsigned i, j; unsigned i, j;
/* initialize all the analog output priority arrays to NULL */
for (i = 0; i < MAX_ANALOG_VALUES; i++) { for (i = 0; i < MAX_ANALOG_VALUES; i++) {
memset(&AV_Descr[i], 0x00, sizeof(ANALOG_VALUE_DESCR));
/* initialize all the analog output priority arrays to NULL */
for (j = 0; j < BACNET_MAX_PRIORITY; j++) { for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
Analog_Value_Level[i][j] = ANALOG_LEVEL_NULL; AV_Descr[i].Priority_Array[j] = ANALOG_LEVEL_NULL;
} }
AV_Descr[i].Units = UNITS_PERCENT;
AV_Descr[i].Event_State = EVENT_STATE_NORMAL;
/* notification class not connected */
AV_Descr[i].Notification_Class = BACNET_MAX_INSTANCE;
} }
return; return;
@@ -158,15 +174,18 @@ bool Analog_Value_Present_Value_Set(
float value, float value,
uint8_t priority) uint8_t priority)
{ {
ANALOG_VALUE_DESCR *CurrentAV;
unsigned index = 0; unsigned index = 0;
bool status = false; bool status = false;
index = Analog_Value_Instance_To_Index(object_instance); index = Analog_Value_Instance_To_Index(object_instance);
if (index < MAX_ANALOG_VALUES) { if (index < MAX_ANALOG_VALUES) {
CurrentAV = &AV_Descr[index];
if (priority && (priority <= BACNET_MAX_PRIORITY) && if (priority && (priority <= BACNET_MAX_PRIORITY) &&
(priority != 6 /* reserved */ ) && (priority != 6 /* reserved */ ) &&
(value >= 0.0) && (value <= 100.0)) { (value >= 0.0) && (value <= 100.0))
Analog_Value_Level[index][priority - 1] = (uint8_t) value; {
CurrentAV->Priority_Array[priority - 1] = (uint8_t) value;
/* Note: you could set the physical output here to the next /* Note: you could set the physical output here to the next
highest priority, or to the relinquish default if no highest priority, or to the relinquish default if no
priorities are set. priorities are set.
@@ -183,15 +202,20 @@ bool Analog_Value_Present_Value_Set(
float Analog_Value_Present_Value( float Analog_Value_Present_Value(
uint32_t object_instance) uint32_t object_instance)
{ {
float value = ANALOG_RELINQUISH_DEFAULT; ANALOG_VALUE_DESCR *CurrentAV;
float value = 0;
unsigned index = 0; unsigned index = 0;
unsigned i = 0; unsigned i = 0;
index = Analog_Value_Instance_To_Index(object_instance); index = Analog_Value_Instance_To_Index(object_instance);
if (index < MAX_ANALOG_VALUES) { if (index < MAX_ANALOG_VALUES) {
CurrentAV = &AV_Descr[index];
/* When all the priorities are level null, the present value returns */
/* the Relinquish Default value */
value = CurrentAV->Relinquish_Default;
for (i = 0; i < BACNET_MAX_PRIORITY; i++) { for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
if (Analog_Value_Level[index][i] != ANALOG_LEVEL_NULL) { if (CurrentAV->Priority_Array[i] != ANALOG_LEVEL_NULL) {
value = Analog_Value_Level[index][i]; value = CurrentAV->Priority_Array[i];
break; break;
} }
} }
@@ -230,32 +254,45 @@ int Analog_Value_Read_Property(
unsigned i = 0; unsigned i = 0;
bool state = false; bool state = false;
uint8_t *apdu = NULL; uint8_t *apdu = NULL;
ANALOG_VALUE_DESCR *CurrentAV;
if ((rpdata == NULL) || (rpdata->application_data == NULL) || if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) { (rpdata->application_data_len == 0)) {
return 0; return 0;
} }
apdu = rpdata->application_data; apdu = rpdata->application_data;
object_index = Analog_Value_Instance_To_Index(rpdata->object_instance);
if (object_index < MAX_ANALOG_VALUES)
CurrentAV = &AV_Descr[object_index];
else
return BACNET_STATUS_ERROR;
switch (rpdata->object_property) { switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER: case PROP_OBJECT_IDENTIFIER:
apdu_len = apdu_len =
encode_application_object_id(&apdu[0], OBJECT_ANALOG_VALUE, encode_application_object_id(&apdu[0], OBJECT_ANALOG_VALUE,
rpdata->object_instance); rpdata->object_instance);
break; break;
case PROP_OBJECT_NAME: case PROP_OBJECT_NAME:
case PROP_DESCRIPTION: case PROP_DESCRIPTION:
Analog_Value_Object_Name(rpdata->object_instance, &char_string); Analog_Value_Object_Name(rpdata->object_instance, &char_string);
apdu_len = apdu_len =
encode_application_character_string(&apdu[0], &char_string); encode_application_character_string(&apdu[0], &char_string);
break; break;
case PROP_OBJECT_TYPE: case PROP_OBJECT_TYPE:
apdu_len = apdu_len =
encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE); encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE);
break; break;
case PROP_PRESENT_VALUE: case PROP_PRESENT_VALUE:
real_value = Analog_Value_Present_Value(rpdata->object_instance); real_value = Analog_Value_Present_Value(rpdata->object_instance);
apdu_len = encode_application_real(&apdu[0], real_value); apdu_len = encode_application_real(&apdu[0], real_value);
break; break;
case PROP_STATUS_FLAGS: case PROP_STATUS_FLAGS:
bitstring_init(&bit_string); bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
@@ -264,19 +301,26 @@ int Analog_Value_Read_Property(
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string); apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break; break;
case PROP_EVENT_STATE: case PROP_EVENT_STATE:
apdu_len = #if defined(INTRINSIC_REPORTING)
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); apdu_len = encode_application_enumerated(&apdu[0],
CurrentAV->Event_State);
#else
apdu_len = encode_application_enumerated(&apdu[0],
EVENT_STATE_NORMAL);
#endif
break; break;
case PROP_OUT_OF_SERVICE: case PROP_OUT_OF_SERVICE:
object_index = state = CurrentAV->Out_Of_Service;
Analog_Value_Instance_To_Index(rpdata->object_instance);
state = Analog_Value_Out_Of_Service[object_index];
apdu_len = encode_application_boolean(&apdu[0], state); apdu_len = encode_application_boolean(&apdu[0], state);
break; break;
case PROP_UNITS: case PROP_UNITS:
apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); apdu_len = encode_application_enumerated(&apdu[0], CurrentAV->Units);
break; break;
case PROP_PRIORITY_ARRAY: case PROP_PRIORITY_ARRAY:
/* Array element zero is the number of elements in the array */ /* Array element zero is the number of elements in the array */
if (rpdata->array_index == 0) if (rpdata->array_index == 0)
@@ -285,15 +329,12 @@ int Analog_Value_Read_Property(
/* if no index was specified, then try to encode the entire list */ /* if no index was specified, then try to encode the entire list */
/* into one packet. */ /* into one packet. */
else if (rpdata->array_index == BACNET_ARRAY_ALL) { else if (rpdata->array_index == BACNET_ARRAY_ALL) {
object_index =
Analog_Value_Instance_To_Index(rpdata->object_instance);
for (i = 0; i < BACNET_MAX_PRIORITY; i++) { for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
/* FIXME: check if we have room before adding it to APDU */ /* FIXME: check if we have room before adding it to APDU */
if (Analog_Value_Level[object_index][i] == if (CurrentAV->Priority_Array[i] == ANALOG_LEVEL_NULL)
ANALOG_LEVEL_NULL)
len = encode_application_null(&apdu[apdu_len]); len = encode_application_null(&apdu[apdu_len]);
else { else {
real_value = Analog_Value_Level[object_index][i]; real_value = CurrentAV->Priority_Array[i];
len = len =
encode_application_real(&apdu[apdu_len], encode_application_real(&apdu[apdu_len],
real_value); real_value);
@@ -309,14 +350,12 @@ int Analog_Value_Read_Property(
} }
} }
} else { } else {
object_index =
Analog_Value_Instance_To_Index(rpdata->object_instance);
if (rpdata->array_index <= BACNET_MAX_PRIORITY) { if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
if (Analog_Value_Level[object_index][rpdata->array_index - if (CurrentAV->Priority_Array[rpdata->array_index - 1]
1] == ANALOG_LEVEL_NULL) == ANALOG_LEVEL_NULL)
apdu_len = encode_application_null(&apdu[0]); apdu_len = encode_application_null(&apdu[0]);
else { else {
real_value = Analog_Value_Level[object_index] real_value = CurrentAV->Priority_Array
[rpdata->array_index - 1]; [rpdata->array_index - 1];
apdu_len = apdu_len =
encode_application_real(&apdu[0], real_value); encode_application_real(&apdu[0], real_value);
@@ -328,10 +367,91 @@ int Analog_Value_Read_Property(
} }
} }
break; break;
case PROP_RELINQUISH_DEFAULT: case PROP_RELINQUISH_DEFAULT:
real_value = ANALOG_RELINQUISH_DEFAULT; real_value = CurrentAV->Relinquish_Default;
apdu_len = encode_application_real(&apdu[0], real_value); apdu_len = encode_application_real(&apdu[0], real_value);
break; break;
#if defined(INTRINSIC_REPORTING)
case PROP_TIME_DELAY:
apdu_len = encode_application_unsigned(&apdu[0], CurrentAV->Time_Delay);
break;
case PROP_NOTIFICATION_CLASS:
apdu_len = encode_application_unsigned(&apdu[0], CurrentAV->Notification_Class);
break;
case PROP_HIGH_LIMIT:
apdu_len = encode_application_real(&apdu[0], CurrentAV->High_Limit);
break;
case PROP_LOW_LIMIT:
apdu_len = encode_application_real(&apdu[0], CurrentAV->Low_Limit);
break;
case PROP_DEADBAND:
apdu_len = encode_application_real(&apdu[0], CurrentAV->Deadband);
break;
case PROP_LIMIT_ENABLE:
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, 0,
(CurrentAV->Limit_Enable & EVENT_LOW_LIMIT_ENABLE ) ? true : false );
bitstring_set_bit(&bit_string, 1,
(CurrentAV->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) ? true : false );
apdu_len = encode_application_bitstring(&apdu[0],&bit_string);
break;
case PROP_EVENT_ENABLE:
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL,
(CurrentAV->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true : false );
bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT,
(CurrentAV->Event_Enable & EVENT_ENABLE_TO_FAULT ) ? true : false );
bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL,
(CurrentAV->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, true);
bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, true);
bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, true);
/// Fixme: finish it
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_NOTIFY_TYPE:
apdu_len = encode_application_enumerated(&apdu[0],
CurrentAV->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], 3);
/* if no index was specified, then try to encode the entire list */
/* into one packet. */
else if (rpdata->array_index == BACNET_ARRAY_ALL) {
/// Fixme:
}
else if (rpdata->array_index <= 3) {
/// Fixme:
}
else {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = BACNET_STATUS_ERROR;
}
break;
#endif
default: default:
rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
@@ -359,6 +479,7 @@ bool Analog_Value_Write_Property(
uint8_t level = ANALOG_LEVEL_NULL; uint8_t level = ANALOG_LEVEL_NULL;
int len = 0; int len = 0;
BACNET_APPLICATION_DATA_VALUE value; BACNET_APPLICATION_DATA_VALUE value;
ANALOG_VALUE_DESCR *CurrentAV;
/* decode the some of the request */ /* decode the some of the request */
len = len =
@@ -371,6 +492,13 @@ bool Analog_Value_Write_Property(
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false; return false;
} }
object_index = Analog_Value_Instance_To_Index(wp_data->object_instance);
if (object_index < MAX_ANALOG_VALUES)
CurrentAV = &AV_Descr[object_index];
else
return false;
switch (wp_data->object_property) { switch (wp_data->object_property) {
case PROP_PRESENT_VALUE: case PROP_PRESENT_VALUE:
if (value.tag == BACNET_APPLICATION_TAG_REAL) { if (value.tag == BACNET_APPLICATION_TAG_REAL) {
@@ -396,13 +524,10 @@ bool Analog_Value_Write_Property(
&wp_data->error_class, &wp_data->error_code); &wp_data->error_class, &wp_data->error_code);
if (status) { if (status) {
level = ANALOG_LEVEL_NULL; level = ANALOG_LEVEL_NULL;
object_index =
Analog_Value_Instance_To_Index(wp_data->
object_instance);
priority = wp_data->priority; priority = wp_data->priority;
if (priority && (priority <= BACNET_MAX_PRIORITY)) { if (priority && (priority <= BACNET_MAX_PRIORITY)) {
priority--; priority--;
Analog_Value_Level[object_index][priority] = level; CurrentAV->Priority_Array[priority] = level;
/* Note: you could set the physical output here to the next /* Note: you could set the physical output here to the next
highest priority, or to the relinquish default if no highest priority, or to the relinquish default if no
priorities are set. priorities are set.
@@ -417,19 +542,140 @@ bool Analog_Value_Write_Property(
} }
} }
break; break;
case PROP_OUT_OF_SERVICE: case PROP_OUT_OF_SERVICE:
status = status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
&wp_data->error_class, &wp_data->error_code); &wp_data->error_class, &wp_data->error_code);
if (status) { if (status) {
object_index = CurrentAV->Out_Of_Service = value.type.Boolean;
Analog_Value_Instance_To_Index(wp_data->object_instance);
Analog_Value_Out_Of_Service[object_index] = value.type.Boolean;
} }
break; break;
case PROP_UNITS:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
&wp_data->error_class, &wp_data->error_code);
if (status) {
CurrentAV->Units = value.type.Enumerated;
}
break;
case PROP_RELINQUISH_DEFAULT:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL,
&wp_data->error_class, &wp_data->error_code);
if (status) {
CurrentAV->Relinquish_Default = value.type.Real;
}
break;
#if defined(INTRINSIC_REPORTING)
case PROP_TIME_DELAY:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
&wp_data->error_class, &wp_data->error_code);
if (status) {
CurrentAV->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) {
CurrentAV->Notification_Class = value.type.Unsigned_Int;
}
break;
case PROP_HIGH_LIMIT:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL,
&wp_data->error_class, &wp_data->error_code);
if (status) {
CurrentAV->High_Limit = value.type.Real;
}
break;
case PROP_LOW_LIMIT:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL,
&wp_data->error_class, &wp_data->error_code);
if (status) {
CurrentAV->Low_Limit = value.type.Real;
}
break;
case PROP_DEADBAND:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL,
&wp_data->error_class, &wp_data->error_code);
if (status) {
CurrentAV->Deadband = value.type.Real;
}
break;
case PROP_LIMIT_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 == 2) {
CurrentAV->Limit_Enable = 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_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) {
CurrentAV->Event_Enable = 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) {
if(value.type.Bit_String.bits_used > NOTIFY_EVENT) {
CurrentAV->Event_Enable = value.type.Enumerated;
}
else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
status = false;
}
}
break;
#endif
default: default:
wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break; break;
} }
@@ -437,6 +683,191 @@ bool Analog_Value_Write_Property(
} }
void Analog_Value_Intrinsic_Reporting(uint32_t object_instance)
{
BACNET_EVENT_NOTIFICATION_DATA event_data;
BACNET_CHARACTER_STRING msgText;
ANALOG_VALUE_DESCR *CurrentAV;
unsigned int object_index;
uint8_t FromState;
uint8_t ToState;
float ExceededLimit;
float PresentVal;
object_index = Analog_Value_Instance_To_Index(object_instance);
if (object_index < MAX_ANALOG_VALUES)
CurrentAV = &AV_Descr[object_index];
else
return;
/* check limits */
if (!CurrentAV->Limit_Enable)
return; /* limits are not configured */
/* actual Present_Value */
PresentVal = Analog_Value_Present_Value(object_instance);
FromState = CurrentAV->Event_State;
switch (CurrentAV->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. */
if ((PresentVal > CurrentAV->High_Limit) &&
/// (Deadband >= CurrentAV->Time_Delay) &&
((CurrentAV->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == EVENT_HIGH_LIMIT_ENABLE) &&
((CurrentAV->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == EVENT_ENABLE_TO_OFFNORMAL))
{
CurrentAV->Event_State = EVENT_STATE_HIGH_LIMIT;
break;
}
/* A TO-OFFNORMAL event is generated under these conditions:
(a) the Present_Value must exceed the Low_Limit plus the Deadband
for a minimum period of time, specified in the Time_Delay property, and
(b) the LowLimitEnable flag must be set in the Limit_Enable property, and
(c) the TO-NORMAL flag must be set in the Event_Enable property. */
if ((PresentVal < CurrentAV->Low_Limit) &&
/// (Deadband >= CurrentAV->Time_Delay) &&
((CurrentAV->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == EVENT_LOW_LIMIT_ENABLE) &&
((CurrentAV->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == EVENT_ENABLE_TO_OFFNORMAL))
{
CurrentAV->Event_State = EVENT_STATE_LOW_LIMIT;
break;
}
break;
case EVENT_STATE_HIGH_LIMIT:
/* Once exceeded, the Present_Value must fall below the High_Limit minus
the Deadband before a TO-NORMAL event is generated under these conditions:
(a) the Present_Value must fall below the High_Limit minus the Deadband
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-NORMAL flag must be set in the Event_Enable property. */
if ((PresentVal < CurrentAV->High_Limit - CurrentAV->Deadband) &&
/// (Deadband >= CurrentAV->Time_Delay) &&
((CurrentAV->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == EVENT_HIGH_LIMIT_ENABLE) &&
((CurrentAV->Event_Enable & EVENT_ENABLE_TO_NORMAL) == EVENT_ENABLE_TO_NORMAL))
{
CurrentAV->Event_State = EVENT_STATE_NORMAL;
}
break;
case EVENT_STATE_LOW_LIMIT:
/* Once the Present_Value has fallen below the Low_Limit,
the Present_Value must exceed the Low_Limit plus the Deadband
before a TO-NORMAL event is generated under these conditions:
(a) the Present_Value must exceed the Low_Limit plus the Deadband
for a minimum period of time, specified in the Time_Delay property, and
(b) the LowLimitEnable flag must be set in the Limit_Enable property, and
(c) the TO-NORMAL flag must be set in the Event_Enable property. */
if ((PresentVal > CurrentAV->Low_Limit + CurrentAV->Deadband) &&
/// (Deadband >= CurrentAV->Time_Delay) &&
((CurrentAV->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == EVENT_LOW_LIMIT_ENABLE) &&
((CurrentAV->Event_Enable & EVENT_ENABLE_TO_NORMAL) == EVENT_ENABLE_TO_NORMAL))
{
CurrentAV->Event_State = EVENT_STATE_NORMAL;
}
break;
default:
return; /* shouldn't happen */
} /* switch (FromState) */
ToState = CurrentAV->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_HIGH_LIMIT:
ExceededLimit = CurrentAV->High_Limit;
characterstring_init_ansi(&msgText,
"Goes to high limit");
break;
case EVENT_STATE_LOW_LIMIT:
ExceededLimit = CurrentAV->Low_Limit;
characterstring_init_ansi(&msgText,
"Goes to low limit");
break;
case EVENT_STATE_NORMAL:
if(FromState == EVENT_STATE_HIGH_LIMIT) {
ExceededLimit = CurrentAV->High_Limit;
characterstring_init_ansi(&msgText,
"Back to normal state from high limit");
}
else {
ExceededLimit = CurrentAV->Low_Limit;
characterstring_init_ansi(&msgText,
"Back to normal state from low limit");
}
break;
default:
ExceededLimit = 0;
break;
} /* switch (ToState) */
/* Event Object Identifier */
event_data.eventObjectIdentifier.type = OBJECT_ANALOG_VALUE;
event_data.eventObjectIdentifier.instance = object_instance;
/* Time Stamp*/
event_data.timeStamp.tag = TIME_STAMP_DATETIME;
Device_getCurrentDateTime(&event_data.timeStamp.value.dateTime);
/* Notification Class */
event_data.notificationClass = CurrentAV->Notification_Class;
/* Event Type */
event_data.eventType = EVENT_OUT_OF_RANGE;
/* Message Text */
event_data.messageText = &msgText;
/* Notify Type */
event_data.notifyType = CurrentAV->Notify_Type;
/* From State */
event_data.fromState = FromState;
/* To State */
event_data.toState = CurrentAV->Event_State;
/* Event Values */
event_data.notificationParams.outOfRange.exceedingValue = PresentVal;
bitstring_init(&event_data.notificationParams.outOfRange.statusFlags);
bitstring_set_bit(&event_data.notificationParams.outOfRange.statusFlags,
STATUS_FLAG_IN_ALARM, CurrentAV->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, CurrentAV->Out_Of_Service);
event_data.notificationParams.outOfRange.deadband = CurrentAV->Deadband;
event_data.notificationParams.outOfRange.exceededLimit = ExceededLimit;
/* add data from notification class */
Notification_Class_common_reporting_function(&event_data);
}
}
#ifdef TEST #ifdef TEST
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
+28
View File
@@ -1,6 +1,7 @@
/************************************************************************** /**************************************************************************
* *
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net> * Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the
@@ -35,6 +36,30 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
typedef struct analog_value_descr {
uint8_t Event_State : 3;
bool Out_Of_Service;
uint8_t Units;
/* 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];
float Relinquish_Default;
#if defined(INTRINSIC_REPORTING)
uint32_t Time_Delay;
uint32_t Notification_Class;
float High_Limit;
float Low_Limit;
float Deadband;
uint8_t Limit_Enable : 2;
uint8_t Event_Enable : 3;
uint8_t Acked_Transitions : 3;
uint8_t Notify_Type : 1;
#endif
} ANALOG_VALUE_DESCR;
void Analog_Value_Property_Lists( void Analog_Value_Property_Lists(
const int **pRequired, const int **pRequired,
const int **pOptional, const int **pOptional,
@@ -65,6 +90,9 @@ extern "C" {
float Analog_Value_Present_Value( float Analog_Value_Present_Value(
uint32_t object_instance); uint32_t object_instance);
void Analog_Value_Intrinsic_Reporting(
uint32_t object_instance);
void Analog_Value_Init( void Analog_Value_Init(
void); void);
+130 -5
View File
@@ -46,6 +46,24 @@
#include "address.h" #include "address.h"
/* os specfic includes */ /* os specfic includes */
#include "timer.h" #include "timer.h"
/* include the device object */
#include "device.h"
#include "ai.h"
#include "ao.h"
#include "av.h"
#include "bi.h"
#include "bo.h"
#include "bv.h"
#include "lc.h"
#include "lsp.h"
#include "mso.h"
#include "ms-input.h"
#include "nc.h"
#include "trendlog.h"
#if defined(BACFILE)
#include "bacfile.h"
#endif
#if defined(__BORLANDC__) #if defined(__BORLANDC__)
/* seems to not be defined in time.h as specified by The Open Group */ /* seems to not be defined in time.h as specified by The Open Group */
@@ -63,9 +81,77 @@ extern int Routed_Device_Read_Property_Local(
extern bool Routed_Device_Write_Property_Local( extern bool Routed_Device_Write_Property_Local(
BACNET_WRITE_PROPERTY_DATA * wp_data); BACNET_WRITE_PROPERTY_DATA * wp_data);
/* Defined in user application;
object functions for all included BACnet objects */ static object_functions_t Object_Table[] = {
static object_functions_t *Object_Table; {
OBJECT_DEVICE, NULL, Device_Count, /* don't init - recursive! */
Device_Index_To_Instance, Device_Valid_Object_Instance_Number,
Device_Object_Name, Device_Read_Property_Local,
Device_Write_Property_Local, Device_Property_Lists,
NULL, NULL, NULL, NULL}, {
OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count,
Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance,
Analog_Input_Object_Name, Analog_Input_Read_Property,
NULL, Analog_Input_Property_Lists,
NULL, NULL, NULL, NULL}, {
OBJECT_ANALOG_VALUE, Analog_Value_Init, Analog_Value_Count,
Analog_Value_Index_To_Instance, Analog_Value_Valid_Instance,
Analog_Value_Object_Name, Analog_Value_Read_Property,
Analog_Value_Write_Property, Analog_Value_Property_Lists,
NULL, NULL, NULL, Analog_Value_Intrinsic_Reporting}, {
OBJECT_BINARY_INPUT, Binary_Input_Init, Binary_Input_Count,
Binary_Input_Index_To_Instance, Binary_Input_Valid_Instance,
Binary_Input_Object_Name, Binary_Input_Read_Property,
NULL, Binary_Input_Property_Lists,
NULL, NULL, NULL, NULL}, {
OBJECT_BINARY_VALUE, Binary_Value_Init, Binary_Value_Count,
Binary_Value_Index_To_Instance, Binary_Value_Valid_Instance,
Binary_Value_Object_Name, Binary_Value_Read_Property,
Binary_Value_Write_Property, Binary_Value_Property_Lists,
NULL, NULL, NULL, NULL}, {
#if defined(INTRINSIC_REPORTING)
OBJECT_NOTIFICATION_CLASS, Notification_Class_Init, Notification_Class_Count,
Notification_Class_Index_To_Instance, Notification_Class_Valid_Instance,
Notification_Class_Object_Name, Notification_Class_Read_Property,
Notification_Class_Write_Property, Notification_Class_Property_Lists,
NULL, NULL, NULL, NULL}, {
#endif
OBJECT_LIFE_SAFETY_POINT, Life_Safety_Point_Init, Life_Safety_Point_Count,
Life_Safety_Point_Index_To_Instance, Life_Safety_Point_Valid_Instance,
Life_Safety_Point_Object_Name, Life_Safety_Point_Read_Property,
Life_Safety_Point_Write_Property, Life_Safety_Point_Property_Lists,
NULL, NULL, NULL, NULL}, {
OBJECT_LOAD_CONTROL, Load_Control_Init, Load_Control_Count,
Load_Control_Index_To_Instance, Load_Control_Valid_Instance,
Load_Control_Object_Name, Load_Control_Read_Property,
Load_Control_Write_Property, Load_Control_Property_Lists,
NULL, NULL, NULL}, {
OBJECT_MULTI_STATE_OUTPUT, Multistate_Output_Init, Multistate_Output_Count,
Multistate_Output_Index_To_Instance, Multistate_Output_Valid_Instance,
Multistate_Output_Object_Name, Multistate_Output_Read_Property,
Multistate_Output_Write_Property, Multistate_Output_Property_Lists,
NULL, NULL, NULL, NULL}, {
OBJECT_MULTI_STATE_INPUT, Multistate_Input_Init, Multistate_Input_Count,
Multistate_Input_Index_To_Instance, Multistate_Input_Valid_Instance,
Multistate_Input_Object_Name, Multistate_Input_Read_Property,
Multistate_Input_Write_Property, Multistate_Input_Property_Lists,
NULL, NULL, NULL, NULL}, {
OBJECT_TRENDLOG, Trend_Log_Init, Trend_Log_Count,
Trend_Log_Index_To_Instance, Trend_Log_Valid_Instance,
Trend_Log_Object_Name, Trend_Log_Read_Property,
Trend_Log_Write_Property, Trend_Log_Property_Lists,
TrendLogGetRRInfo, NULL, NULL, NULL}, {
#if defined(BACFILE)
OBJECT_FILE, bacfile_init, bacfile_count,
bacfile_index_to_instance, bacfile_valid_instance,
bacfile_object_name, bacfile_read_property,
bacfile_write_property, BACfile_Property_Lists,
NULL, NULL, NULL, NULL}, {
#endif
MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL}
};
/** Glue function to let the Device object, when called by a handler, /** Glue function to let the Device object, when called by a handler,
* lookup which Object type needs to be invoked. * lookup which Object type needs to be invoked.
@@ -872,6 +958,14 @@ int tm_isdst Daylight Savings flag.
} }
} }
void Device_getCurrentDateTime(BACNET_DATE_TIME * DateTime)
{
Update_Current_Time();
DateTime->date = Local_Date;
DateTime->time = Local_Time;
}
/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or /* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or
BACNET_STATUS_ABORT for abort message */ BACNET_STATUS_ABORT for abort message */
int Device_Read_Property_Local( int Device_Read_Property_Local(
@@ -1390,6 +1484,36 @@ bool Device_Encode_Value_List(
return (status); return (status);
} }
void Device_local_reporting(uint32_t milliseconds)
{
struct object_functions *pObject;
uint32_t objects_count;
uint32_t object_instance;
int object_type;
uint32_t idx;
objects_count = Device_Object_List_Count();
/* loop for all objects */
for (idx = 0; idx < objects_count; idx++)
{
Device_Object_List_Identifier(idx, &object_type,
&object_instance);
pObject = Device_Objects_Find_Functions(object_type);
if (pObject != NULL) {
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(object_instance)) {
if (pObject->Object_Intrinsic_Reporting) {
pObject->Object_Intrinsic_Reporting(
object_instance);
}
}
}
}
}
/** Looks up the requested Object to see if the functionality is supported. /** Looks up the requested Object to see if the functionality is supported.
* @ingroup ObjHelpers * @ingroup ObjHelpers
* @param [in] The object type to be looked up. * @param [in] The object type to be looked up.
@@ -1425,8 +1549,9 @@ void Device_Init(
struct object_functions *pObject = NULL; struct object_functions *pObject = NULL;
characterstring_init_ansi(&My_Object_Name, "SimpleServer"); characterstring_init_ansi(&My_Object_Name, "SimpleServer");
/* call all the child objects initialization functions */ /* we don't use the object table passed in
Object_Table = object_table; since there is extra stuff we don't need in there. */
(void)object_table;
pObject = &Object_Table[0]; pObject = &Object_Table[0];
while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
if (pObject->Object_Init) { if (pObject->Object_Init) {
+14
View File
@@ -112,6 +112,15 @@ typedef bool(
uint32_t object_instance, uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list); BACNET_PROPERTY_VALUE * value_list);
/** Intrinsic Reporting funcionality.
* @ingroup ObjHelpers
* @param [in] Object instance.
*/
typedef void(
*object_intrinsic_reporting_function) (
uint32_t object_instance);
/** Defines the group of object helper functions for any supported Object. /** Defines the group of object helper functions for any supported Object.
* @ingroup ObjHelpers * @ingroup ObjHelpers
* Each Object must provide some implementation of each of these helpers * Each Object must provide some implementation of each of these helpers
@@ -134,6 +143,7 @@ typedef struct object_functions {
rr_info_function Object_RR_Info; rr_info_function Object_RR_Info;
object_iterate_function Object_Iterator; object_iterate_function Object_Iterator;
object_value_list_function Object_Value_List; object_value_list_function Object_Value_List;
object_intrinsic_reporting_function Object_Intrinsic_Reporting;
} object_functions_t; } object_functions_t;
/* String Lengths - excluding any nul terminator */ /* String Lengths - excluding any nul terminator */
@@ -203,6 +213,8 @@ extern "C" {
rr_info_function Device_Objects_RR_Info( rr_info_function Device_Objects_RR_Info(
BACNET_OBJECT_TYPE object_type); BACNET_OBJECT_TYPE object_type);
void Device_getCurrentDateTime(BACNET_DATE_TIME * DateTime);
void Device_Property_Lists( void Device_Property_Lists(
const int **pRequired, const int **pRequired,
const int **pOptional, const int **pOptional,
@@ -325,6 +337,8 @@ extern "C" {
bool Device_Write_Property_Local( bool Device_Write_Property_Local(
BACNET_WRITE_PROPERTY_DATA * wp_data); BACNET_WRITE_PROPERTY_DATA * wp_data);
void Device_local_reporting(uint32_t milliseconds);
/* Prototypes for Routing functionality in the Device Object. /* Prototypes for Routing functionality in the Device Object.
* Enable by defining BAC_ROUTING in config.h and including gw_device.c * Enable by defining BAC_ROUTING in config.h and including gw_device.c
* in the build (lib/Makefile). * in the build (lib/Makefile).
+227 -26
View File
@@ -25,20 +25,22 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include "address.h"
#include "bacdef.h" #include "bacdef.h"
#include "bacdcode.h" #include "bacdcode.h"
#include "bacenum.h" #include "bacenum.h"
#include "bacapp.h" #include "bacapp.h"
#include "client.h"
#include "config.h" #include "config.h"
#include "device.h" #include "device.h"
#include "event.h"
#include "handlers.h" #include "handlers.h"
#include "txbuf.h"
#include "wp.h" #include "wp.h"
#include "nc.h" #include "nc.h"
#include "datalink.h" //for send who is
#include "whois.h"
#ifndef MAX_NOTIFICATION_CLASSES #ifndef MAX_NOTIFICATION_CLASSES
#define MAX_NOTIFICATION_CLASSES 2 #define MAX_NOTIFICATION_CLASSES 2
@@ -89,15 +91,15 @@ void Notification_Class_Init(void)
{ {
uint8_t NotifyIdx = 0; uint8_t NotifyIdx = 0;
for(NotifyIdx = 0; NotifyIdx < MAX_NOTIFICATION_CLASSES; NotifyIdx++) for (NotifyIdx = 0; NotifyIdx < MAX_NOTIFICATION_CLASSES; NotifyIdx++)
{ {
/* init with zeros */ /* init with zeros */
memset(&NC_Info[NotifyIdx], 0x00, sizeof(NOTIFICATION_CLASS_INFO)); memset(&NC_Info[NotifyIdx], 0x00, sizeof(NOTIFICATION_CLASS_INFO));
/* set the basic parameters*/ /* set the basic parameters */
NC_Info[NotifyIdx].Ack_Required = 0; NC_Info[NotifyIdx].Ack_Required = 0;
NC_Info[NotifyIdx].Priority[0] = 255; // The lowest priority for Normal message. NC_Info[NotifyIdx].Priority[0] = 255; /* The lowest priority for Normal message. */
NC_Info[NotifyIdx].Priority[1] = 255; // The lowest priority for Normal message. NC_Info[NotifyIdx].Priority[1] = 255; /* The lowest priority for Normal message. */
NC_Info[NotifyIdx].Priority[2] = 255; // The lowest priority for Normal message. NC_Info[NotifyIdx].Priority[2] = 255; /* The lowest priority for Normal message. */
} }
/* init with special values for tests */ /* init with special values for tests */
@@ -111,7 +113,7 @@ void Notification_Class_Init(void)
// NC_Info[0].Recipient_List[0].FromTime.sec = 10; // NC_Info[0].Recipient_List[0].FromTime.sec = 10;
// NC_Info[0].Recipient_List[0].FromTime.hundredths = 0; // NC_Info[0].Recipient_List[0].FromTime.hundredths = 0;
// NC_Info[0].Recipient_List[0].ToTime.hour = 23; // NC_Info[0].Recipient_List[0].ToTime.hour = 23;
// NC_Info[0].Recipient_List[0].ToTime.min = 0; // NC_Info[0].Recipient_List[0].ToTime.min = 48;
// NC_Info[0].Recipient_List[0].ToTime.sec = 59; // NC_Info[0].Recipient_List[0].ToTime.sec = 59;
// NC_Info[0].Recipient_List[0].ToTime.hundredths = 0; // NC_Info[0].Recipient_List[0].ToTime.hundredths = 0;
// NC_Info[0].Recipient_List[0].Recipient.RecipientType = RECIPIENT_TYPE_ADDRESS; // NC_Info[0].Recipient_List[0].Recipient.RecipientType = RECIPIENT_TYPE_ADDRESS;
@@ -122,6 +124,7 @@ void Notification_Class_Init(void)
// NC_Info[0].Recipient_List[0].Recipient._.Address.mac[4] = 0xBA; // NC_Info[0].Recipient_List[0].Recipient._.Address.mac[4] = 0xBA;
// NC_Info[0].Recipient_List[0].Recipient._.Address.mac[5] = 0xC0; // NC_Info[0].Recipient_List[0].Recipient._.Address.mac[5] = 0xC0;
// NC_Info[0].Recipient_List[0].Recipient._.Address.mac_len = 6; // NC_Info[0].Recipient_List[0].Recipient._.Address.mac_len = 6;
// NC_Info[0].Recipient_List[0].ProcessIdentifier = 112233;
// NC_Info[0].Recipient_List[0].ConfirmedNotify = true; // NC_Info[0].Recipient_List[0].ConfirmedNotify = true;
// NC_Info[0].Recipient_List[0].Transitions = // NC_Info[0].Recipient_List[0].Transitions =
// TRANSITION_TO_OFFNORMAL_MASKED | TRANSITION_TO_NORMAL_MASKED; // TRANSITION_TO_OFFNORMAL_MASKED | TRANSITION_TO_NORMAL_MASKED;
@@ -244,7 +247,7 @@ int Notification_Class_Read_Property(
apdu_len += encode_application_unsigned(&apdu[0], 3); apdu_len += encode_application_unsigned(&apdu[0], 3);
else else
{ {
if(rpdata->array_index == BACNET_ARRAY_ALL) if (rpdata->array_index == BACNET_ARRAY_ALL)
{ {
/* TO-OFFNORMAL */ /* TO-OFFNORMAL */
apdu_len += encode_application_unsigned(&apdu[apdu_len], apdu_len += encode_application_unsigned(&apdu[apdu_len],
@@ -256,7 +259,7 @@ int Notification_Class_Read_Property(
apdu_len += encode_application_unsigned(&apdu[apdu_len], apdu_len += encode_application_unsigned(&apdu[apdu_len],
CurrentNotify->Priority[2]); CurrentNotify->Priority[2]);
} }
else if(rpdata->array_index <= 3) else if (rpdata->array_index <= 3)
{ {
apdu_len += encode_application_unsigned(&apdu[apdu_len], apdu_len += encode_application_unsigned(&apdu[apdu_len],
CurrentNotify->Priority[rpdata->array_index - 1]); CurrentNotify->Priority[rpdata->array_index - 1]);
@@ -286,22 +289,22 @@ int Notification_Class_Read_Property(
case PROP_RECIPIENT_LIST: case PROP_RECIPIENT_LIST:
/* encode all entry of Recipient_List */ /* encode all entry of Recipient_List */
for(idx = 0; idx < NC_MAX_RECIPIENTS; idx++) for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++)
{ {
BACNET_DESTINATION *RecipientEntry; BACNET_DESTINATION *RecipientEntry;
int i = 0; int i = 0;
/* get pointer of current element for Recipient_List - easier for use */ /* get pointer of current element for Recipient_List - easier for use */
RecipientEntry = &CurrentNotify->Recipient_List[idx]; RecipientEntry = &CurrentNotify->Recipient_List[idx];
if(RecipientEntry->Recipient.RecipientType != RECIPIENT_TYPE_NOTINITIALIZED) if (RecipientEntry->Recipient.RecipientType != RECIPIENT_TYPE_NOTINITIALIZED)
{ {
/* Valid Days - BACnetDaysOfWeek - [bitstring] monday-sunday */ /* Valid Days - BACnetDaysOfWeek - [bitstring] monday-sunday */
u8Val = 0x01; u8Val = 0x01;
bitstring_init(&bit_string); bitstring_init(&bit_string);
for(i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) for (i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++)
{ {
if(RecipientEntry->ValidDays & u8Val) if (RecipientEntry->ValidDays & u8Val)
bitstring_set_bit(&bit_string, i, true); bitstring_set_bit(&bit_string, i, true);
else else
bitstring_set_bit(&bit_string, i, false); bitstring_set_bit(&bit_string, i, false);
@@ -330,7 +333,7 @@ int Notification_Class_Read_Property(
RecipientEntry->Recipient._.DeviceIdentifier); RecipientEntry->Recipient._.DeviceIdentifier);
} }
/* CHOICE - address [1] BACnetAddress */ /* CHOICE - address [1] BACnetAddress */
else if(RecipientEntry->Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) else if (RecipientEntry->Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS)
{ {
/* opening tag 1 */ /* opening tag 1 */
apdu_len += encode_opening_tag(&apdu[apdu_len], 1); apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
@@ -403,7 +406,6 @@ int Notification_Class_Read_Property(
} }
bool Notification_Class_Write_Property( bool Notification_Class_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data) BACNET_WRITE_PROPERTY_DATA * wp_data)
{ {
@@ -430,15 +432,15 @@ bool Notification_Class_Write_Property(
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
&wp_data->error_class, &wp_data->error_code); &wp_data->error_class, &wp_data->error_code);
if(status) { if (status) {
if (wp_data->array_index == 0) { if (wp_data->array_index == 0) {
wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
} }
else if(wp_data->array_index == BACNET_ARRAY_ALL) { else if (wp_data->array_index == BACNET_ARRAY_ALL) {
/// FIXME: wite all array /// FIXME: wite all array
} }
else if(wp_data->array_index <= 3) { else if (wp_data->array_index <= 3) {
CurrentNotify->Priority[wp_data->array_index - 1] = CurrentNotify->Priority[wp_data->array_index - 1] =
value.type.Unsigned_Int; value.type.Unsigned_Int;
} }
@@ -526,7 +528,7 @@ bool Notification_Class_Write_Property(
iOffset += len; iOffset += len;
/* context tag [0] - Device */ /* context tag [0] - Device */
if(decode_is_context_tag(&wp_data->application_data[iOffset], 0)) if (decode_is_context_tag(&wp_data->application_data[iOffset], 0))
{ {
TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_DEVICE; TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_DEVICE;
/* Decode Network Number */ /* Decode Network Number */
@@ -548,7 +550,7 @@ bool Notification_Class_Write_Property(
iOffset += len; iOffset += len;
} }
/* opening tag [1] - Recipient */ /* opening tag [1] - Recipient */
else if(decode_is_opening_tag_number(&wp_data->application_data[iOffset], 1)) else if (decode_is_opening_tag_number(&wp_data->application_data[iOffset], 1))
{ {
iOffset++; iOffset++;
TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_ADDRESS; TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_ADDRESS;
@@ -596,7 +598,7 @@ bool Notification_Class_Write_Property(
iOffset += len; iOffset += len;
/* closing tag [1] - Recipient */ /* closing tag [1] - Recipient */
if(decode_is_closing_tag_number(&wp_data->application_data[iOffset], 1)) if (decode_is_closing_tag_number(&wp_data->application_data[iOffset], 1))
iOffset++; iOffset++;
else { else {
/* Bad decode, wrong tag or following required parameter missing */ /* Bad decode, wrong tag or following required parameter missing */
@@ -665,7 +667,7 @@ bool Notification_Class_Write_Property(
iOffset += len; iOffset += len;
/* Increasing element of list */ /* Increasing element of list */
if(++idx >= NC_MAX_RECIPIENTS) { if (++idx >= NC_MAX_RECIPIENTS) {
wp_data->error_class = ERROR_CLASS_RESOURCES; wp_data->error_class = ERROR_CLASS_RESOURCES;
wp_data->error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; wp_data->error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY;
return false; return false;
@@ -674,18 +676,217 @@ bool Notification_Class_Write_Property(
/* Decoded all recipient list */ /* Decoded all recipient list */
/* copy elements from temporary object */ /* copy elements from temporary object */
for(idx = 0; idx < NC_MAX_RECIPIENTS; idx++) for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++)
{
BACNET_ADDRESS src = {0};
unsigned max_apdu = 0;
int32_t DeviceID;
CurrentNotify->Recipient_List[idx] = TmpNotify.Recipient_List[idx]; CurrentNotify->Recipient_List[idx] = TmpNotify.Recipient_List[idx];
if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) {
/* copy Device_ID */
DeviceID = CurrentNotify->Recipient_List[idx].Recipient._.DeviceIdentifier;
address_bind_request(DeviceID, &max_apdu, &src);
}
else if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) {
/* copy Address */
///src = CurrentNotify->Recipient_List[idx].Recipient._.Address;
///address_bind_request(BACNET_MAX_INSTANCE, &max_apdu, &src);
}
}
status = true; status = true;
break; break;
default: default:
wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break; break;
} }
return status; return status;
} }
static bool IsRecipientActive(BACNET_DESTINATION * pBacDest, uint8_t EventToState)
{
BACNET_DATE_TIME DateTime;
/* valid Transitions */
switch (EventToState)
{
case EVENT_STATE_OFFNORMAL:
case EVENT_STATE_HIGH_LIMIT:
case EVENT_STATE_LOW_LIMIT:
if (!(pBacDest->Transitions & TRANSITION_TO_OFFNORMAL_MASKED))
return false;
break;
case EVENT_STATE_FAULT:
if (!(pBacDest->Transitions & TRANSITION_TO_FAULT_MASKED))
return false;
break;
case EVENT_STATE_NORMAL:
if (!(pBacDest->Transitions & TRANSITION_TO_NORMAL_MASKED))
return false;
break;
default:
return false; /* shouldn't happen */
}
/* get actual date and time */
Device_getCurrentDateTime(&DateTime);
/* valid Days */
if (!((0x01 << (DateTime.date.wday - 1)) & pBacDest->ValidDays))
return false;
/* valid FromTime */
if (datetime_compare_time(&DateTime.time, &pBacDest->FromTime) < 0)
return false;
/* valid ToTime */
if (datetime_compare_time(&pBacDest->ToTime, &DateTime.time) < 0)
return false;
return true;
}
void Notification_Class_common_reporting_function(
BACNET_EVENT_NOTIFICATION_DATA * event_data)
{
/* Fill the parameters common for all types of events. */
NOTIFICATION_CLASS_INFO *CurrentNotify;
BACNET_DESTINATION *pBacDest;
uint32_t notify_index;
uint8_t index;
notify_index = Notification_Class_Instance_To_Index(
event_data->notificationClass);
if (notify_index < MAX_NOTIFICATION_CLASSES)
CurrentNotify = &NC_Info[notify_index];
else
return;
/* Initiating Device Identifier */
event_data->initiatingObjectIdentifier.type = OBJECT_DEVICE;
event_data->initiatingObjectIdentifier.instance =
Device_Object_Instance_Number();
/* Priority and AckRequired*/
switch (event_data->toState)
{
case EVENT_STATE_OFFNORMAL:
event_data->priority =
CurrentNotify->Priority[EVENT_STATE_OFFNORMAL];
event_data->ackRequired = (CurrentNotify->Ack_Required &
TRANSITION_TO_OFFNORMAL_MASKED) ? true : false;
break;
case EVENT_STATE_FAULT:
event_data->priority =
CurrentNotify->Priority[EVENT_STATE_FAULT];
event_data->ackRequired = (CurrentNotify->Ack_Required &
TRANSITION_TO_FAULT_MASKED) ? true : false;
break;
case EVENT_STATE_NORMAL:
case EVENT_STATE_HIGH_LIMIT:
case EVENT_STATE_LOW_LIMIT:
event_data->priority =
CurrentNotify->Priority[EVENT_STATE_NORMAL];
event_data->ackRequired = (CurrentNotify->Ack_Required &
TRANSITION_TO_NORMAL) ? true : false;
break;
default: // shouldn't happen
break;
}
/* send notifications for active recipients */
/* pointer to first recipient */
pBacDest = &CurrentNotify->Recipient_List[0];
for (index = 0; index < NC_MAX_RECIPIENTS; index++, pBacDest++)
{
/* check if recipient is defined */
if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_NOTINITIALIZED)
break; /* recipient doesn't defined - end of list */
if (IsRecipientActive(pBacDest, event_data->toState) == true)
{
BACNET_ADDRESS dest;
uint32_t device_id;
unsigned max_apdu;
/* Process Identifier */
event_data->processIdentifier = pBacDest->ProcessIdentifier;
/* send notification */
if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) {
/* send notification to the specified device */
device_id = pBacDest->Recipient._.DeviceIdentifier;
if (pBacDest->ConfirmedNotify == true)
Send_CEvent_Notify(device_id, event_data);
else if (address_get_by_device(device_id, &max_apdu, &dest))
Send_UEvent_Notify(Handler_Transmit_Buffer, event_data, &dest);
}
else if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) {
/* send notification to the address indicated */
if (pBacDest->ConfirmedNotify == true) {
if (address_get_device_id(&dest, &device_id))
Send_CEvent_Notify(device_id, event_data);
}
else {
dest = pBacDest->Recipient._.Address;
Send_UEvent_Notify(Handler_Transmit_Buffer, event_data, &dest);
}
}
}
}
}
/* This function tries to find the addresses of the defined devices. */
/* It should be called periodically (example once per minute). */
void Notification_Class_find_recipient(void)
{
NOTIFICATION_CLASS_INFO *CurrentNotify;
BACNET_DESTINATION *pBacDest;
BACNET_ADDRESS src = {0};
unsigned max_apdu = 0;
uint32_t notify_index;
uint32_t DeviceID;
uint8_t idx;
for (notify_index = 0; notify_index < MAX_NOTIFICATION_CLASSES; notify_index++)
{
/* pointer to current notification */
CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index(notify_index)];
/* pointer to first recipient */
pBacDest = &CurrentNotify->Recipient_List[0];
for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++, pBacDest++)
{
if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) {
/* Device ID */
DeviceID = CurrentNotify->Recipient_List[idx].Recipient._.DeviceIdentifier;
/* Send who_ is request only when address of device is unknown. */
if(!address_bind_request(DeviceID, &max_apdu, &src))
Send_WhoIs(DeviceID, DeviceID);
}
else if(CurrentNotify->Recipient_List[idx].Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) {
}
}
}
}
+16 -4
View File
@@ -25,17 +25,24 @@
#ifndef NC_H #ifndef NC_H
#define NC_H #define NC_H
#include "event.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
#define NC_MAX_RECIPIENTS 10 #define NC_RESCAN_RECIPIENTS_SECS 60
#define RECIPIENT_TYPE_NOTINITIALIZED 0 /* max "length" of recipient_list */
#define RECIPIENT_TYPE_DEVICE 1 #define NC_MAX_RECIPIENTS 10
#define RECIPIENT_TYPE_ADDRESS 2 /* Recipient types */
typedef enum {
RECIPIENT_TYPE_NOTINITIALIZED = 0,
RECIPIENT_TYPE_DEVICE = 1,
RECIPIENT_TYPE_ADDRESS = 2
} NC_RECIPIENT_TYPE;
/* BACnetRecipient sructuer*/ /* BACnetRecipient sructuer*/
@@ -97,6 +104,11 @@ typedef struct Notification_Class_info {
bool Notification_Class_Write_Property( bool Notification_Class_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data); BACNET_WRITE_PROPERTY_DATA * wp_data);
void Notification_Class_common_reporting_function(
BACNET_EVENT_NOTIFICATION_DATA * event_data);
void Notification_Class_find_recipient(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
+19 -24
View File
@@ -66,29 +66,6 @@
#include "bacfile.h" #include "bacfile.h"
#endif #endif
/* All included BACnet objects */
static object_functions_t Object_Table[] = {
{DEVICE_OBJ_FUNCTIONS},
{ANALOG_INPUT_OBJ_FUNCTIONS},
{ANALOG_OUTPUT_OBJ_FUNCTIONS},
{ANALOG_VALUE_OBJ_FUNCTIONS},
{BINARY_INPUT_OBJ_FUNCTIONS},
{BINARY_OUTPUT_OBJ_FUNCTIONS},
{BINARY_VALUE_OBJ_FUNCTIONS},
#if defined(INTRINSIC_REPORTING)
{NOTIFICATION_CLASS_OBJ_FUNCTIONS},
#endif
{LIFE_SAFETY_POINT_OBJ_FUNCTIONS},
{LOAD_CONTROL_OBJ_FUNCTIONS},
{MULTI_STATE_OUTPUT_OBJ_FUNCTIONS},
{MULTI_STATE_INPUT_OBJ_FUNCTIONS},
{TRENDLOG_OBJ_FUNCTIONS},
#if defined(BACFILE)
{FILE_OBJ_FUNCTIONS},
#endif
{MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL}
};
/** @file server/main.c Example server application using the BACnet Stack. */ /** @file server/main.c Example server application using the BACnet Stack. */
@@ -106,10 +83,12 @@ static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
static void Init_Service_Handlers( static void Init_Service_Handlers(
void) void)
{ {
Device_Init(&Object_Table[0]); Device_Init(NULL);
/* we need to handle who-is to support dynamic device binding */ /* we need to handle who-is to support dynamic device binding */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has);
/* handle i-am to support binding to other devices */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind);
/* set the handler for all the services we don't implement */ /* set the handler for all the services we don't implement */
/* It is required to send the proper reject message... */ /* It is required to send the proper reject message... */
apdu_set_unrecognized_service_handler_handler apdu_set_unrecognized_service_handler_handler
@@ -182,6 +161,8 @@ int main(
time_t current_seconds = 0; time_t current_seconds = 0;
uint32_t elapsed_seconds = 0; uint32_t elapsed_seconds = 0;
uint32_t elapsed_milliseconds = 0; uint32_t elapsed_milliseconds = 0;
uint32_t address_binding_tmr = 0;
uint32_t recipient_scan_tmr = 0;
/* allow the device ID to be set */ /* allow the device ID to be set */
if (argc > 1) if (argc > 1)
@@ -224,7 +205,21 @@ int main(
} }
tsm_timer_milliseconds(elapsed_milliseconds); tsm_timer_milliseconds(elapsed_milliseconds);
trend_log_timer(elapsed_seconds); trend_log_timer(elapsed_seconds);
Device_local_reporting(elapsed_seconds * 1000);
} }
/* scan cache address */
address_binding_tmr += elapsed_seconds;
if (address_binding_tmr >= 60) {
address_cache_timer(address_binding_tmr);
address_binding_tmr = 0;
}
/* try to find addresses of recipients */
recipient_scan_tmr += elapsed_seconds;
if (recipient_scan_tmr >= NC_RESCAN_RECIPIENTS_SECS) {
Notification_Class_find_recipient();
recipient_scan_tmr = 0;
}
/* output */ /* output */
/* blink LEDs, Turn on or off outputs, etc */ /* blink LEDs, Turn on or off outputs, etc */
+12
View File
@@ -393,6 +393,12 @@ typedef enum {
/* do the MAX as define rather than enumeration for devices /* do the MAX as define rather than enumeration for devices
and compilers that want to use smaller datatype for enum */ and compilers that want to use smaller datatype for enum */
#define MAX_BACNET_PROPERTY_ID 4194303 #define MAX_BACNET_PROPERTY_ID 4194303
typedef enum {
EVENT_LOW_LIMIT_ENABLE = 1,
EVENT_HIGH_LIMIT_ENABLE = 2
} BACNET_LIMIT_ENABLE;
typedef enum { typedef enum {
ACTION_DIRECT = 0, ACTION_DIRECT = 0,
ACTION_REVERSE = 1 ACTION_REVERSE = 1
@@ -420,6 +426,12 @@ typedef enum {
EVENT_STATE_LOW_LIMIT = 4 EVENT_STATE_LOW_LIMIT = 4
} BACNET_EVENT_STATE; } BACNET_EVENT_STATE;
typedef enum {
EVENT_ENABLE_TO_OFFNORMAL = 1,
EVENT_ENABLE_TO_FAULT = 2,
EVENT_ENABLE_TO_NORMAL = 4
} BACNET_EVENT_ENABLE;
typedef enum { typedef enum {
STATUS_OPERATIONAL = 0, STATUS_OPERATIONAL = 0,
STATUS_OPERATIONAL_READ_ONLY = 1, STATUS_OPERATIONAL_READ_ONLY = 1,