Feature/oscbs 29 calendar time object (#440)

* Added basic Calendar object, unit tests, and integration with example device object.

* Added basic Time Value object, unit tests, and integration with example device object.

---------

Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
Mikhail Antropov
2024-02-02 08:01:27 +03:00
committed by GitHub
parent 340bd09561
commit 8f576461a8
38 changed files with 2574 additions and 45 deletions
+16 -16
View File
@@ -21,19 +21,19 @@ jobs:
run: | run: |
gcc --version gcc --version
make clean make clean
make CSTANDARD="-std=gnu89" all make LEGACY=true CSTANDARD="-std=gnu89" all
- name: Build Demo Apps GNU99 - name: Build Demo Apps GNU99
run: | run: |
make clean make clean
make CSTANDARD="-std=gnu99" all make CSTANDARD="-std=gnu99" all
- name: Build Demo Apps GNU11 - name: LEGACY=true Build Demo Apps GNU11
run: | run: |
make clean make clean
make CSTANDARD="-std=gnu11" all make LEGACY=true CSTANDARD="-std=gnu11" all
- name: Build Demo Apps GNU17 - name: Build Demo Apps GNU17
run: | run: |
make clean make clean
make CSTANDARD="-std=gnu17" all make LEGACY=true CSTANDARD="-std=gnu17" all
bip-no-bbmd-apps: bip-no-bbmd-apps:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -47,7 +47,7 @@ jobs:
run: | run: |
gcc --version gcc --version
make clean make clean
make BBMD=none all make LEGACY=true BBMD=none all
bip-client-bbmd-apps: bip-client-bbmd-apps:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -61,7 +61,7 @@ jobs:
run: | run: |
gcc --version gcc --version
make clean make clean
make BBMD=client all make LEGACY=true BBMD=client all
gateway: gateway:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -75,7 +75,7 @@ jobs:
run: | run: |
gcc --version gcc --version
make clean make clean
make gateway make LEGACY=true gateway
router: router:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -89,7 +89,7 @@ jobs:
run: | run: |
gcc --version gcc --version
make clean make clean
make router make LEGACY=true router
router-ipv6: router-ipv6:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -103,7 +103,7 @@ jobs:
run: | run: |
gcc --version gcc --version
make clean make clean
make router-ipv6 make LEGACY=true router-ipv6
router-mstp: router-mstp:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -117,7 +117,7 @@ jobs:
run: | run: |
gcc --version gcc --version
make clean make clean
make router-mstp make LEGACY=true router-mstp
bip6: bip6:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -131,7 +131,7 @@ jobs:
run: | run: |
gcc --version gcc --version
make clean make clean
make bip6 make LEGACY=true bip6
mstp: mstp:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -145,7 +145,7 @@ jobs:
run: | run: |
gcc --version gcc --version
make clean make clean
make mstp make LEGACY=true mstp
ethernet: ethernet:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -159,7 +159,7 @@ jobs:
run: | run: |
gcc --version gcc --version
make clean make clean
make ethernet make LEGACY=true ethernet
ports-arm-makefile: ports-arm-makefile:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -175,9 +175,9 @@ jobs:
run: | run: |
make clean make clean
arm-none-eabi-gcc --version arm-none-eabi-gcc --version
make stm32f10x make LEGACY=true stm32f10x
make stm32f4xx make LEGACY=true stm32f4xx
make at91sam7s make LEGACY=true at91sam7s
ports-arm-cmake: ports-arm-cmake:
runs-on: ubuntu-latest runs-on: ubuntu-latest
+4
View File
@@ -198,6 +198,8 @@ add_library(${PROJECT_NAME}
src/bacnet/basic/object/bv.h src/bacnet/basic/object/bv.h
src/bacnet/basic/object/channel.c src/bacnet/basic/object/channel.c
src/bacnet/basic/object/channel.h src/bacnet/basic/object/channel.h
src/bacnet/basic/object/calendar.c
src/bacnet/basic/object/calendar.h
src/bacnet/basic/object/color_object.c src/bacnet/basic/object/color_object.c
src/bacnet/basic/object/color_object.h src/bacnet/basic/object/color_object.h
src/bacnet/basic/object/color_temperature.c src/bacnet/basic/object/color_temperature.c
@@ -238,6 +240,8 @@ add_library(${PROJECT_NAME}
src/bacnet/basic/object/piv.h src/bacnet/basic/object/piv.h
src/bacnet/basic/object/schedule.c src/bacnet/basic/object/schedule.c
src/bacnet/basic/object/schedule.h src/bacnet/basic/object/schedule.h
src/bacnet/basic/object/time_value.c
src/bacnet/basic/object/time_value.h
src/bacnet/basic/object/trendlog.c src/bacnet/basic/object/trendlog.c
src/bacnet/basic/object/trendlog.h src/bacnet/basic/object/trendlog.h
src/bacnet/basic/service/h_alarm_ack.c src/bacnet/basic/service/h_alarm_ack.c
+2
View File
@@ -18,6 +18,7 @@ BACNET_OBJECT_SRC := \
$(BACNET_OBJECT_DIR)/blo.c \ $(BACNET_OBJECT_DIR)/blo.c \
$(BACNET_OBJECT_DIR)/bo.c \ $(BACNET_OBJECT_DIR)/bo.c \
$(BACNET_OBJECT_DIR)/bv.c \ $(BACNET_OBJECT_DIR)/bv.c \
$(BACNET_OBJECT_DIR)/calendar.c \
$(BACNET_OBJECT_DIR)/channel.c \ $(BACNET_OBJECT_DIR)/channel.c \
$(BACNET_OBJECT_DIR)/color_object.c \ $(BACNET_OBJECT_DIR)/color_object.c \
$(BACNET_OBJECT_DIR)/color_temperature.c \ $(BACNET_OBJECT_DIR)/color_temperature.c \
@@ -35,6 +36,7 @@ BACNET_OBJECT_SRC := \
$(BACNET_OBJECT_DIR)/piv.c \ $(BACNET_OBJECT_DIR)/piv.c \
$(BACNET_OBJECT_DIR)/nc.c \ $(BACNET_OBJECT_DIR)/nc.c \
$(BACNET_OBJECT_DIR)/netport.c \ $(BACNET_OBJECT_DIR)/netport.c \
$(BACNET_OBJECT_DIR)/time_value.c \
$(BACNET_OBJECT_DIR)/trendlog.c \ $(BACNET_OBJECT_DIR)/trendlog.c \
$(BACNET_OBJECT_DIR)/schedule.c \ $(BACNET_OBJECT_DIR)/schedule.c \
$(BACNET_OBJECT_DIR)/access_credential.c \ $(BACNET_OBJECT_DIR)/access_credential.c \
+2
View File
@@ -13,6 +13,7 @@ SRC = main.c \
$(BACNET_OBJECT_DIR)/bo.c \ $(BACNET_OBJECT_DIR)/bo.c \
$(BACNET_OBJECT_DIR)/blo.c \ $(BACNET_OBJECT_DIR)/blo.c \
$(BACNET_OBJECT_DIR)/bv.c \ $(BACNET_OBJECT_DIR)/bv.c \
$(BACNET_OBJECT_DIR)/calendar.c \
$(BACNET_OBJECT_DIR)/channel.c \ $(BACNET_OBJECT_DIR)/channel.c \
$(BACNET_OBJECT_DIR)/color_object.c \ $(BACNET_OBJECT_DIR)/color_object.c \
$(BACNET_OBJECT_DIR)/color_temperature.c \ $(BACNET_OBJECT_DIR)/color_temperature.c \
@@ -29,6 +30,7 @@ SRC = main.c \
$(BACNET_OBJECT_DIR)/piv.c \ $(BACNET_OBJECT_DIR)/piv.c \
$(BACNET_OBJECT_DIR)/nc.c \ $(BACNET_OBJECT_DIR)/nc.c \
$(BACNET_OBJECT_DIR)/netport.c \ $(BACNET_OBJECT_DIR)/netport.c \
$(BACNET_OBJECT_DIR)/time_value.c \
$(BACNET_OBJECT_DIR)/trendlog.c \ $(BACNET_OBJECT_DIR)/trendlog.c \
$(BACNET_OBJECT_DIR)/schedule.c \ $(BACNET_OBJECT_DIR)/schedule.c \
$(BACNET_OBJECT_DIR)/access_credential.c \ $(BACNET_OBJECT_DIR)/access_credential.c \
+1 -3
View File
@@ -73,9 +73,7 @@ CORESRC = \
$(BACNET_CORE)/abort.c \ $(BACNET_CORE)/abort.c \
$(BACNET_CORE)/reject.c \ $(BACNET_CORE)/reject.c \
$(BACNET_CORE)/bacerror.c \ $(BACNET_CORE)/bacerror.c \
$(BACNET_CORE)/bacapp.c \ $(BACNET_CORE)/bacapp.c
# $(BACNET_CORE)/ethernet.c
## Include Directories ## Include Directories
INCLUDES = -I. -I$(BACNET_INCLUDE) INCLUDES = -I. -I$(BACNET_INCLUDE)
-19
View File
@@ -52,25 +52,6 @@ CORESRC = \
$(BACNET_CORE)/bacapp.c \ $(BACNET_CORE)/bacapp.c \
$(BACNET_CORE)/version.c $(BACNET_CORE)/version.c
# $(BACNET_CORE)/bacprop.c \
# $(BACNET_CORE)/bactext.c \
# $(BACNET_CORE)/datetime.c \
# $(BACNET_CORE)/indtext.c \
# $(BACNET_CORE)/bigend.c \
# $(BACNET_CORE)/arf.c \
# $(BACNET_CORE)/awf.c \
# $(BACNET_CORE)/cov.c \
# $(BACNET_CORE)/dcc.c \
# $(BACNET_CORE)/iam/iam_client.c \
# $(BACNET_CORE)/ihave.c \
# $(BACNET_CORE)/rd.c \
# $(BACNET_CORE)/rpm.c \
# $(BACNET_CORE)/timesync.c \
# $(BACNET_CORE)/whohas.c \
# $(BACNET_CORE)/filename.c \
# $(BACNET_CORE)/tsm.c \
# $(BACNET_CORE)/address.c \
## Include Directories ## Include Directories
INCLUDES = -I. -I$(BACNET_INCLUDE) INCLUDES = -I. -I$(BACNET_INCLUDE)
@@ -182,6 +182,7 @@
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\bi.c" /> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\bi.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\bo.c" /> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\bo.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\bv.c" /> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\bv.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\calendar.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\channel.c" /> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\channel.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\command.c" /> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\command.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\csv.c" /> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\csv.c" />
@@ -197,6 +198,7 @@
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\osv.c" /> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\osv.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\piv.c" /> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\piv.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\schedule.c" /> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\schedule.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\time_value.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\trendlog.c" /> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\trendlog.c" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -36,6 +36,9 @@
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\bv.c"> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\bv.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\calendar.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\channel.c"> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\channel.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@@ -69,6 +72,9 @@
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\nc.c"> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\nc.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\time_value.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\trendlog.c"> <ClCompile Include="..\..\..\..\src\bacnet\basic\object\trendlog.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
+2 -1
View File
@@ -1320,7 +1320,8 @@ int bacapp_known_property_tag(
* @param max_apdu_len - number of bytes in the buffer * @param max_apdu_len - number of bytes in the buffer
* @param value - stores the decoded property value * @param value - stores the decoded property value
* @param property - context property identifier * @param property - context property identifier
* @return number of bytes decoded, or ERROR if errors occur * @return number of bytes decoded, or BACNET_STATUS_ERROR if errors occur
* @note number of bytes can be 0 for empty lists, etc.
*/ */
int bacapp_decode_known_property(uint8_t *apdu, int bacapp_decode_known_property(uint8_t *apdu,
int max_apdu_len, int max_apdu_len,
+801
View File
@@ -0,0 +1,801 @@
/**
* @file
* @author Steve Karg <skarg@users.sourceforge.net>
* @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
* @date June 2022
* @brief Calendar objects, customize for your use
*
* @section DESCRIPTION
*
* The Calendar object is an object with a present-value that
* uses a BOOLEAN data type, and features a Date_List
* that is a BACnetLIST of BACnetCalendarEntry.
*
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/config.h"
#include "bacnet/abort.h"
#include "bacnet/apdu.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacerror.h"
#include "bacnet/bacapp.h"
#include "bacnet/bactext.h"
#include "bacnet/cov.h"
#include "bacnet/npdu.h"
#include "bacnet/proplist.h"
#include "bacnet/reject.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/sys/keylist.h"
/* me! */
#include "calendar.h"
struct object_data {
bool Changed : 1;
bool Write_Enabled : 1;
bool Present_Value;
OS_Keylist Date_List;
const char *Object_Name;
const char *Description;
};
/* Key List for storing the object data sorted by instance number */
static OS_Keylist Object_List;
/* callback for present value writes */
static calendar_write_present_value_callback Calendar_Write_Present_Value_Callback;
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Calendar_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_DATE_LIST,
-1 };
static const int Calendar_Properties_Optional[] = { PROP_DESCRIPTION, -1 };
static const int Calendar_Properties_Proprietary[] = { -1 };
/* standard properties that are arrays for this object,
but not necessary supported in this object */
static const int BACnetARRAY_Properties[] = {
PROP_PRIORITY_ARRAY, PROP_TAGS, -1 };
/**
* Returns the list of required, optional, and proprietary properties.
* Used by ReadPropertyMultiple service.
*
* @param pRequired - pointer to list of int terminated by -1, of
* BACnet required properties for this object.
* @param pOptional - pointer to list of int terminated by -1, of
* BACnet optkional properties for this object.
* @param pProprietary - pointer to list of int terminated by -1, of
* BACnet proprietary properties for this object.
*/
void Calendar_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Calendar_Properties_Required;
}
if (pOptional) {
*pOptional = Calendar_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Calendar_Properties_Proprietary;
}
return;
}
/**
* Determines if a given Calendar instance is valid
*
* @param object_instance - object-instance number of the object
*
* @return true if the instance is valid, and false if not
*/
bool Calendar_Valid_Instance(uint32_t object_instance)
{
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
return true;
}
return false;
}
/**
* Determines the number of Calendar objects
*
* @return Number of Calendar objects
*/
unsigned Calendar_Count(void)
{
return Keylist_Count(Object_List);
}
/**
* Determines the object instance-number for a given 0..N index
* of Calendar objects where N is Calendar_Count().
*
* @param index - 0..N where N is Calendar_Count()
*
* @return object instance-number for the given index
*/
uint32_t Calendar_Index_To_Instance(unsigned index)
{
return Keylist_Key(Object_List, index);
}
/**
* For a given object instance-number, determines a 0..N index
* of Calendar objects where N is Calendar_Count().
*
* @param object_instance - object-instance number of the object
*
* @return index for the given instance-number, or Calendar_Count()
* if not valid.
*/
unsigned Calendar_Instance_To_Index(uint32_t object_instance)
{
return Keylist_Index(Object_List, object_instance);
}
/**
* For a given object instance-number, sets the present-value
*
* @param object_instance - object-instance number of the object
* @param old_value - boolean, previous value of the Present Value property
* @param value - boolean, new value of the Present Value property
* @param priority - priority-array index value 1..16
* @param error_class - the BACnet error class
* @param error_code - BACnet Error code
*
* @return true if values are within range and present-value is set.
*/
static bool Calendar_Present_Value_Write(uint32_t object_instance,
bool old_value,
bool value,
uint8_t priority,
BACNET_ERROR_CLASS *error_class,
BACNET_ERROR_CODE *error_code)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
(void)priority;
if (pObject->Write_Enabled) {
old_value = pObject->Present_Value;
pObject->Present_Value = value;
if (Calendar_Write_Present_Value_Callback) {
Calendar_Write_Present_Value_Callback(
object_instance, old_value, value);
}
status = true;
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
}
} else {
*error_class = ERROR_CLASS_OBJECT;
*error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return status;
}
/**
* For a given date-list, deletes the entire data-list.
* @param list - the list to be deleted
*/
static void Calendar_Date_List_Clean(OS_Keylist list)
{
void *data;
while (Keylist_Count(list) > 0) {
data = Keylist_Data_Pop(list);
free(data);
}
}
/**
* For a given object instance-number, returns the Calendar entity by index.
*
* @param object_instance - object-instance number of the object
* @param index - index of entity
*
* @return Calendar entity.
*/
BACNET_CALENDAR_ENTRY *Calendar_Date_List_Get(
uint32_t object_instance, uint8_t index)
{
BACNET_CALENDAR_ENTRY *entry = NULL;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
entry = Keylist_Data_Index(pObject->Date_List, index);
}
return entry;
}
/**
* For a given object instance-number, adds a Calendar entity to entities list.
*
* @param object_instance - object-instance number of the object
* @param entity - Calendar entity
*
* @return true if the entity is add successfully.
*/
bool Calendar_Date_List_Add(uint32_t object_instance,
BACNET_CALENDAR_ENTRY *value)
{
bool st = false;
BACNET_CALENDAR_ENTRY *entry;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (!pObject) {
return false;
}
entry = calloc(1, sizeof(BACNET_CALENDAR_ENTRY));
if (!entry) {
return false;
}
*entry = *value;
st = Keylist_Data_Add(
pObject->Date_List, Keylist_Count(pObject->Date_List), entry);
return st;
}
/**
* For a given object instance-number, clears to entities list.
*
* @param object_instance - object-instance number of the object
*
* @return true if entities list is clear successfully.
*/
bool Calendar_Date_List_Delete_All(uint32_t object_instance)
{
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (!pObject) {
return false;
}
Calendar_Date_List_Clean(pObject->Date_List);
return true;
}
/**
* For a given object instance-number, returns the entities list length.
*
* @param object_instance - object-instance number of the object
*
* @return size of entities list.
*/
int Calendar_Date_List_Count(uint32_t object_instance)
{
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (!pObject) {
return 0;
}
return Keylist_Count(pObject->Date_List);
}
/**
* @brief Encode a Calendar entity list complex data type
*
* @param object_instance - object-instance number of the object
* @param apdu - the APDU buffer
* @param apdu_size - size of the apdu buffer.
*
* @return bytes encoded or zero on error.
*/
int Calendar_Date_List_Encode(
uint32_t object_instance, uint8_t *apdu, int max_apdu)
{
BACNET_CALENDAR_ENTRY *entry = NULL;
int apdu_len = 0;
unsigned index = 0;
unsigned size = 0;
size = Calendar_Date_List_Count(object_instance);
for (index = 0; index < size; index++) {
entry = Calendar_Date_List_Get(object_instance, index);
apdu_len += bacnet_calendar_entry_encode(NULL, entry);
}
if (apdu_len > max_apdu) {
return BACNET_STATUS_ABORT;
}
apdu_len = 0;
for (index = 0; index < size; index++) {
entry = Calendar_Date_List_Get(object_instance, index);
apdu_len += bacnet_calendar_entry_encode(&apdu[apdu_len], entry);
}
return apdu_len;
}
/**
* For a given object instance-number, determines the present-value
*
* @param object_instance - object-instance number of the object
*
* @return present-value of the object
*/
bool Calendar_Present_Value(uint32_t object_instance)
{
BACNET_DATE date;
BACNET_TIME time;
BACNET_CALENDAR_ENTRY *entry = NULL;
unsigned size = 0;
int index;
datetime_local(&date, &time, NULL, NULL);
size = Calendar_Date_List_Count(object_instance);
for (index = 0; index < size; index++) {
entry = Calendar_Date_List_Get(object_instance, index);
if (bacapp_date_in_calendar_entry(&date, entry)) {
return true;
}
}
return false;
}
/**
* For a given object instance-number, loads the object-name into
* a characterstring. Note that the object name must be unique
* within this device.
*
* @param object_instance - object-instance number of the object
* @param object_name - holds the object-name retrieved
*
* @return true if object-name was retrieved
*/
bool Calendar_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
bool status = false;
struct object_data *pObject;
char name_text[16] = "CALENDAR-4194303";
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (pObject->Object_Name) {
status =
characterstring_init_ansi(object_name, pObject->Object_Name);
} else {
snprintf(name_text, sizeof(name_text), "CALENDAR-%u", object_instance);
status = characterstring_init_ansi(object_name, name_text);
}
}
return status;
}
/**
* For a given object instance-number, sets the object-name
* Note that the object name must be unique within this device.
*
* @param object_instance - object-instance number of the object
* @param new_name - holds the object-name to be set
*
* @return true if object-name was set
*/
bool Calendar_Name_Set(uint32_t object_instance, char *new_name)
{
bool status = false; /* return value */
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject && new_name) {
status = true;
pObject->Object_Name = new_name;
}
return status;
}
/**
* For a given object instance-number, returns the description
*
* @param object_instance - object-instance number of the object
*
* @return description text or NULL if not found
*/
char *Calendar_Description(uint32_t object_instance)
{
char *name = NULL;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (pObject->Description) {
name = (char *)pObject->Description;
} else {
name = "";
}
}
return name;
}
/**
* For a given object instance-number, sets the description
*
* @param object_instance - object-instance number of the object
* @param new_name - holds the description to be set
*
* @return true if object-name was set
*/
bool Calendar_Description_Set(uint32_t object_instance, char *new_name)
{
bool status = false; /* return value */
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = true;
pObject->Description = new_name;
}
return status;
}
/**
* @brief Determine if the object property is a member of this object instance
* @param object_instance - object-instance number of the object
* @param object_property - object-property to be checked
* @return true if the property is a member of this object instance
*/
static bool Property_List_Member(
uint32_t object_instance, int object_property)
{
bool found = false;
const int *pRequired = NULL;
const int *pOptional = NULL;
const int *pProprietary = NULL;
(void)object_instance;
Calendar_Property_Lists(
&pRequired, &pOptional, &pProprietary);
found = property_list_member(pRequired, object_property);
if (!found) {
found = property_list_member(pOptional, object_property);
}
if (!found) {
found = property_list_member(pProprietary, object_property);
}
return found;
}
/**
* @brief Determine if the object property is a BACnetARRAY property
* @param object_property - object-property to be checked
* @return true if the property is a BACnetARRAY property
*/
static bool BACnetARRAY_Property(
int object_property)
{
return property_list_member(
BACnetARRAY_Properties, object_property);
}
/**
* ReadProperty handler for this object. For the given ReadProperty
* data, the application_data is loaded or the error flags are set.
*
* @param rpdata - BACNET_READ_PROPERTY_DATA data, including
* requested data and space for the reply, or error response.
*
* @return number of APDU bytes in the response, or
* BACNET_STATUS_ERROR on error.
*/
int Calendar_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
BACNET_CHARACTER_STRING char_string;
uint8_t *apdu = NULL;
int apdu_max = 0;
bool value = false;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
apdu_max = rpdata->application_data_len;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], rpdata->object_type, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Calendar_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len =
encode_application_enumerated(&apdu[0], rpdata->object_type);
break;
case PROP_PRESENT_VALUE:
value = Calendar_Present_Value(rpdata->object_instance);
apdu_len = encode_application_boolean(apdu, value);
break;
case PROP_DATE_LIST:
apdu_len = Calendar_Date_List_Encode(
rpdata->object_instance, apdu, apdu_max);
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(
&char_string, Calendar_Description(rpdata->object_instance));
apdu_len = encode_application_character_string(apdu, &char_string);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) &&
(!BACnetARRAY_Property(rpdata->object_property)) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/**
* WriteProperty handler for this object. For the given WriteProperty
* data, the application_data is loaded or the error flags are set.
*
* @param wp_data - BACNET_WRITE_PROPERTY_DATA data, including
* requested data and space for the reply, or error response.
*
* @return false if an error is loaded, true if no errors
*/
bool Calendar_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
int iOffset;
BACNET_CALENDAR_ENTRY entry;
bool pv_old;
bool pv;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
if ((!BACnetARRAY_Property(wp_data->object_property)) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
/* only array properties can have array options */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_DATE_LIST:
pv_old = Calendar_Present_Value(wp_data->object_instance);
Calendar_Date_List_Delete_All(wp_data->object_instance);
iOffset = 0;
/* decode all packed */
while (iOffset < wp_data->application_data_len) {
len = bacnet_calendar_entry_decode(
&wp_data->application_data[iOffset],
wp_data->application_data_len - iOffset,
&entry);
if (len == BACNET_STATUS_REJECT) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
iOffset += len;
Calendar_Date_List_Add(wp_data->object_instance, &entry);
}
pv = Calendar_Present_Value(wp_data->object_instance);
status = Calendar_Present_Value_Write(wp_data->object_instance,
pv_old, pv, wp_data->priority, &wp_data->error_class,
&wp_data->error_code);
break;
default:
if (Property_List_Member(
wp_data->object_instance, wp_data->object_property)) {
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_UNKNOWN_PROPERTY;
}
break;
}
return status;
}
/**
* @brief Sets a callback used when present-value is written from BACnet
* @param cb - callback used to provide indications
*/
void Calendar_Write_Present_Value_Callback_Set(
calendar_write_present_value_callback cb)
{
Calendar_Write_Present_Value_Callback = cb;
}
/**
* @brief Determines a object write-enabled flag state
* @param object_instance - object-instance number of the object
* @return write-enabled status flag
*/
bool Calendar_Write_Enabled(uint32_t object_instance)
{
bool value = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
value = pObject->Write_Enabled;
}
return value;
}
/**
* @brief For a given object instance-number, sets the write-enabled flag
* @param object_instance - object-instance number of the object
*/
void Calendar_Write_Enable(uint32_t object_instance)
{
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Write_Enabled = true;
}
}
/**
* @brief For a given object instance-number, clears the write-enabled flag
* @param object_instance - object-instance number of the object
*/
void Calendar_Write_Disable(uint32_t object_instance)
{
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Write_Enabled = false;
}
}
/**
* Creates a Calendar object
* @param object_instance - object-instance number of the object
* @return object_instance if the object is created, else BACNET_MAX_INSTANCE
*/
uint32_t Calendar_Create(uint32_t object_instance)
{
bool status = false;
struct object_data *pObject = NULL;
int index = 0;
if (object_instance > BACNET_MAX_INSTANCE) {
return BACNET_MAX_INSTANCE;
} else if (object_instance == BACNET_MAX_INSTANCE) {
/* wildcard instance */
/* the Object_Identifier property of the newly created object
shall be initialized to a value that is unique within the
responding BACnet-user device. The method used to generate
the object identifier is a local matter.*/
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
}
pObject = Keylist_Data(Object_List, object_instance);
if (!pObject) {
pObject = calloc(1, sizeof(struct object_data));
if (!pObject) {
return BACNET_MAX_INSTANCE;
}
pObject->Object_Name = NULL;
pObject->Description = NULL;
pObject->Present_Value = false;
pObject->Date_List = Keylist_Create();
pObject->Changed = false;
pObject->Write_Enabled = false;
/* add to list */
index = Keylist_Data_Add(Object_List, object_instance, pObject);
if (index < 0) {
free(pObject);
return BACNET_MAX_INSTANCE;
}
}
return object_instance;
}
/**
* Deletes an Calendar object
* @param object_instance - object-instance number of the object
* @return true if the object is deleted
*/
bool Calendar_Delete(uint32_t object_instance)
{
bool status = false;
struct object_data *pObject = NULL;
pObject = Keylist_Data_Delete(Object_List, object_instance);
if (pObject) {
Calendar_Date_List_Clean(pObject->Date_List);
Keylist_Delete(pObject->Date_List);
free(pObject);
status = true;
}
return status;
}
/**
* Deletes all the Calendars and their data
*/
void Calendar_Cleanup(void)
{
struct object_data *pObject;
if (Object_List) {
do {
pObject = Keylist_Data_Pop(Object_List);
if (pObject) {
Calendar_Date_List_Clean(pObject->Date_List);
Keylist_Delete(pObject->Date_List);
free(pObject);
}
} while (pObject);
Keylist_Delete(Object_List);
Object_List = NULL;
}
}
/**
* Initializes the Calendar object data
*/
void Calendar_Init(void)
{
Object_List = Keylist_Create();
if (Object_List) {
atexit(Calendar_Cleanup);
}
}
+105
View File
@@ -0,0 +1,105 @@
/**
* @file
* @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
* @date June 2022
* @brief API for a Calendar object used by a BACnet device object
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#ifndef BACNET_CALENDAR_OBJECT_H
#define BACNET_CALENDAR_OBJECT_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacnet_stack_exports.h"
#include "bacnet/config.h"
#include "bacnet/bacdef.h"
#include "bacnet/calendar_entry.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
/**
* @brief Callback for gateway write present value request
* @param object_instance - object-instance number of the object
* @param old_value - bool value prior to write
* @param value - bool value of the write
*/
typedef void (*calendar_write_present_value_callback)(
uint32_t object_instance, bool old_value, bool value);
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
BACNET_STACK_EXPORT
void Calendar_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary);
BACNET_STACK_EXPORT
bool Calendar_Valid_Instance(uint32_t object_instance);
BACNET_STACK_EXPORT
unsigned Calendar_Count(void);
BACNET_STACK_EXPORT
uint32_t Calendar_Index_To_Instance(unsigned index);
BACNET_STACK_EXPORT
unsigned Calendar_Instance_To_Index(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Calendar_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name);
BACNET_STACK_EXPORT
bool Calendar_Name_Set(uint32_t object_instance, char *new_name);
BACNET_STACK_EXPORT
int Calendar_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata);
BACNET_STACK_EXPORT
bool Calendar_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data);
BACNET_STACK_EXPORT
bool Calendar_Present_Value(uint32_t object_instance);
BACNET_STACK_EXPORT
void Calendar_Write_Present_Value_Callback_Set(
calendar_write_present_value_callback cb);
BACNET_STACK_EXPORT
BACNET_CALENDAR_ENTRY *Calendar_Date_List_Get(
uint32_t object_instance, uint8_t index);
BACNET_STACK_EXPORT
bool Calendar_Date_List_Add(
uint32_t object_instance, BACNET_CALENDAR_ENTRY *value);
BACNET_STACK_EXPORT
bool Calendar_Date_List_Delete_All(uint32_t object_instance);
BACNET_STACK_EXPORT
int Calendar_Date_List_Count(uint32_t object_instance);
BACNET_STACK_EXPORT
int Calendar_Date_List_Encode(
uint32_t object_instance, uint8_t *apdu, int max_apdu);
BACNET_STACK_EXPORT
char *Calendar_Description(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Calendar_Description_Set(uint32_t object_instance, char *new_name);
BACNET_STACK_EXPORT
bool Calendar_Write_Enabled(uint32_t instance);
BACNET_STACK_EXPORT
void Calendar_Write_Enable(uint32_t instance);
BACNET_STACK_EXPORT
void Calendar_Write_Disable(uint32_t instance);
BACNET_STACK_EXPORT
uint32_t Calendar_Create(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Calendar_Delete(uint32_t object_instance);
BACNET_STACK_EXPORT
void Calendar_Cleanup(void);
BACNET_STACK_EXPORT
void Calendar_Init(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+18
View File
@@ -56,6 +56,7 @@
#endif #endif
#include "bacnet/basic/object/bo.h" #include "bacnet/basic/object/bo.h"
#include "bacnet/basic/object/bv.h" #include "bacnet/basic/object/bv.h"
#include "bacnet/basic/object/calendar.h"
#include "bacnet/basic/object/channel.h" #include "bacnet/basic/object/channel.h"
#include "bacnet/basic/object/command.h" #include "bacnet/basic/object/command.h"
#include "bacnet/basic/object/csv.h" #include "bacnet/basic/object/csv.h"
@@ -71,6 +72,7 @@
#include "bacnet/basic/object/osv.h" #include "bacnet/basic/object/osv.h"
#include "bacnet/basic/object/piv.h" #include "bacnet/basic/object/piv.h"
#include "bacnet/basic/object/schedule.h" #include "bacnet/basic/object/schedule.h"
#include "bacnet/basic/object/time_value.h"
#include "bacnet/basic/object/trendlog.h" #include "bacnet/basic/object/trendlog.h"
#if defined(INTRINSIC_REPORTING) #if defined(INTRINSIC_REPORTING)
#include "bacnet/basic/object/nc.h" #include "bacnet/basic/object/nc.h"
@@ -164,6 +166,14 @@ static object_functions_t My_Object_Table[] = {
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */, NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */, NULL /* Timer */ }, NULL /* Create */, NULL /* Delete */, NULL /* Timer */ },
{ OBJECT_CALENDAR, Calendar_Init, Calendar_Count,
Calendar_Index_To_Instance, Calendar_Valid_Instance,
Calendar_Object_Name, Calendar_Read_Property,
Calendar_Write_Property, Calendar_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */, NULL /* Timer */ },
{ OBJECT_CHARACTERSTRING_VALUE, CharacterString_Value_Init, { OBJECT_CHARACTERSTRING_VALUE, CharacterString_Value_Init,
CharacterString_Value_Count, CharacterString_Value_Index_To_Instance, CharacterString_Value_Count, CharacterString_Value_Index_To_Instance,
CharacterString_Value_Valid_Instance, CharacterString_Value_Object_Name, CharacterString_Value_Valid_Instance, CharacterString_Value_Object_Name,
@@ -337,6 +347,14 @@ static object_functions_t My_Object_Table[] = {
NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */, NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */,
NULL /* Remove_List_Element */, NULL /* Create */, NULL /* Delete */, NULL /* Remove_List_Element */, NULL /* Create */, NULL /* Delete */,
NULL /* Timer */ }, NULL /* Timer */ },
{ OBJECT_TIME_VALUE, Time_Value_Init, Time_Value_Count,
Time_Value_Index_To_Instance, Time_Value_Valid_Instance,
Time_Value_Object_Name, Time_Value_Read_Property,
Time_Value_Write_Property, Time_Value_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */, NULL /* Timer */ },
{ OBJECT_ACCUMULATOR, Accumulator_Init, Accumulator_Count, { OBJECT_ACCUMULATOR, Accumulator_Init, Accumulator_Count,
Accumulator_Index_To_Instance, Accumulator_Valid_Instance, Accumulator_Index_To_Instance, Accumulator_Valid_Instance,
Accumulator_Object_Name, Accumulator_Read_Property, Accumulator_Object_Name, Accumulator_Read_Property,
+741
View File
@@ -0,0 +1,741 @@
/**
* @file
* @author Steve Karg <skarg@users.sourceforge.net>
* @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
* @date June 2023
* @brief Time Value objects used by a BACnet device object
*
* @section DESCRIPTION
*
* The Time Value object is an object with a present-value that
* uses an bacnet time data type.
*
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/config.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacerror.h"
#include "bacnet/bacapp.h"
#include "bacnet/bactext.h"
#include "bacnet/cov.h"
#include "bacnet/apdu.h"
#include "bacnet/npdu.h"
#include "bacnet/abort.h"
#include "bacnet/proplist.h"
#include "bacnet/reject.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/sys/keylist.h"
/* me! */
#include "time_value.h"
struct object_data {
bool Changed : 1;
bool Write_Enabled : 1;
bool Out_Of_Service : 1;
BACNET_TIME Present_Value;
const char *Object_Name;
const char *Description;
};
/* Key List for storing the object data sorted by instance number */
static OS_Keylist Object_List;
/* callback for present value writes */
static time_value_write_present_value_callback Time_Value_Write_Present_Value_Callback;
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Time_Value_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
-1 };
static const int Time_Value_Properties_Optional[] = { PROP_DESCRIPTION,
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, -1 };
static const int Time_Value_Properties_Proprietary[] = { -1 };
/* standard properties that are arrays for this object,
but not necessary supported in this object */
static const int BACnetARRAY_Properties[] = {
PROP_EVENT_TIME_STAMPS, PROP_EVENT_MESSAGE_TEXTS,
PROP_EVENT_MESSAGE_TEXTS_CONFIG,
PROP_VALUE_SOURCE_ARRAY, PROP_COMMAND_TIME_ARRAY, PROP_TAGS, -1 };
/**
* Returns the list of required, optional, and proprietary properties.
* Used by ReadPropertyMultiple service.
*
* @param pRequired - pointer to list of int terminated by -1, of
* BACnet required properties for this object.
* @param pOptional - pointer to list of int terminated by -1, of
* BACnet optkional properties for this object.
* @param pProprietary - pointer to list of int terminated by -1, of
* BACnet proprietary properties for this object.
*/
void Time_Value_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Time_Value_Properties_Required;
}
if (pOptional) {
*pOptional = Time_Value_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Time_Value_Properties_Proprietary;
}
return;
}
/**
* Determines if a given Time Value instance is valid
*
* @param object_instance - object-instance number of the object
*
* @return true if the instance is valid, and false if not
*/
bool Time_Value_Valid_Instance(uint32_t object_instance)
{
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
return true;
}
return false;
}
/**
* Determines the number of Time Value objects
*
* @return Number of Time Value objects
*/
unsigned Time_Value_Count(void)
{
return Keylist_Count(Object_List);
}
/**
* Determines the object instance-number for a given 0..N index
* of Time Value objects where N is Time_Value_Count().
*
* @param index - 0..N where N is Time_Value_Count()
*
* @return object instance-number for the given index
*/
uint32_t Time_Value_Index_To_Instance(unsigned index)
{
return Keylist_Key(Object_List, index);
}
/**
* For a given object instance-number, determines a 0..N index
* of Time Value objects where N is Time_Value_Count().
*
* @param object_instance - object-instance number of the object
*
* @return index for the given instance-number, or Time_Value_Count()
* if not valid.
*/
unsigned Time_Value_Instance_To_Index(uint32_t object_instance)
{
return Keylist_Index(Object_List, object_instance);
}
/**
* For a given object instance-number, determines the present-value
*
* @param object_instance - object-instance number of the object
*
* @return present-value of the object
*/
bool Time_Value_Present_Value(uint32_t object_instance, BACNET_TIME *value)
{
bool status = false;
struct object_data *pObject;
BACNET_DATE date;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (pObject->Out_Of_Service) {
*value = pObject->Present_Value;
status = true;
} else {
status = datetime_local(&date, value, NULL, NULL);
}
}
return status;
}
/**
* For a given object instance-number, sets the present-value
*
* @param object_instance - object-instance number of the object
* @param value - indicator of 'present value'
*
* @return true if values are within range and present-value is set.
*/
bool Time_Value_Present_Value_Set(uint32_t object_instance, BACNET_TIME *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Present_Value = *value;
status = true;
}
return status;
}
/**
* For a given object instance-number, sets the present-value
*
* @param object_instance - object-instance number of the object
* @param value - Bacnet time data object
* @param priority - priority-array index value 1..16
* @param error_class - the BACnet error class
* @param error_code - BACnet Error code
*
* @return true if values are within range and present-value is set.
*/
static bool Time_Value_Present_Value_Write(uint32_t object_instance,
BACNET_TIME *value,
uint8_t priority,
BACNET_ERROR_CLASS *error_class,
BACNET_ERROR_CODE *error_code)
{
bool status = false;
struct object_data *pObject;
BACNET_TIME old_value = {0};
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
(void)priority;
if (pObject->Write_Enabled) {
old_value = pObject->Present_Value;
pObject->Present_Value = *value;
if (Time_Value_Write_Present_Value_Callback) {
Time_Value_Write_Present_Value_Callback(
object_instance, &old_value, value);
}
status = true;
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
}
} else {
*error_class = ERROR_CLASS_OBJECT;
*error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return status;
}
/**
* For a given object instance-number, returns the Out-of-service value
*
* @param object_instance - object-instance number of the object
*
* @return Out-of-service value of the object
*/
bool Time_Value_Out_Of_Service(uint32_t object_instance)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = pObject->Out_Of_Service;
}
return status;
}
/**
* For a given object instance-number, sets the Out-of-service value
*
* @param object_instance - object-instance number of the object
* @param value - indicator of 'Out-of-service'
*
* @return true if Out-of-service value is set.
*/
bool Time_Value_Out_Of_Service_Set(uint32_t object_instance, bool value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Out_Of_Service = value;
status = true;
}
return status;
}
/**
* For a given object instance-number, loads the object-name into
* a characterstring. Note that the object name must be unique
* within this device.
*
* @param object_instance - object-instance number of the object
* @param object_name - holds the object-name retrieved
*
* @return true if object-name was retrieved
*/
bool Time_Value_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
bool status = false;
struct object_data *pObject;
char name_text[16] = "Time-4194303";
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (pObject->Object_Name) {
status =
characterstring_init_ansi(object_name, pObject->Object_Name);
} else {
snprintf(name_text, sizeof(name_text), "Time-%u", object_instance);
status = characterstring_init_ansi(object_name, name_text);
}
}
return status;
}
/**
* For a given object instance-number, sets the object-name
* Note that the object name must be unique within this device.
*
* @param object_instance - object-instance number of the object
* @param new_name - holds the object-name to be set
*
* @return true if object-name was set
*/
bool Time_Value_Name_Set(uint32_t object_instance, char *new_name)
{
bool status = false; /* return value */
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject && new_name) {
status = true;
pObject->Object_Name = new_name;
}
return status;
}
/**
* For a given object instance-number, returns the description
*
* @param object_instance - object-instance number of the object
*
* @return description text or NULL if not found
*/
char *Time_Value_Description(uint32_t object_instance)
{
char *name = NULL;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (pObject->Description) {
name = (char *)pObject->Description;
} else {
name = "";
}
}
return name;
}
/**
* For a given object instance-number, sets the description
*
* @param object_instance - object-instance number of the object
* @param new_name - holds the description to be set
*
* @return true if object-name was set
*/
bool Time_Value_Description_Set(uint32_t object_instance, char *new_name)
{
bool status = false; /* return value */
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = true;
pObject->Description = new_name;
}
return status;
}
/**
* @brief Determine if the object property is a member of this object instance
* @param object_instance - object-instance number of the object
* @param object_property - object-property to be checked
* @return true if the property is a member of this object instance
*/
static bool Property_List_Member(
uint32_t object_instance, int object_property)
{
bool found = false;
const int *pRequired = NULL;
const int *pOptional = NULL;
const int *pProprietary = NULL;
(void)object_instance;
Time_Value_Property_Lists(
&pRequired, &pOptional, &pProprietary);
found = property_list_member(pRequired, object_property);
if (!found) {
found = property_list_member(pOptional, object_property);
}
if (!found) {
found = property_list_member(pProprietary, object_property);
}
return found;
}
/**
* @brief Determine if the object property is a BACnetARRAY property
* @param object_property - object-property to be checked
* @return true if the property is a BACnetARRAY property
*/
static bool BACnetARRAY_Property(
int object_property)
{
return property_list_member(
BACnetARRAY_Properties, object_property);
}
/**
* ReadProperty handler for this object. For the given ReadProperty
* data, the application_data is loaded or the error flags are set.
*
* @param rpdata - BACNET_READ_PROPERTY_DATA data, including
* requested data and space for the reply, or error response.
*
* @return number of APDU bytes in the response, or
* BACNET_STATUS_ERROR on error.
*/
int Time_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
uint8_t *apdu = NULL;
BACNET_TIME value;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], rpdata->object_type, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Time_Value_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len =
encode_application_enumerated(&apdu[0], rpdata->object_type);
break;
case PROP_PRESENT_VALUE:
if (Time_Value_Present_Value(rpdata->object_instance, &value)) {
apdu_len = encode_application_time(apdu, &value);
}
break;
case PROP_STATUS_FLAGS:
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
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,
Time_Value_Out_Of_Service(rpdata->object_instance));
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_OUT_OF_SERVICE:
apdu_len = encode_application_boolean(&apdu[0],
Time_Value_Out_Of_Service(rpdata->object_instance));
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(
&char_string, Time_Value_Description(rpdata->object_instance));
apdu_len = encode_application_character_string(apdu, &char_string);
break;
case PROP_EVENT_STATE:
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) &&
(!BACnetARRAY_Property(rpdata->object_property)) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/**
* WriteProperty handler for this object. For the given WriteProperty
* data, the application_data is loaded or the error flags are set.
*
* @param wp_data - BACNET_WRITE_PROPERTY_DATA data, including
* requested data and space for the reply, or error response.
*
* @return false if an error is loaded, true if no errors
*/
bool Time_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
BACNET_APPLICATION_DATA_VALUE value;
int len = 0;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
if ((!BACnetARRAY_Property(wp_data->object_property)) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
/* only array properties can have array options */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
if (Time_Value_Out_Of_Service(wp_data->object_instance)) {
status = write_property_type_valid(wp_data, &value,
BACNET_APPLICATION_TAG_TIME);
if (status) {
status = Time_Value_Present_Value_Write(
wp_data->object_instance,
&value.type.Time, wp_data->priority,
&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;
}
break;
default:
if (Property_List_Member(
wp_data->object_instance, wp_data->object_property)) {
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_UNKNOWN_PROPERTY;
}
break;
}
return status;
}
/**
* @brief Sets a callback used when present-value is written from BACnet
* @param cb - callback used to provide indications
*/
void Time_Value_Write_Present_Value_Callback_Set(
time_value_write_present_value_callback cb)
{
Time_Value_Write_Present_Value_Callback = cb;
}
/**
* @brief Determines the status flags for a given object instance-number
* @param object_instance - object-instance number of the object
* @return status flags bitstring octet
*/
uint8_t Time_Value_Status_Flags(uint32_t object_instance)
{
BACNET_BIT_STRING bit_string;
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
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,
Time_Value_Out_Of_Service(object_instance));
return bitstring_octet(&bit_string, 0);
}
/**
* @brief Determines a object write-enabled flag state
* @param object_instance - object-instance number of the object
* @return write-enabled status flag
*/
bool Time_Value_Write_Enabled(uint32_t object_instance)
{
bool value = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
value = pObject->Write_Enabled;
}
return value;
}
/**
* @brief For a given object instance-number, sets the write-enabled flag
* @param object_instance - object-instance number of the object
*/
void Time_Value_Write_Enable(uint32_t object_instance)
{
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Write_Enabled = true;
}
}
/**
* @brief For a given object instance-number, clears the write-enabled flag
* @param object_instance - object-instance number of the object
*/
void Time_Value_Write_Disable(uint32_t object_instance)
{
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Write_Enabled = false;
}
}
/**
* Creates a Time Value object
* @param object_instance - object-instance number of the object
* @return object_instance if the object is created, else BACNET_MAX_INSTANCE
*/
uint32_t Time_Value_Create(uint32_t object_instance)
{
struct object_data *pObject = NULL;
int index = 0;
if (object_instance > BACNET_MAX_INSTANCE) {
return BACNET_MAX_INSTANCE;
} else if (object_instance == BACNET_MAX_INSTANCE) {
/* wildcard instance */
/* the Object_Identifier property of the newly created object
shall be initialized to a value that is unique within the
responding BACnet-user device. The method used to generate
the object identifier is a local matter.*/
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
}
pObject = Keylist_Data(Object_List, object_instance);
if (!pObject) {
pObject = calloc(1, sizeof(struct object_data));
if (!pObject) {
return BACNET_MAX_INSTANCE;
}
pObject->Object_Name = NULL;
pObject->Description = NULL;
memset(&pObject->Present_Value, 0, sizeof(pObject->Present_Value));
pObject->Changed = false;
pObject->Write_Enabled = false;
/* add to list */
index = Keylist_Data_Add(Object_List, object_instance, pObject);
if (index < 0) {
free(pObject);
return BACNET_MAX_INSTANCE;
}
}
return object_instance;
}
/**
* Deletes an Time Value object
* @param object_instance - object-instance number of the object
* @return true if the object is deleted
*/
bool Time_Value_Delete(uint32_t object_instance)
{
bool status = false;
struct object_data *pObject = NULL;
pObject = Keylist_Data_Delete(Object_List, object_instance);
if (pObject) {
free(pObject);
status = true;
}
return status;
}
/**
* Deletes all the Time Values and their data
*/
void Time_Value_Cleanup(void)
{
struct object_data *pObject;
if (Object_List) {
do {
pObject = Keylist_Data_Pop(Object_List);
if (pObject) {
free(pObject);
}
} while (pObject);
Keylist_Delete(Object_List);
Object_List = NULL;
}
}
/**
* Initializes the Time Value object data
*/
void Time_Value_Init(void)
{
Object_List = Keylist_Create();
if (Object_List) {
atexit(Time_Value_Cleanup);
}
}
+101
View File
@@ -0,0 +1,101 @@
/**
* @file
* @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
* @date June 2023
* @brief API for a Time Value object used by a BACnet device object
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#ifndef BACNET_TIME_VALUE_OBJECT_H
#define BACNET_TIME_VALUE_OBJECT_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacnet_stack_exports.h"
#include "bacnet/config.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
/**
* @brief Callback for gateway write present value request
* @param object_instance - object-instance number of the object
* @param old_value - BACNET_TIME value prior to write
* @param value - BACNET_TIME value of the write
*/
typedef void (*time_value_write_present_value_callback)(uint32_t object_instance,
BACNET_TIME *old_value,
BACNET_TIME *value);
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
BACNET_STACK_EXPORT
void Time_Value_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary);
BACNET_STACK_EXPORT
bool Time_Value_Valid_Instance(uint32_t object_instance);
BACNET_STACK_EXPORT
unsigned Time_Value_Count(void);
BACNET_STACK_EXPORT
uint32_t Time_Value_Index_To_Instance(unsigned index);
BACNET_STACK_EXPORT
unsigned Time_Value_Instance_To_Index(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Time_Value_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name);
BACNET_STACK_EXPORT
bool Time_Value_Name_Set(uint32_t object_instance, char *new_name);
BACNET_STACK_EXPORT
int Time_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata);
BACNET_STACK_EXPORT
bool Time_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data);
BACNET_STACK_EXPORT
bool Time_Value_Present_Value_Set(uint32_t object_instance, BACNET_TIME *value);
BACNET_STACK_EXPORT
bool Time_Value_Present_Value(uint32_t object_instance, BACNET_TIME *value);
BACNET_STACK_EXPORT
void Time_Value_Write_Present_Value_Callback_Set(
time_value_write_present_value_callback cb);
BACNET_STACK_EXPORT
uint8_t Time_Value_Status_Flags(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Time_Value_Out_Of_Service(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Time_Value_Out_Of_Service_Set(uint32_t object_instance, bool oos_flag);
BACNET_STACK_EXPORT
char *Time_Description(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Time_Value_Description_Set(uint32_t object_instance, char *new_name);
BACNET_STACK_EXPORT
bool Time_Value_Write_Enabled(uint32_t instance);
BACNET_STACK_EXPORT
void Time_Value_Write_Enable(uint32_t instance);
BACNET_STACK_EXPORT
void Time_Value_Write_Disable(uint32_t instance);
BACNET_STACK_EXPORT
uint32_t Time_Value_Create(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Time_Value_Delete(uint32_t object_instance);
BACNET_STACK_EXPORT
void Time_Value_Cleanup(void);
BACNET_STACK_EXPORT
void Time_Value_Init(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+4
View File
@@ -113,6 +113,10 @@ int bacnet_calendar_entry_decode(
if (!apdu || !entry) { if (!apdu || !entry) {
return BACNET_STATUS_REJECT; return BACNET_STATUS_REJECT;
} }
if (apdu_size == 0) {
/* empty list */
return 0;
}
len = bacnet_tag_decode(&apdu[apdu_len], apdu_size - apdu_len, &tag); len = bacnet_tag_decode(&apdu[apdu_len], apdu_size - apdu_len, &tag);
if (len <= 0) { if (len <= 0) {
return BACNET_STATUS_REJECT; return BACNET_STATUS_REJECT;
+2
View File
@@ -119,6 +119,7 @@ list(APPEND testdirs
bacnet/basic/object/bo bacnet/basic/object/bo
bacnet/basic/object/bv bacnet/basic/object/bv
bacnet/basic/object/channel bacnet/basic/object/channel
bacnet/basic/object/calendar
bacnet/basic/object/color_object bacnet/basic/object/color_object
bacnet/basic/object/color_temperature bacnet/basic/object/color_temperature
bacnet/basic/object/command bacnet/basic/object/command
@@ -138,6 +139,7 @@ list(APPEND testdirs
bacnet/basic/object/osv bacnet/basic/object/osv
bacnet/basic/object/piv bacnet/basic/object/piv
bacnet/basic/object/schedule bacnet/basic/object/schedule
bacnet/basic/object/time_value
bacnet/basic/object/trendlog bacnet/basic/object/trendlog
# basic/sys # basic/sys
bacnet/basic/sys/color_rgb bacnet/basic/sys/color_rgb
+4 -2
View File
@@ -27,17 +27,19 @@ static void testBinaryLightingOutput(void)
uint8_t apdu[MAX_APDU] = { 0 }; uint8_t apdu[MAX_APDU] = { 0 };
int len = 0, test_len = 0; int len = 0, test_len = 0;
BACNET_READ_PROPERTY_DATA rpdata; BACNET_READ_PROPERTY_DATA rpdata;
BACNET_WRITE_PROPERTY_DATA wpdata = { 0 };
BACNET_APPLICATION_DATA_VALUE value = { 0 }; BACNET_APPLICATION_DATA_VALUE value = { 0 };
const int *pRequired = NULL; const int *pRequired = NULL;
const int *pOptional = NULL; const int *pOptional = NULL;
const int *pProprietary = NULL; const int *pProprietary = NULL;
const uint32_t instance = 123; const uint32_t instance = 123;
BACNET_WRITE_PROPERTY_DATA wpdata = { 0 }; uint32_t test_instance = 0;
bool status = false; bool status = false;
unsigned index; unsigned index;
Binary_Lighting_Output_Init(); Binary_Lighting_Output_Init();
Binary_Lighting_Output_Create(instance); test_instance = Binary_Lighting_Output_Create(instance);
zassert_equal(test_instance, instance, NULL);
status = Binary_Lighting_Output_Valid_Instance(instance); status = Binary_Lighting_Output_Valid_Instance(instance);
zassert_true(status, NULL); zassert_true(status, NULL);
index = Binary_Lighting_Output_Instance_To_Index(instance); index = Binary_Lighting_Output_Instance_To_Index(instance);
@@ -0,0 +1,69 @@
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(test_${basename}
VERSION 1.0.0
LANGUAGES C)
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z_/-]*$"
"/src"
SRC_DIR
${CMAKE_CURRENT_SOURCE_DIR})
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z_/-]*$"
"/test"
TST_DIR
${CMAKE_CURRENT_SOURCE_DIR})
set(ZTST_DIR "${TST_DIR}/ztest/src")
add_compile_definitions(
BIG_ENDIAN=0
CONFIG_ZTEST=1
)
include_directories(
${SRC_DIR}
${TST_DIR}/ztest/include
)
add_executable(${PROJECT_NAME}
# File(s) under test
${SRC_DIR}/bacnet/basic/object/calendar.c
# Support files and stubs (pathname alphabetical)
${SRC_DIR}/bacnet/bacaddr.c
${SRC_DIR}/bacnet/bacapp.c
${SRC_DIR}/bacnet/bacdcode.c
${SRC_DIR}/bacnet/bacdest.c
${SRC_DIR}/bacnet/bacdevobjpropref.c
${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bacreal.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bactext.c
${SRC_DIR}/bacnet/basic/sys/bigend.c
${SRC_DIR}/bacnet/basic/sys/keylist.c
${SRC_DIR}/bacnet/cov.c
${SRC_DIR}/bacnet/datetime.c
${SRC_DIR}/bacnet/basic/sys/days.c
${SRC_DIR}/bacnet/indtext.c
${SRC_DIR}/bacnet/hostnport.c
${SRC_DIR}/bacnet/lighting.c
${SRC_DIR}/bacnet/proplist.c
${SRC_DIR}/bacnet/timestamp.c
${SRC_DIR}/bacnet/memcopy.c
${SRC_DIR}/bacnet/wp.c
${SRC_DIR}/bacnet/weeklyschedule.c
${SRC_DIR}/bacnet/calendar_entry.c
${SRC_DIR}/bacnet/special_event.c
${SRC_DIR}/bacnet/bactimevalue.c
${SRC_DIR}/bacnet/dailyschedule.c
# Test and test library files
./src/main.c
./stubs.c
../mock/device_mock.c
${ZTST_DIR}/ztest_mock.c
${ZTST_DIR}/ztest.c
)
@@ -0,0 +1,239 @@
/**
* @file
* @brief Unit test for BACnet Calendar object encode/decode APIs
* @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
* @author Steve Karg <skarg@users.sourceforge.net>
* @date June 2023
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/ztest.h>
#include <bacnet/basic/object/calendar.h>
#include <bacnet/bactext.h>
/**
* @addtogroup bacnet_tests
* @{
*/
/**
* @brief Test Calendar handling
*/
static void testCalendar(void)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0, test_len = 0;
BACNET_READ_PROPERTY_DATA rpdata = { 0 };
BACNET_WRITE_PROPERTY_DATA wpdata = { 0 };
BACNET_APPLICATION_DATA_VALUE value = {0};
const int *pRequired = NULL;
const int *pOptional = NULL;
const int *pProprietary = NULL;
const uint32_t instance = 1;
uint32_t test_instance = 0;
bool status = false;
unsigned index;
Calendar_Init();
test_instance = Calendar_Create(instance);
zassert_equal(test_instance, instance, NULL);
status = Calendar_Valid_Instance(instance);
zassert_true(status, NULL);
index = Calendar_Instance_To_Index(instance);
zassert_equal(index, 0, NULL);
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_CALENDAR;
rpdata.object_instance = instance;
rpdata.array_index = BACNET_ARRAY_ALL;
Calendar_Property_Lists(&pRequired, &pOptional, &pProprietary);
while ((*pRequired) >= 0) {
rpdata.object_property = *pRequired;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Calendar_Read_Property(&rpdata);
zassert_not_equal(len, BACNET_STATUS_ERROR,
"property '%s': failed to ReadProperty!\n",
bactext_property_name(rpdata.object_property));
if (len >= 0) {
test_len = bacapp_decode_known_property(rpdata.application_data,
len, &value, rpdata.object_type, rpdata.object_property);
zassert_equal(len, test_len,
"property '%s': failed to decode!\n",
bactext_property_name(rpdata.object_property));
/* check WriteProperty properties */
wpdata.object_type = rpdata.object_type;
wpdata.object_instance = rpdata.object_instance;
wpdata.object_property = rpdata.object_property;
wpdata.array_index = rpdata.array_index;
memcpy(&wpdata.application_data, rpdata.application_data, MAX_APDU);
wpdata.application_data_len = len;
wpdata.error_code = ERROR_CODE_SUCCESS;
status = Calendar_Write_Property(&wpdata);
if (!status) {
/* verify WriteProperty property is known */
zassert_not_equal(wpdata.error_code,
ERROR_CODE_UNKNOWN_PROPERTY,
"property '%s': WriteProperty Unknown!\n",
bactext_property_name(rpdata.object_property));
}
}
pRequired++;
}
while ((*pOptional) != -1) {
rpdata.object_property = *pOptional;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Calendar_Read_Property(&rpdata);
zassert_not_equal(len, BACNET_STATUS_ERROR,
"property '%s': failed to ReadProperty!\n",
bactext_property_name(rpdata.object_property));
if (len > 0) {
test_len = bacapp_decode_application_data(rpdata.application_data,
(uint8_t)rpdata.application_data_len, &value);
zassert_equal(len, test_len, "property '%s': failed to decode!\n",
bactext_property_name(rpdata.object_property));
/* check WriteProperty properties */
wpdata.object_type = rpdata.object_type;
wpdata.object_instance = rpdata.object_instance;
wpdata.object_property = rpdata.object_property;
wpdata.array_index = rpdata.array_index;
memcpy(&wpdata.application_data, rpdata.application_data, MAX_APDU);
wpdata.application_data_len = len;
wpdata.error_code = ERROR_CODE_SUCCESS;
status = Calendar_Write_Property(&wpdata);
if (!status) {
/* verify WriteProperty property is known */
zassert_not_equal(wpdata.error_code,
ERROR_CODE_UNKNOWN_PROPERTY,
"property '%s': WriteProperty Unknown!\n",
bactext_property_name(rpdata.object_property));
}
}
pOptional++;
}
/* check for unsupported property - use ALL */
rpdata.object_property = PROP_ALL;
len = Calendar_Read_Property(&rpdata);
zassert_equal(len, BACNET_STATUS_ERROR, NULL);
status = Calendar_Write_Property(&wpdata);
zassert_false(status, NULL);
/* check the delete function */
status = Calendar_Delete(instance);
zassert_true(status, NULL);
}
static void testPresentValue(void)
{
const uint32_t instance = 1;
BACNET_DATE date;
BACNET_TIME time;
BACNET_CALENDAR_ENTRY entry;
BACNET_CALENDAR_ENTRY *value;
uint32_t test_instance = 0;
Calendar_Init();
test_instance = Calendar_Create(instance);
zassert_equal(test_instance, instance, NULL);
datetime_local(&date, &time, NULL, NULL);
// test Date
zassert_false(Calendar_Present_Value(instance), NULL);
zassert_equal(0, Calendar_Date_List_Count(instance), NULL);
entry.tag = BACNET_CALENDAR_DATE;
entry.type.Date = date;
entry.type.Date.day++;
Calendar_Date_List_Add(instance, &entry);
zassert_equal(1, Calendar_Date_List_Count(instance), NULL);
zassert_false(Calendar_Present_Value(instance), NULL);
entry.type.Date = date;
Calendar_Date_List_Add(instance, &entry);
zassert_equal(2, Calendar_Date_List_Count(instance), NULL);
zassert_true(Calendar_Present_Value(instance), NULL);
value = Calendar_Date_List_Get(instance, 1);
value->type.Date.day += 2;
zassert_equal(2, Calendar_Date_List_Count(instance), NULL);
zassert_false(Calendar_Present_Value(instance), NULL);
// test Date Range
entry.tag = BACNET_CALENDAR_DATE_RANGE;
entry.type.DateRange.startdate = date;
entry.type.DateRange.enddate = date;
entry.type.DateRange.startdate.day += 2;
entry.type.DateRange.enddate.day += 10;
Calendar_Date_List_Add(instance, &entry);
zassert_equal(3, Calendar_Date_List_Count(instance), NULL);
zassert_false(Calendar_Present_Value(instance), NULL);
value = Calendar_Date_List_Get(instance, 2);
value->type.DateRange.startdate.day = date.day;
zassert_true(Calendar_Present_Value(instance), NULL);
if (date.day > 1) {
value->type.DateRange.startdate.day --;
value->type.DateRange.enddate.day = date.day;
zassert_true(Calendar_Present_Value(instance), NULL);
}
value->type.DateRange.startdate.day = date.day + 2;
value->type.DateRange.enddate.day = date.day + 2;
zassert_false(Calendar_Present_Value(instance), NULL);
// test WeekNDay
entry.tag = BACNET_CALENDAR_WEEK_N_DAY;
entry.type.WeekNDay.month = 0xff;
entry.type.WeekNDay.weekofmonth = 0xff;
entry.type.WeekNDay.dayofweek = 0xff;
Calendar_Date_List_Add(instance, &entry);
zassert_equal(4, Calendar_Date_List_Count(instance), NULL);
value = Calendar_Date_List_Get(instance, 3);
zassert_true(Calendar_Present_Value(instance), NULL);
value->type.WeekNDay.month = date.month;
zassert_true(Calendar_Present_Value(instance), NULL);
value->type.WeekNDay.month++;
zassert_false(Calendar_Present_Value(instance), NULL);
value->type.WeekNDay.month = (date.month % 2) ? 13 : 14;
zassert_true(Calendar_Present_Value(instance), NULL);
value->type.WeekNDay.month = (date.month % 2) ? 14 : 13;
zassert_false(Calendar_Present_Value(instance), NULL);
value->type.WeekNDay.month = 0xff;
value->type.WeekNDay.weekofmonth = (date.day - 1) % 7 + 1;
zassert_true(Calendar_Present_Value(instance), NULL);
value->type.WeekNDay.weekofmonth++;
if (value->type.WeekNDay.weekofmonth >5)
value->type.WeekNDay.weekofmonth = 1;
zassert_false(Calendar_Present_Value(instance), NULL);
value->type.WeekNDay.weekofmonth = 0xff;
value->type.WeekNDay.dayofweek = date.wday;
zassert_true(Calendar_Present_Value(instance), NULL);
value->type.WeekNDay.dayofweek++;
if (value->type.WeekNDay.dayofweek > 7)
value->type.WeekNDay.dayofweek = 1;
zassert_false(Calendar_Present_Value(instance), NULL);
Calendar_Date_List_Delete_All(instance);
zassert_equal(0, Calendar_Date_List_Count(instance), NULL);
zassert_true(Calendar_Delete(instance), NULL);
}
/**
* @}
*/
void test_main(void)
{
ztest_test_suite(calendar_tests,
ztest_unit_test(testCalendar),
ztest_unit_test(testPresentValue)
);
ztest_run_test_suite(calendar_tests);
}
+28
View File
@@ -0,0 +1,28 @@
/**
* @file
* @brief Stub functions for unit test of a BACnet object
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2022
*
* SPDX-License-Identifier: MIT
*/
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/datetime.h"
bool datetime_local(
BACNET_DATE * bdate,
BACNET_TIME * btime,
int16_t * utc_offset_minutes,
bool * dst_active)
{
bdate->year = 2023;
bdate->month = 6;
bdate->day = 26;
bdate->wday = 1;
(void)btime;
(void)utc_offset_minutes;
(void)dst_active;
return true;
}
@@ -55,6 +55,7 @@ add_executable(${PROJECT_NAME}
${SRC_DIR}/bacnet/basic/object/blo.c ${SRC_DIR}/bacnet/basic/object/blo.c
${SRC_DIR}/bacnet/basic/object/bo.c ${SRC_DIR}/bacnet/basic/object/bo.c
${SRC_DIR}/bacnet/basic/object/bv.c ${SRC_DIR}/bacnet/basic/object/bv.c
${SRC_DIR}/bacnet/basic/object/calendar.c
${SRC_DIR}/bacnet/basic/object/channel.c ${SRC_DIR}/bacnet/basic/object/channel.c
${SRC_DIR}/bacnet/basic/object/color_object.c ${SRC_DIR}/bacnet/basic/object/color_object.c
${SRC_DIR}/bacnet/basic/object/color_temperature.c ${SRC_DIR}/bacnet/basic/object/color_temperature.c
@@ -71,6 +72,7 @@ add_executable(${PROJECT_NAME}
${SRC_DIR}/bacnet/basic/object/osv.c ${SRC_DIR}/bacnet/basic/object/osv.c
${SRC_DIR}/bacnet/basic/object/piv.c ${SRC_DIR}/bacnet/basic/object/piv.c
${SRC_DIR}/bacnet/basic/object/schedule.c ${SRC_DIR}/bacnet/basic/object/schedule.c
${SRC_DIR}/bacnet/basic/object/time_value.c
${SRC_DIR}/bacnet/basic/object/trendlog.c ${SRC_DIR}/bacnet/basic/object/trendlog.c
${SRC_DIR}/bacnet/basic/service/h_apdu.c ${SRC_DIR}/bacnet/basic/service/h_apdu.c
${SRC_DIR}/bacnet/basic/service/h_cov.c ${SRC_DIR}/bacnet/basic/service/h_cov.c
@@ -0,0 +1,69 @@
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(test_${basename}
VERSION 1.0.0
LANGUAGES C)
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z_/-]*$"
"/src"
SRC_DIR
${CMAKE_CURRENT_SOURCE_DIR})
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z_/-]*$"
"/test"
TST_DIR
${CMAKE_CURRENT_SOURCE_DIR})
set(ZTST_DIR "${TST_DIR}/ztest/src")
add_compile_definitions(
BIG_ENDIAN=0
CONFIG_ZTEST=1
)
include_directories(
${SRC_DIR}
${TST_DIR}/ztest/include
)
add_executable(${PROJECT_NAME}
# File(s) under test
${SRC_DIR}/bacnet/basic/object/time_value.c
# Support files and stubs (pathname alphabetical)
${SRC_DIR}/bacnet/bacaddr.c
${SRC_DIR}/bacnet/bacapp.c
${SRC_DIR}/bacnet/bacdcode.c
${SRC_DIR}/bacnet/bacdest.c
${SRC_DIR}/bacnet/bacdevobjpropref.c
${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bacreal.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bactext.c
${SRC_DIR}/bacnet/basic/sys/bigend.c
${SRC_DIR}/bacnet/cov.c
${SRC_DIR}/bacnet/datetime.c
${SRC_DIR}/bacnet/basic/sys/days.c
${SRC_DIR}/bacnet/basic/sys/keylist.c
${SRC_DIR}/bacnet/indtext.c
${SRC_DIR}/bacnet/hostnport.c
${SRC_DIR}/bacnet/lighting.c
${SRC_DIR}/bacnet/proplist.c
${SRC_DIR}/bacnet/timestamp.c
${SRC_DIR}/bacnet/memcopy.c
${SRC_DIR}/bacnet/wp.c
${SRC_DIR}/bacnet/weeklyschedule.c
${SRC_DIR}/bacnet/bactimevalue.c
${SRC_DIR}/bacnet/dailyschedule.c
${SRC_DIR}/bacnet/calendar_entry.c
${SRC_DIR}/bacnet/special_event.c
# Test and test library files
./src/main.c
./stubs.c
../mock/device_mock.c
${ZTST_DIR}/ztest_mock.c
${ZTST_DIR}/ztest.c
)
@@ -0,0 +1,141 @@
/**
* @file
* @brief Unit test for BACnet Time Value object encode/decode APIs
* @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
* @author Steve Karg <skarg@users.sourceforge.net>
* @date June 2023
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/ztest.h>
#include <bacnet/basic/object/time_value.h>
#include <bacnet/bactext.h>
/**
* @addtogroup bacnet_tests
* @{
*/
/**
* @brief Test Time Value handling
*/
static void testTimeValue(void)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0, test_len = 0;
BACNET_READ_PROPERTY_DATA rpdata = { 0 };
BACNET_WRITE_PROPERTY_DATA wpdata = { 0 };
BACNET_APPLICATION_DATA_VALUE value = {0};
const int *pRequired = NULL;
const int *pOptional = NULL;
const int *pProprietary = NULL;
const uint32_t instance = 123;
uint32_t test_instance = 0;
bool status = false;
unsigned index;
Time_Value_Init();
test_instance = Time_Value_Create(instance);
zassert_equal(test_instance, instance, NULL);
status = Time_Value_Valid_Instance(instance);
zassert_true(status, NULL);
index = Time_Value_Instance_To_Index(instance);
zassert_equal(index, 0, NULL);
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_TIME_VALUE;
rpdata.object_instance = instance;
rpdata.array_index = BACNET_ARRAY_ALL;
Time_Value_Property_Lists(
&pRequired, &pOptional, &pProprietary);
while ((*pRequired) >= 0) {
rpdata.object_property = *pRequired;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Time_Value_Read_Property(&rpdata);
zassert_not_equal(len, BACNET_STATUS_ERROR,
"property '%s': failed to ReadProperty!\n",
bactext_property_name(rpdata.object_property));
if (len >= 0) {
test_len = bacapp_decode_known_property(rpdata.application_data,
len, &value, rpdata.object_type, rpdata.object_property);
if (rpdata.object_property != PROP_PRIORITY_ARRAY) {
zassert_equal(len, test_len,
"property '%s': failed to decode!\n",
bactext_property_name(rpdata.object_property));
}
/* check WriteProperty properties */
wpdata.object_type = rpdata.object_type;
wpdata.object_instance = rpdata.object_instance;
wpdata.object_property = rpdata.object_property;
wpdata.array_index = rpdata.array_index;
memcpy(&wpdata.application_data, rpdata.application_data, MAX_APDU);
wpdata.application_data_len = len;
wpdata.error_code = ERROR_CODE_SUCCESS;
status = Time_Value_Write_Property(&wpdata);
if (!status) {
/* verify WriteProperty property is known */
zassert_not_equal(wpdata.error_code,
ERROR_CODE_UNKNOWN_PROPERTY,
"property '%s': WriteProperty Unknown!\n",
bactext_property_name(rpdata.object_property));
}
}
pRequired++;
}
while ((*pOptional) != -1) {
rpdata.object_property = *pOptional;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Time_Value_Read_Property(&rpdata);
zassert_not_equal(len, BACNET_STATUS_ERROR,
"property '%s': failed to ReadProperty!\n",
bactext_property_name(rpdata.object_property));
if (len > 0) {
test_len = bacapp_decode_application_data(rpdata.application_data,
(uint8_t)rpdata.application_data_len, &value);
zassert_equal(len, test_len, "property '%s': failed to decode!\n",
bactext_property_name(rpdata.object_property));
/* check WriteProperty properties */
wpdata.object_type = rpdata.object_type;
wpdata.object_instance = rpdata.object_instance;
wpdata.object_property = rpdata.object_property;
wpdata.array_index = rpdata.array_index;
memcpy(&wpdata.application_data, rpdata.application_data, MAX_APDU);
wpdata.application_data_len = len;
wpdata.error_code = ERROR_CODE_SUCCESS;
status = Time_Value_Write_Property(&wpdata);
if (!status) {
/* verify WriteProperty property is known */
zassert_not_equal(wpdata.error_code,
ERROR_CODE_UNKNOWN_PROPERTY,
"property '%s': WriteProperty Unknown!\n",
bactext_property_name(rpdata.object_property));
}
}
pOptional++;
}
/* check for unsupported property - use ALL */
rpdata.object_property = PROP_ALL;
len = Time_Value_Read_Property(&rpdata);
zassert_equal(len, BACNET_STATUS_ERROR, NULL);
status = Time_Value_Write_Property(&wpdata);
zassert_false(status, NULL);
/* check the delete function */
status = Time_Value_Delete(instance);
zassert_true(status, NULL);
return;
}
/**
* @}
*/
void test_main(void)
{
ztest_test_suite(tv_tests,
ztest_unit_test(testTimeValue)
);
ztest_run_test_suite(tv_tests);
}
@@ -0,0 +1,28 @@
/**
* @file
* @brief Stub functions for unit test of a BACnet object
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2022
*
* SPDX-License-Identifier: MIT
*/
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/datetime.h"
bool datetime_local(
BACNET_DATE * bdate,
BACNET_TIME * btime,
int16_t * utc_offset_minutes,
bool * dst_active)
{
bdate->year = 2023;
bdate->month = 6;
bdate->day = 26;
bdate->wday = 1;
(void)btime;
(void)utc_offset_minutes;
(void)dst_active;
return true;
}
-1
View File
@@ -45,7 +45,6 @@ add_executable(${PROJECT_NAME}
${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bactext.c ${SRC_DIR}/bacnet/bactext.c
${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/basic/sys/bigend.c
${SRC_DIR}/bacnet/calendar_entry.c
${SRC_DIR}/bacnet/datetime.c ${SRC_DIR}/bacnet/datetime.c
${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/basic/sys/days.c
${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/indtext.c
+9 -2
View File
@@ -634,8 +634,15 @@ ZTEST_SUITE(datetime_tests, NULL, NULL, NULL, NULL, NULL);
#else #else
void test_main(void) void test_main(void)
{ {
ztest_test_suite(datetime_tests, ztest_unit_test(testBACnetDate), #if 0
ztest_unit_test(testBACnetTime), ztest_unit_test(testBACnetDateTime), ztest_unit_test(testDateEpoch),
ztest_unit_test(testBACnetDateTimeSeconds),
ztest_unit_test(testDayOfYear),
#endif
ztest_test_suite(datetime_tests,
ztest_unit_test(testBACnetDate),
ztest_unit_test(testBACnetTime),
ztest_unit_test(testBACnetDateTime),
ztest_unit_test(testBACnetDayOfWeek), ztest_unit_test(testBACnetDayOfWeek),
ztest_unit_test(testDateEpochConversion), ztest_unit_test(testDateEpochConversion),
ztest_unit_test(testBACnetDateTimeAdd), ztest_unit_test(testBACnetDateTimeAdd),
+4
View File
@@ -112,6 +112,7 @@ set(BACNETSTACK_SRCS
${BACNETSTACK_SRC}/bacnet/basic/object/bi.h ${BACNETSTACK_SRC}/bacnet/basic/object/bi.h
${BACNETSTACK_SRC}/bacnet/basic/object/bo.h ${BACNETSTACK_SRC}/bacnet/basic/object/bo.h
${BACNETSTACK_SRC}/bacnet/basic/object/bv.h ${BACNETSTACK_SRC}/bacnet/basic/object/bv.h
${BACNETSTACK_SRC}/bacnet/basic/object/calendar.h
${BACNETSTACK_SRC}/bacnet/basic/object/channel.h ${BACNETSTACK_SRC}/bacnet/basic/object/channel.h
${BACNETSTACK_SRC}/bacnet/basic/object/color_object.h ${BACNETSTACK_SRC}/bacnet/basic/object/color_object.h
${BACNETSTACK_SRC}/bacnet/basic/object/color_temperature.h ${BACNETSTACK_SRC}/bacnet/basic/object/color_temperature.h
@@ -132,6 +133,7 @@ set(BACNETSTACK_SRCS
${BACNETSTACK_SRC}/bacnet/basic/object/osv.h ${BACNETSTACK_SRC}/bacnet/basic/object/osv.h
${BACNETSTACK_SRC}/bacnet/basic/object/piv.h ${BACNETSTACK_SRC}/bacnet/basic/object/piv.h
${BACNETSTACK_SRC}/bacnet/basic/object/schedule.h ${BACNETSTACK_SRC}/bacnet/basic/object/schedule.h
${BACNETSTACK_SRC}/bacnet/basic/object/time_value.h
${BACNETSTACK_SRC}/bacnet/basic/object/trendlog.h ${BACNETSTACK_SRC}/bacnet/basic/object/trendlog.h
${BACNETSTACK_SRC}/bacnet/basic/service/h_alarm_ack.h ${BACNETSTACK_SRC}/bacnet/basic/service/h_alarm_ack.h
${BACNETSTACK_SRC}/bacnet/basic/service/h_apdu.c ${BACNETSTACK_SRC}/bacnet/basic/service/h_apdu.c
@@ -342,6 +344,7 @@ set(BACNETSTACK_BASIC_SRCS
${BACNETSTACK_SRC}/bacnet/basic/object/bi.c ${BACNETSTACK_SRC}/bacnet/basic/object/bi.c
${BACNETSTACK_SRC}/bacnet/basic/object/bo.c ${BACNETSTACK_SRC}/bacnet/basic/object/bo.c
${BACNETSTACK_SRC}/bacnet/basic/object/bv.c ${BACNETSTACK_SRC}/bacnet/basic/object/bv.c
${BACNETSTACK_SRC}/bacnet/basic/object/calendar.c
${BACNETSTACK_SRC}/bacnet/basic/object/channel.c ${BACNETSTACK_SRC}/bacnet/basic/object/channel.c
#${BACNETSTACK_SRC}/bacnet/basic/object/client/device-client.c #${BACNETSTACK_SRC}/bacnet/basic/object/client/device-client.c
${BACNETSTACK_SRC}/bacnet/basic/object/command.c ${BACNETSTACK_SRC}/bacnet/basic/object/command.c
@@ -364,6 +367,7 @@ set(BACNETSTACK_BASIC_SRCS
${BACNETSTACK_SRC}/bacnet/basic/object/osv.c ${BACNETSTACK_SRC}/bacnet/basic/object/osv.c
${BACNETSTACK_SRC}/bacnet/basic/object/piv.c ${BACNETSTACK_SRC}/bacnet/basic/object/piv.c
${BACNETSTACK_SRC}/bacnet/basic/object/schedule.c ${BACNETSTACK_SRC}/bacnet/basic/object/schedule.c
${BACNETSTACK_SRC}/bacnet/basic/object/time_value.c
${BACNETSTACK_SRC}/bacnet/basic/object/trendlog.c ${BACNETSTACK_SRC}/bacnet/basic/object/trendlog.c
${BACNETSTACK_SRC}/bacnet/basic/service/h_alarm_ack.c ${BACNETSTACK_SRC}/bacnet/basic/service/h_alarm_ack.c
${BACNETSTACK_SRC}/bacnet/basic/service/h_arf_a.c ${BACNETSTACK_SRC}/bacnet/basic/service/h_arf_a.c
+2
View File
@@ -27,6 +27,7 @@ if(CONFIG_BACNET_USE_DYNAMIC_DESCRIPTION)
bi.c bi.c
bo.c bo.c
bv.c bv.c
calendar.c
channel.c channel.c
command.c command.c
credential_data_input.c credential_data_input.c
@@ -42,6 +43,7 @@ if(CONFIG_BACNET_USE_DYNAMIC_DESCRIPTION)
osv.c osv.c
piv.c piv.c
schedule.c schedule.c
time_value.c
trendlog.c trendlog.c
) )
@@ -0,0 +1,74 @@
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.13.1)
# Extract module path and names
string(REGEX REPLACE
"/zephyr/tests/[a-zA-Z_/-]*$" ""
BACNET_BASE
${CMAKE_CURRENT_SOURCE_DIR})
string(REGEX REPLACE
"/zephyr/tests/" "/src/"
BACNET_SRC_PATH
${CMAKE_CURRENT_SOURCE_DIR})
string(REGEX REPLACE
"/zephyr/tests/" "/test/"
BACNET_TEST_PATH
${CMAKE_CURRENT_SOURCE_DIR})
get_filename_component(BACNET_NAME ${BACNET_BASE} NAME)
# Update include path for this module
list(APPEND BACNET_INCLUDE ${BACNET_BASE}/src)
if(BOARD STREQUAL unit_testing)
file(RELATIVE_PATH BACNET_INCLUDE $ENV{ZEPHYR_BASE} ${BACNET_BASE}/src)
list(APPEND INCLUDE ${BACNET_INCLUDE})
list(APPEND SOURCES
${BACNET_SRC_PATH}.c
${BACNET_TEST_PATH}/src/main.c
${BACNET_TEST_PATH}/stubs.c
${BACNET_TEST_PATH}/../mock/device_mock.c
)
get_filename_component(BACNET_OBJECT_SRC ${BACNET_SRC_PATH} PATH)
get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH)
get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH)
list(APPEND SOURCES
${BACNET_SRC}/bacaddr.c
${BACNET_SRC}/bacapp.c
${BACNET_SRC}/bacdcode.c
${BACNET_SRC}/bacdest.c
${BACNET_SRC}/bacstr.c
${BACNET_SRC}/bacint.c
${BACNET_SRC}/bacreal.c
${BACNET_SRC}/datetime.c
${BACNET_SRC}/timestamp.c
${BACNET_SRC}/basic/sys/days.c
${BACNET_SRC}/basic/sys/keylist.c
${BACNET_SRC}/bacdevobjpropref.c
${BACNET_SRC}/bactext.c
${BACNET_SRC}/indtext.c
${BACNET_SRC}/lighting.c
${BACNET_SRC}/proplist.c
${BACNET_SRC}/wp.c
${BACNET_SRC}/hostnport.c
${BACNET_SRC}/calendar_entry.c
${BACNET_SRC}/dailyschedule.c
${BACNET_SRC}/special_event.c
${BACNET_SRC}/weeklyschedule.c
${BACNET_SRC}/basic/sys/bigend.c
${BACNET_SRC}/bactimevalue.c
)
set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf")
find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(${BACNET_NAME})
else()
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(${BACNET_NAME})
target_include_directories(app PRIVATE ${BACNET_INCLUDE})
target_sources(app PRIVATE
${BACNET_TEST_PATH}/src/main.c
)
endif()
@@ -0,0 +1,2 @@
CONFIG_ZTEST=y
CONFIG_BACNETSTACK=y
@@ -0,0 +1,2 @@
CONFIG_ZTEST=y
#CONFIG_BACNETSTACK=y
@@ -0,0 +1,6 @@
tests:
bacnet.basic.object.calendar:
tags: bacnet
bacnet.basic.object.calendar.unit:
tags: bacnet
type: unit
@@ -40,6 +40,7 @@ if(BOARD STREQUAL unit_testing)
${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacstr.c
${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacint.c
${BACNET_SRC}/bacreal.c ${BACNET_SRC}/bacreal.c
${BACNET_SRC}/calendar.c
${BACNET_SRC}/datetime.c ${BACNET_SRC}/datetime.c
${BACNET_SRC}/timestamp.c ${BACNET_SRC}/timestamp.c
${BACNET_SRC}/basic/sys/days.c ${BACNET_SRC}/basic/sys/days.c
@@ -68,6 +69,7 @@ if(BOARD STREQUAL unit_testing)
${BACNET_SRC}/basic/object/bi.c ${BACNET_SRC}/basic/object/bi.c
${BACNET_SRC}/basic/object/bo.c ${BACNET_SRC}/basic/object/bo.c
${BACNET_SRC}/basic/object/bv.c ${BACNET_SRC}/basic/object/bv.c
${BACNET_SRC}/basic/object/calendar.c
${BACNET_SRC}/basic/object/channel.c ${BACNET_SRC}/basic/object/channel.c
${BACNET_SRC}/basic/object/color_object.c ${BACNET_SRC}/basic/object/color_object.c
${BACNET_SRC}/basic/object/color_temperature.c ${BACNET_SRC}/basic/object/color_temperature.c
@@ -84,6 +86,7 @@ if(BOARD STREQUAL unit_testing)
${BACNET_SRC}/basic/object/osv.c ${BACNET_SRC}/basic/object/osv.c
${BACNET_SRC}/basic/object/piv.c ${BACNET_SRC}/basic/object/piv.c
${BACNET_SRC}/basic/object/schedule.c ${BACNET_SRC}/basic/object/schedule.c
${BACNET_SRC}/basic/object/time_value.c
${BACNET_SRC}/basic/object/trendlog.c ${BACNET_SRC}/basic/object/trendlog.c
${BACNET_SRC}/hostnport.c ${BACNET_SRC}/hostnport.c
${BACNET_SRC}/basic/service/h_apdu.c ${BACNET_SRC}/basic/service/h_apdu.c
@@ -0,0 +1,74 @@
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.13.1)
# Extract module path and names
string(REGEX REPLACE
"/zephyr/tests/[a-zA-Z_/-]*$" ""
BACNET_BASE
${CMAKE_CURRENT_SOURCE_DIR})
string(REGEX REPLACE
"/zephyr/tests/" "/src/"
BACNET_SRC_PATH
${CMAKE_CURRENT_SOURCE_DIR})
string(REGEX REPLACE
"/zephyr/tests/" "/test/"
BACNET_TEST_PATH
${CMAKE_CURRENT_SOURCE_DIR})
get_filename_component(BACNET_NAME ${BACNET_BASE} NAME)
# Update include path for this module
list(APPEND BACNET_INCLUDE ${BACNET_BASE}/src)
if(BOARD STREQUAL unit_testing)
file(RELATIVE_PATH BACNET_INCLUDE $ENV{ZEPHYR_BASE} ${BACNET_BASE}/src)
list(APPEND INCLUDE ${BACNET_INCLUDE})
list(APPEND SOURCES
${BACNET_SRC_PATH}.c
${BACNET_TEST_PATH}/src/main.c
${BACNET_TEST_PATH}/stubs.c
${BACNET_TEST_PATH}/../mock/device_mock.c
)
get_filename_component(BACNET_OBJECT_SRC ${BACNET_SRC_PATH} PATH)
get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH)
get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH)
list(APPEND SOURCES
${BACNET_SRC}/bacaddr.c
${BACNET_SRC}/bacapp.c
${BACNET_SRC}/bacdcode.c
${BACNET_SRC}/bacdest.c
${BACNET_SRC}/bacstr.c
${BACNET_SRC}/bacint.c
${BACNET_SRC}/bacreal.c
${BACNET_SRC}/datetime.c
${BACNET_SRC}/timestamp.c
${BACNET_SRC}/basic/sys/days.c
${BACNET_SRC}/basic/sys/keylist.c
${BACNET_SRC}/bacdevobjpropref.c
${BACNET_SRC}/bactext.c
${BACNET_SRC}/indtext.c
${BACNET_SRC}/lighting.c
${BACNET_SRC}/proplist.c
${BACNET_SRC}/wp.c
${BACNET_SRC}/hostnport.c
${BACNET_SRC}/calendar_entry.c
${BACNET_SRC}/dailyschedule.c
${BACNET_SRC}/special_event.c
${BACNET_SRC}/weeklyschedule.c
${BACNET_SRC}/basic/sys/bigend.c
${BACNET_SRC}/bactimevalue.c
)
set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf")
find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(${BACNET_NAME})
else()
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(${BACNET_NAME})
target_include_directories(app PRIVATE ${BACNET_INCLUDE})
target_sources(app PRIVATE
${BACNET_TEST_PATH}/src/main.c
)
endif()
@@ -0,0 +1,2 @@
CONFIG_ZTEST=y
CONFIG_BACNETSTACK=y
@@ -0,0 +1,2 @@
CONFIG_ZTEST=y
#CONFIG_BACNETSTACK=y
@@ -0,0 +1,6 @@
tests:
bacnet.basic.object.time_value:
tags: bacnet
bacnet.basic.object.time_value.unit:
tags: bacnet
type: unit
+1 -1
View File
@@ -53,7 +53,7 @@ if(BOARD STREQUAL unit_testing)
${BACNET_SRC}/dailyschedule.c ${BACNET_SRC}/dailyschedule.c
${BACNET_SRC}/lighting.c ${BACNET_SRC}/lighting.c
${BACNET_SRC}/timestamp.c ${BACNET_SRC}/timestamp.c
${BACNET_SRC}/hostnport.c ${BACNET_SRC}/hostnport.c
) )
set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf") set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf")