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:
+246
-36
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user