Schedule encoding/decoding (#319)

* schedule: add decode_daily_schedule() and encode_daily_schedule()

* schedule: encode/decode implemented + add to bacapp

* add safe encode/decode functions for timevalue, schedule function renaming

* fix unit tests build failing

* add IDEA and test temporary files to .gitignore

* try to make "deprecated" work in MSVC

* add WeeklySchedule compare function

* add bacnet_weeklyschedule_context_decode()

* Add basic test for WeeklySchedule

* Fix WeeklySchedule parsing and snprintf, decoder verified with real hardware

* try to fix windows build

* improve boolean parsing in 'bacapp_parse_application_data'

* add parse function for weekly schedule

* allow types > 16 in bacwp, show the decoded value before sending

* add bacapp binaries to gitignore

* remove bacwp logging

* Add error checking to bacapp_parse_application_data

* try to fix windows build

* fix avr build

* Fix error handling in RP Ack

* add singleDay flag

* show day name in single day weeklyschedule snprintf

* show weeklyschedule inner tag in snprintf

* improve weeklyschedule parsing and printing, supports type names now

* add weekly schedule to bacapp_decode_data

* move bacnet/bacnet_plat_compat.h to bacnet/basic/sys/platform.h

* disable tag limit also in bacwpm

* add ifdef's around strtoX helper functions in bacapp

* move strtox to BACAPP_PRINT_ENABLED ifdef in bacapp

* fix stm32 makefiles

* fix at91sam7s build

* use BACNET_UNSIGNED_INTEGER in BACnet_Short_Application_Data_Value

* fix capitalization in BACnet_Daily_Schedule

* add name to BACNET_TIME_VALUE struct

* change bacwp bacwpm to use bacapp_known_property_tag()

* fix some macros in bacdcode missing parentheses

* Remove dummy fields from BACNET_SHORT_APPLICATION_DATA_VALUE, replace remaining uses of upcasting (adds extra overhead but is maybe safer), rename short DV to Primitive

* fix new ci warnings

* more fixes for ancient C

* fix tests no longer building

* primitive value renamed to shorter name
This commit is contained in:
Ondřej Hruška
2022-09-05 16:27:15 +02:00
committed by GitHub
parent bd40c51e19
commit 7cdab61d72
66 changed files with 1800 additions and 108 deletions
+246 -36
View File
@@ -34,57 +34,195 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h> /* memcpy */
#include "bacnet/bacdcode.h"
#include "bacnet/bactimevalue.h"
#include "bacnet/bacapp.h"
int bacapp_encode_time_value(uint8_t *apdu, BACNET_TIME_VALUE *value)
static bool is_data_value_schedule_compatible(uint8_t tag) {
switch (tag) {
/* Every member of the union must be listed here to allow decoding */
case BACNET_APPLICATION_TAG_NULL:
return true;
#if defined (BACAPP_BOOLEAN)
case BACNET_APPLICATION_TAG_BOOLEAN:
return true;
#endif
#if defined (BACAPP_UNSIGNED)
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
return true;
#endif
#if defined (BACAPP_SIGNED)
case BACNET_APPLICATION_TAG_SIGNED_INT:
return true;
#endif
#if defined (BACAPP_REAL)
case BACNET_APPLICATION_TAG_REAL:
return true;
#endif
#if defined (BACAPP_DOUBLE)
case BACNET_APPLICATION_TAG_DOUBLE:
return true;
#endif
#if defined (BACAPP_ENUMERATED)
case BACNET_APPLICATION_TAG_ENUMERATED:
return true;
#endif
default:
return false;
}
}
int bacnet_time_value_encode(uint8_t *apdu, BACNET_TIME_VALUE *value)
{
int len;
int apdu_len = 0;
uint8_t *apdu_offset = NULL;
len = encode_application_time(&apdu[apdu_len], &value->Time);
if (!value || !is_data_value_schedule_compatible(value->Value.tag)) {
return BACNET_STATUS_ERROR;
}
if (apdu) {
apdu_offset = &apdu[apdu_len];
}
len = encode_application_time(apdu_offset, &value->Time);
apdu_len += len;
len = bacapp_encode_application_data(&apdu[apdu_len], &value->Value);
if (apdu) {
apdu_offset = &apdu[apdu_len];
}
BACNET_APPLICATION_DATA_VALUE adv;
bacnet_primitive_to_application_data_value(&adv, &value->Value);
len = bacapp_encode_application_data(apdu_offset, &adv);
apdu_len += len;
return apdu_len;
}
int bacapp_encode_context_time_value(
int bacapp_encode_time_value(uint8_t *apdu, BACNET_TIME_VALUE *value) {
return bacnet_time_value_encode(apdu, value);
}
int bacnet_time_value_context_encode(
uint8_t *apdu, uint8_t tag_number, BACNET_TIME_VALUE *value)
{
int len;
int apdu_len = 0;
uint8_t *apdu_offset = NULL;
len = encode_opening_tag(&apdu[apdu_len], tag_number);
if (apdu) {
apdu_offset = &apdu[apdu_len];
}
len = encode_opening_tag(apdu_offset, tag_number);
apdu_len += len;
len = bacapp_encode_time_value(&apdu[apdu_len], value);
if (apdu) {
apdu_offset = &apdu[apdu_len];
}
len = bacnet_time_value_encode(apdu_offset, value);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], tag_number);
if (apdu) {
apdu_offset = &apdu[apdu_len];
}
len = encode_closing_tag(apdu_offset, tag_number);
apdu_len += len;
return apdu_len;
}
int bacapp_encode_context_time_value(uint8_t *apdu, uint8_t tag_number, BACNET_TIME_VALUE *value)
{
return bacnet_time_value_context_encode(apdu, tag_number, value);
}
/** returns 0 if OK, -1 on error */
int bacnet_application_to_primitive_data_value(
struct BACnet_Primitive_Data_Value *dest,
const struct BACnet_Application_Data_Value *src)
{
// make sure the value passed is valid
if (!src || !dest || !is_data_value_schedule_compatible(src->tag)) {
return BACNET_STATUS_ERROR;
}
memset(dest, 0, sizeof(struct BACnet_Primitive_Data_Value));
dest->tag = src->tag;
memcpy(&dest->type, &src->type, sizeof(dest->type));
return BACNET_STATUS_OK;
}
/** returns 0 if OK, -1 on error */
int bacnet_primitive_to_application_data_value(
struct BACnet_Application_Data_Value * dest,
const struct BACnet_Primitive_Data_Value * src)
{
// make sure the value passed is valid
if (!dest || !src) {
return BACNET_STATUS_ERROR;
}
memset(dest, 0, sizeof(struct BACnet_Application_Data_Value));
dest->tag = src->tag;
memcpy(&dest->type, &src->type, sizeof(src->type));
return BACNET_STATUS_OK; /* OK */
}
int bacnet_time_value_decode(uint8_t *apdu, int max_apdu_len, BACNET_TIME_VALUE *value)
{
int len;
int apdu_len = 0;
BACNET_APPLICATION_DATA_VALUE full_data_value = { 0 };
len = bacnet_time_application_decode(&apdu[apdu_len], max_apdu_len, &value->Time);
if (len <= 0) {
return -1;
}
apdu_len += len;
len = bacapp_decode_application_data(&apdu[apdu_len], max_apdu_len - apdu_len, &full_data_value);
if (len <= 0) {
return -1;
}
if (BACNET_STATUS_OK !=
bacnet_application_to_primitive_data_value(
&value->Value, &full_data_value)) {
return -1;
}
apdu_len += len;
return apdu_len;
}
int bacapp_decode_time_value(uint8_t *apdu, BACNET_TIME_VALUE *value)
{
return bacnet_time_value_decode(apdu, MAX_APDU, value);
}
int bacnet_time_value_context_decode(uint8_t *apdu, int max_apdu_len, uint8_t tag_number, BACNET_TIME_VALUE *value)
{
int len;
int apdu_len = 0;
len = decode_application_time(&apdu[apdu_len], &value->Time);
if (len <= 0) {
if ((max_apdu_len - apdu_len) >= 1 && decode_is_opening_tag_number(&apdu[apdu_len], tag_number)) {
apdu_len += 1;
} else {
return -1;
}
apdu_len += len;
len = bacapp_decode_application_data(&apdu[apdu_len], 2048, &value->Value);
if (len <= 0) {
len = bacnet_time_value_decode(&apdu[apdu_len], max_apdu_len - apdu_len, value);
if (len > 0) {
apdu_len += len;
} else {
return -1;
}
if ((max_apdu_len - apdu_len) >= 1 && decode_is_closing_tag_number(&apdu[apdu_len], tag_number)) {
apdu_len += 1;
} else {
return -1;
}
apdu_len += len;
return apdu_len;
}
@@ -92,27 +230,99 @@ int bacapp_decode_time_value(uint8_t *apdu, BACNET_TIME_VALUE *value)
int bacapp_decode_context_time_value(
uint8_t *apdu, uint8_t tag_number, BACNET_TIME_VALUE *value)
{
int len = 0;
int section_length;
if (decode_is_opening_tag_number(&apdu[len], tag_number)) {
len++;
} else {
return -1;
}
section_length = bacapp_decode_time_value(&apdu[len], value);
if (section_length > 0) {
len += section_length;
} else {
return -1;
}
if (decode_is_closing_tag_number(&apdu[len], tag_number)) {
len++;
} else {
return -1;
}
return len;
return bacnet_time_value_context_decode(apdu, MAX_APDU, tag_number, value);
}
int bacnet_time_values_context_decode(
uint8_t * apdu,
const int max_apdu_len,
const uint8_t tag_number,
BACNET_TIME_VALUE *time_values,
const unsigned int max_time_values,
unsigned int *out_count)
{
unsigned int j;
int len;
int apdu_len = 0;
unsigned int count_values = 0;
BACNET_TIME_VALUE dummy;
/* day-schedule [0] SEQUENCE OF BACnetTimeValue */
if (decode_is_opening_tag_number(&apdu[apdu_len], tag_number)) {
apdu_len++;
while ((apdu_len < max_apdu_len) &&
!decode_is_closing_tag_number(&apdu[apdu_len], tag_number)) {
if (count_values < max_time_values) {
len = bacnet_time_value_decode(&apdu[apdu_len],
max_apdu_len - apdu_len, &time_values[count_values++]);
} else {
len = bacnet_time_value_decode(
&apdu[apdu_len], max_apdu_len - apdu_len, &dummy);
}
if (len < 0) {
return -1;
}
apdu_len += len;
}
/* Zeroing other values */
for (j = count_values; j < max_time_values; j++) {
time_values[j].Value.tag = BACNET_APPLICATION_TAG_NULL;
time_values[j].Value.type.Unsigned_Int = 0;
time_values[j].Time.hour = 0;
time_values[j].Time.min = 0;
time_values[j].Time.sec = 0;
time_values[j].Time.hundredths = 0;
}
/* overflow ! */
if (apdu_len >= max_apdu_len) {
return -1;
}
apdu_len++; /* closing tag */
if (out_count) {
*out_count = count_values;
}
return apdu_len;
}
return -1;
}
/* Encodes a : [x] SEQUENCE OF BACnetTimeValue into a fixed-size buffer */
int bacnet_time_values_context_encode(
uint8_t * apdu,
uint8_t tag_number,
BACNET_TIME_VALUE *time_values,
unsigned int max_time_values)
{
unsigned int j;
int apdu_len = 0;
int len = 0;
BACNET_TIME t0 = { 0 };
uint8_t *apdu_offset = NULL;
/* day-schedule [x] SEQUENCE OF BACnetTimeValue */
if (apdu) {
apdu_offset = &apdu[apdu_len];
}
apdu_len += encode_opening_tag(apdu_offset, tag_number);
for (j = 0; j < max_time_values; j++)
/* Encode only non-null values (NULL,00:00:00.00) */
if (time_values[j].Value.tag != BACNET_APPLICATION_TAG_NULL ||
datetime_compare_time(&t0, &time_values[j].Time) != 0) {
if (apdu) {
apdu_offset = &apdu[apdu_len];
}
len =
bacnet_time_value_encode(apdu_offset,
&time_values[j]);
if (len < 0)
return -1;
apdu_len += len;
}
/* close tag */
if (apdu) {
apdu_offset = &apdu[apdu_len];
}
apdu_len += encode_closing_tag(apdu_offset, tag_number);
return apdu_len;
}