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:
+16
-16
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 \
|
||||||
|
|||||||
@@ -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 \
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
+2
@@ -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>
|
||||||
|
|||||||
+6
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user