adjust root folder

This commit is contained in:
Steve Karg
2019-10-08 23:47:53 -05:00
parent b6fc50ddea
commit a42e8f507c
1258 changed files with 26 additions and 214 deletions
+255
View File
@@ -0,0 +1,255 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "abort.h"
/** @file abort.c Abort Encoding/Decoding */
/* Helper function to avoid needing additional entries in service data structures
* when passing back abort status.
* Convert from error code to abort code.
* Anything not defined converts to ABORT_REASON_OTHER.
* Will need reworking if it is required to return proprietary abort codes.
*/
BACNET_ABORT_REASON abort_convert_error_code(
BACNET_ERROR_CODE error_code)
{
BACNET_ABORT_REASON abort_code = ABORT_REASON_OTHER;
switch (error_code) {
case ERROR_CODE_ABORT_BUFFER_OVERFLOW:
abort_code = ABORT_REASON_BUFFER_OVERFLOW;
break;
case ERROR_CODE_ABORT_INVALID_APDU_IN_THIS_STATE:
abort_code = ABORT_REASON_INVALID_APDU_IN_THIS_STATE;
break;
case ERROR_CODE_ABORT_PREEMPTED_BY_HIGHER_PRIORITY_TASK:
abort_code = ABORT_REASON_PREEMPTED_BY_HIGHER_PRIORITY_TASK;
break;
case ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED:
abort_code = ABORT_REASON_SEGMENTATION_NOT_SUPPORTED;
break;
case ERROR_CODE_ABORT_PROPRIETARY:
abort_code = ABORT_REASON_PROPRIETARY_FIRST;
break;
case ERROR_CODE_ABORT_OTHER:
default:
abort_code = ABORT_REASON_OTHER;
break;
}
return (abort_code);
}
/* encode service */
int abort_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
uint8_t abort_reason,
bool server)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
if (server)
apdu[0] = PDU_TYPE_ABORT | 1;
else
apdu[0] = PDU_TYPE_ABORT;
apdu[1] = invoke_id;
apdu[2] = abort_reason;
apdu_len = 3;
}
return apdu_len;
}
#if !BACNET_SVC_SERVER
/* decode the service request only */
int abort_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
uint8_t * abort_reason)
{
int len = 0;
if (apdu_len > 0) {
if (invoke_id)
*invoke_id = apdu[0];
if (abort_reason)
*abort_reason = apdu[1];
}
return len;
}
#endif
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
/* decode the whole APDU - mainly used for unit testing */
int abort_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
uint8_t * abort_reason,
bool * server)
{
int len = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu_len > 0) {
if ((apdu[0] & 0xF0) != PDU_TYPE_ABORT)
return -1;
if (apdu[0] & 1)
*server = true;
else
*server = false;
if (apdu_len > 1) {
len =
abort_decode_service_request(&apdu[1], apdu_len - 1, invoke_id,
abort_reason);
}
}
return len;
}
void testAbortAPDU(
Test * pTest,
uint8_t invoke_id,
uint8_t abort_reason,
bool server)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t test_invoke_id = 0;
uint8_t test_abort_reason = 0;
bool test_server = false;
len = abort_encode_apdu(&apdu[0], invoke_id, abort_reason, server);
apdu_len = len;
ct_test(pTest, len != 0);
len =
abort_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_abort_reason, &test_server);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, test_abort_reason == abort_reason);
ct_test(pTest, test_server == server);
return;
}
void testAbort(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 0;
uint8_t test_invoke_id = 0;
uint8_t abort_reason = 0;
uint8_t test_abort_reason = 0;
bool server = false;
bool test_server = false;
len = abort_encode_apdu(&apdu[0], invoke_id, abort_reason, server);
ct_test(pTest, len != 0);
apdu_len = len;
len =
abort_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_abort_reason, &test_server);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, test_abort_reason == abort_reason);
ct_test(pTest, test_server == server);
/* change type to get negative response */
apdu[0] = PDU_TYPE_REJECT;
len =
abort_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_abort_reason, &test_server);
ct_test(pTest, len == -1);
/* test NULL APDU */
len =
abort_decode_apdu(NULL, apdu_len, &test_invoke_id, &test_abort_reason,
&test_server);
ct_test(pTest, len == -1);
/* force a zero length */
len =
abort_decode_apdu(&apdu[0], 0, &test_invoke_id, &test_abort_reason,
&test_server);
ct_test(pTest, len == 0);
/* check them all... */
for (invoke_id = 0; invoke_id < 255; invoke_id++) {
for (abort_reason = 0; abort_reason < 255; abort_reason++) {
testAbortAPDU(pTest, invoke_id, abort_reason, false);
testAbortAPDU(pTest, invoke_id, abort_reason, true);
}
}
}
#ifdef TEST_ABORT
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Abort", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAbort);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ABORT */
#endif /* TEST */
+185
View File
@@ -0,0 +1,185 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include "access_rule.h"
#include "bacdcode.h"
int bacapp_encode_access_rule(
uint8_t * apdu,
BACNET_ACCESS_RULE * rule)
{
int len;
int apdu_len = 0;
len = encode_context_enumerated(&apdu[0], 0, rule->time_range_specifier);
apdu_len += len;
if (rule->time_range_specifier == TIME_RANGE_SPECIFIER_SPECIFIED) {
len =
bacapp_encode_context_device_obj_property_ref(&apdu[apdu_len], 1,
&rule->time_range);
if (len > 0)
apdu_len += len;
else
return -1;
}
len =
encode_context_enumerated(&apdu[apdu_len], 2,
rule->location_specifier);
apdu_len += len;
if (rule->location_specifier == LOCATION_SPECIFIER_SPECIFIED) {
len =
bacapp_encode_context_device_obj_property_ref(&apdu[apdu_len], 3,
&rule->location);
if (len > 0)
apdu_len += len;
else
return -1;
}
len = encode_context_boolean(&apdu[apdu_len], 4, rule->enable);
apdu_len += len;
return apdu_len;
}
int bacapp_encode_context_access_rule(
uint8_t * apdu,
uint8_t tag_number,
BACNET_ACCESS_RULE * rule)
{
int len;
int apdu_len = 0;
len = encode_opening_tag(&apdu[apdu_len], tag_number);
apdu_len += len;
len = bacapp_encode_access_rule(&apdu[apdu_len], rule);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], tag_number);
apdu_len += len;
return apdu_len;
}
int bacapp_decode_access_rule(
uint8_t * apdu,
BACNET_ACCESS_RULE * rule)
{
int len;
int apdu_len = 0;
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
len =
decode_context_enumerated(&apdu[apdu_len], 0,
&rule->time_range_specifier);
if (len < 0)
return -1;
else
apdu_len += len;
} else
return -1;
if (rule->time_range_specifier == TIME_RANGE_SPECIFIER_SPECIFIED) {
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
len =
bacapp_decode_context_device_obj_property_ref(&apdu[apdu_len],
1, &rule->time_range);
if (len < 0)
return -1;
else
apdu_len += len;
} else
return -1;
}
if (decode_is_context_tag(&apdu[apdu_len], 2)) {
len =
decode_context_enumerated(&apdu[apdu_len], 2,
&rule->location_specifier);
if (len < 0)
return -1;
else
apdu_len += len;
} else
return -1;
if (rule->location_specifier == LOCATION_SPECIFIER_SPECIFIED) {
if (decode_is_context_tag(&apdu[apdu_len], 3)) {
len =
bacapp_decode_context_device_obj_property_ref(&apdu[apdu_len],
3, &rule->location);
if (len < 0)
return -1;
else
apdu_len += len;
} else
return -1;
}
if (decode_is_context_tag(&apdu[apdu_len], 4)) {
len = decode_context_boolean2(&apdu[apdu_len], 4, &rule->enable);
if (len < 0)
return -1;
else
apdu_len += len;
} else
return -1;
return apdu_len;
}
int bacapp_decode_context_access_rule(
uint8_t * apdu,
uint8_t tag_number,
BACNET_ACCESS_RULE * rule)
{
int len = 0;
int section_length;
if (decode_is_opening_tag_number(&apdu[len], tag_number)) {
len++;
section_length = bacapp_decode_access_rule(&apdu[len], rule);
if (section_length == -1) {
len = -1;
} else {
len += section_length;
if (decode_is_closing_tag_number(&apdu[len], tag_number)) {
len++;
} else {
len = -1;
}
}
} else {
len = -1;
}
return len;
}
+1171
View File
File diff suppressed because it is too large Load Diff
+281
View File
@@ -0,0 +1,281 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2009 John Minack
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include "alarm_ack.h"
/** @file alarm_ack.c Handles Event Notifications (ACKs) */
/***************************************************
**
** Creates an Unconfirmed Event Notification APDU
**
****************************************************/
int alarm_ack_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_ALARM_ACK_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM; /* service choice */
apdu_len = 4;
len = alarm_ack_encode_service_request(&apdu[apdu_len], data);
apdu_len += len;
}
return apdu_len;
}
/***************************************************
**
** Encodes the service data part of Event Notification
**
****************************************************/
int alarm_ack_encode_service_request(
uint8_t * apdu,
BACNET_ALARM_ACK_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
len =
encode_context_unsigned(&apdu[apdu_len], 0,
data->ackProcessIdentifier);
apdu_len += len;
len =
encode_context_object_id(&apdu[apdu_len], 1,
(int) data->eventObjectIdentifier.type,
data->eventObjectIdentifier.instance);
apdu_len += len;
len =
encode_context_enumerated(&apdu[apdu_len], 2,
data->eventStateAcked);
apdu_len += len;
len =
bacapp_encode_context_timestamp(&apdu[apdu_len], 3,
&data->eventTimeStamp);
apdu_len += len;
len =
encode_context_character_string(&apdu[apdu_len], 4,
&data->ackSource);
apdu_len += len;
len =
bacapp_encode_context_timestamp(&apdu[apdu_len], 5,
&data->ackTimeStamp);
apdu_len += len;
}
return apdu_len;
}
/***************************************************
**
** Decodes the service data part of Event Notification
**
****************************************************/
int alarm_ack_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_ALARM_ACK_DATA * data)
{
int len = 0;
int section_len;
uint32_t enumValue;
/* unused parameter */
apdu_len = apdu_len;
if (-1 == (section_len =
decode_context_unsigned(&apdu[len], 0,
&data->ackProcessIdentifier))) {
return -1;
}
len += section_len;
if (-1 == (section_len =
decode_context_object_id(&apdu[len], 1,
&data->eventObjectIdentifier.type,
&data->eventObjectIdentifier.instance))) {
return -1;
}
len += section_len;
if (-1 == (section_len =
decode_context_enumerated(&apdu[len], 2, &enumValue))) {
return -1;
}
data->eventStateAcked = (BACNET_EVENT_STATE) enumValue;
len += section_len;
if (-1 == (section_len =
bacapp_decode_context_timestamp(&apdu[len], 3,
&data->eventTimeStamp))) {
return -1;
}
len += section_len;
if (-1 == (section_len =
decode_context_character_string(&apdu[len], 4,
&data->ackSource))) {
return -1;
}
len += section_len;
if (-1 == (section_len =
bacapp_decode_context_timestamp(&apdu[len], 5,
&data->ackTimeStamp))) {
return -1;
}
len += section_len;
return len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testAlarmAck(
Test * pTest)
{
BACNET_ALARM_ACK_DATA testAlarmAckIn;
BACNET_ALARM_ACK_DATA testAlarmAckOut;
uint8_t buffer[MAX_APDU];
int inLen;
int outLen;
testAlarmAckIn.ackProcessIdentifier = 0x1234;
characterstring_init_ansi(&testAlarmAckIn.ackSource, "This is a test");
testAlarmAckIn.ackTimeStamp.tag = TIME_STAMP_SEQUENCE;
testAlarmAckIn.ackTimeStamp.value.sequenceNum = 0x4331;
testAlarmAckIn.eventObjectIdentifier.instance = 567;
testAlarmAckIn.eventObjectIdentifier.type = OBJECT_DEVICE;
testAlarmAckIn.eventTimeStamp.tag = TIME_STAMP_TIME;
testAlarmAckIn.eventTimeStamp.value.time.hour = 10;
testAlarmAckIn.eventTimeStamp.value.time.min = 11;
testAlarmAckIn.eventTimeStamp.value.time.sec = 12;
testAlarmAckIn.eventTimeStamp.value.time.hundredths = 14;
testAlarmAckIn.eventStateAcked = EVENT_STATE_OFFNORMAL;
memset(&testAlarmAckOut, 0, sizeof(testAlarmAckOut));
inLen = alarm_ack_encode_service_request(buffer, &testAlarmAckIn);
outLen = alarm_ack_decode_service_request(buffer, inLen, &testAlarmAckOut);
ct_test(pTest, inLen == outLen);
ct_test(pTest,
testAlarmAckIn.ackProcessIdentifier ==
testAlarmAckOut.ackProcessIdentifier);
ct_test(pTest,
testAlarmAckIn.ackTimeStamp.tag == testAlarmAckOut.ackTimeStamp.tag);
ct_test(pTest,
testAlarmAckIn.ackTimeStamp.value.sequenceNum ==
testAlarmAckOut.ackTimeStamp.value.sequenceNum);
ct_test(pTest,
testAlarmAckIn.ackProcessIdentifier ==
testAlarmAckOut.ackProcessIdentifier);
ct_test(pTest,
testAlarmAckIn.eventObjectIdentifier.instance ==
testAlarmAckOut.eventObjectIdentifier.instance);
ct_test(pTest,
testAlarmAckIn.eventObjectIdentifier.type ==
testAlarmAckOut.eventObjectIdentifier.type);
ct_test(pTest,
testAlarmAckIn.eventTimeStamp.tag ==
testAlarmAckOut.eventTimeStamp.tag);
ct_test(pTest,
testAlarmAckIn.eventTimeStamp.value.time.hour ==
testAlarmAckOut.eventTimeStamp.value.time.hour);
ct_test(pTest,
testAlarmAckIn.eventTimeStamp.value.time.min ==
testAlarmAckOut.eventTimeStamp.value.time.min);
ct_test(pTest,
testAlarmAckIn.eventTimeStamp.value.time.sec ==
testAlarmAckOut.eventTimeStamp.value.time.sec);
ct_test(pTest,
testAlarmAckIn.eventTimeStamp.value.time.hundredths ==
testAlarmAckOut.eventTimeStamp.value.time.hundredths);
ct_test(pTest,
testAlarmAckIn.eventStateAcked == testAlarmAckOut.eventStateAcked);
}
#ifdef TEST_ALARM_ACK
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Alarm Ack", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAlarmAck);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ALARM_ACK */
#endif /* TEST */
+643
View File
@@ -0,0 +1,643 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "bits.h"
#include "apdu.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "bacenum.h"
#include "tsm.h"
#include "dcc.h"
#include "iam.h"
/** @file apdu.c Handles APDU services */
extern int Routed_Device_Service_Approval(
BACNET_CONFIRMED_SERVICE service,
int service_argument,
uint8_t * apdu_buff,
uint8_t invoke_id);
/* APDU Timeout in Milliseconds */
static uint16_t Timeout_Milliseconds = 3000;
/* Number of APDU Retries */
static uint8_t Number_Of_Retries = 3;
/* a simple table for crossing the services supported */
static BACNET_SERVICES_SUPPORTED
confirmed_service_supported[MAX_BACNET_CONFIRMED_SERVICE] = {
SERVICE_SUPPORTED_ACKNOWLEDGE_ALARM,
SERVICE_SUPPORTED_CONFIRMED_COV_NOTIFICATION,
SERVICE_SUPPORTED_CONFIRMED_EVENT_NOTIFICATION,
SERVICE_SUPPORTED_GET_ALARM_SUMMARY,
SERVICE_SUPPORTED_GET_ENROLLMENT_SUMMARY,
SERVICE_SUPPORTED_SUBSCRIBE_COV,
SERVICE_SUPPORTED_ATOMIC_READ_FILE,
SERVICE_SUPPORTED_ATOMIC_WRITE_FILE,
SERVICE_SUPPORTED_ADD_LIST_ELEMENT,
SERVICE_SUPPORTED_REMOVE_LIST_ELEMENT,
SERVICE_SUPPORTED_CREATE_OBJECT,
SERVICE_SUPPORTED_DELETE_OBJECT,
SERVICE_SUPPORTED_READ_PROPERTY,
SERVICE_SUPPORTED_READ_PROP_CONDITIONAL,
SERVICE_SUPPORTED_READ_PROP_MULTIPLE,
SERVICE_SUPPORTED_WRITE_PROPERTY,
SERVICE_SUPPORTED_WRITE_PROP_MULTIPLE,
SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL,
SERVICE_SUPPORTED_PRIVATE_TRANSFER,
SERVICE_SUPPORTED_TEXT_MESSAGE,
SERVICE_SUPPORTED_REINITIALIZE_DEVICE,
SERVICE_SUPPORTED_VT_OPEN,
SERVICE_SUPPORTED_VT_CLOSE,
SERVICE_SUPPORTED_VT_DATA,
SERVICE_SUPPORTED_AUTHENTICATE,
SERVICE_SUPPORTED_REQUEST_KEY,
SERVICE_SUPPORTED_READ_RANGE,
SERVICE_SUPPORTED_LIFE_SAFETY_OPERATION,
SERVICE_SUPPORTED_SUBSCRIBE_COV_PROPERTY,
SERVICE_SUPPORTED_GET_EVENT_INFORMATION
};
/* a simple table for crossing the services supported */
static BACNET_SERVICES_SUPPORTED
unconfirmed_service_supported[MAX_BACNET_UNCONFIRMED_SERVICE] = {
SERVICE_SUPPORTED_I_AM,
SERVICE_SUPPORTED_I_HAVE,
SERVICE_SUPPORTED_UNCONFIRMED_COV_NOTIFICATION,
SERVICE_SUPPORTED_UNCONFIRMED_EVENT_NOTIFICATION,
SERVICE_SUPPORTED_UNCONFIRMED_PRIVATE_TRANSFER,
SERVICE_SUPPORTED_UNCONFIRMED_TEXT_MESSAGE,
SERVICE_SUPPORTED_TIME_SYNCHRONIZATION,
SERVICE_SUPPORTED_WHO_HAS,
SERVICE_SUPPORTED_WHO_IS,
SERVICE_SUPPORTED_UTC_TIME_SYNCHRONIZATION
};
/* Confirmed Function Handlers */
/* If they are not set, they are handled by a reject message */
static confirmed_function Confirmed_Function[MAX_BACNET_CONFIRMED_SERVICE];
void apdu_set_confirmed_handler(
BACNET_CONFIRMED_SERVICE service_choice,
confirmed_function pFunction)
{
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE)
Confirmed_Function[service_choice] = pFunction;
}
/* Allow the APDU handler to automatically reject */
static confirmed_function Unrecognized_Service_Handler;
void apdu_set_unrecognized_service_handler_handler(
confirmed_function pFunction)
{
Unrecognized_Service_Handler = pFunction;
}
/* Unconfirmed Function Handlers */
/* If they are not set, they are not handled */
static unconfirmed_function
Unconfirmed_Function[MAX_BACNET_UNCONFIRMED_SERVICE];
void apdu_set_unconfirmed_handler(
BACNET_UNCONFIRMED_SERVICE service_choice,
unconfirmed_function pFunction)
{
if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE)
Unconfirmed_Function[service_choice] = pFunction;
}
bool apdu_service_supported(
BACNET_SERVICES_SUPPORTED service_supported)
{
int i = 0;
bool status = false;
bool found = false;
if (service_supported < MAX_BACNET_SERVICES_SUPPORTED) {
/* is it a confirmed service? */
for (i = 0; i < MAX_BACNET_CONFIRMED_SERVICE; i++) {
if (confirmed_service_supported[i] == service_supported) {
found = true;
if (Confirmed_Function[i] != NULL) {
#if BAC_ROUTING
/* Check to see if the current Device supports this service. */
int len =
Routed_Device_Service_Approval(service_supported, 0,
NULL, 0);
if (len > 0)
break; /* Not supported - return false */
#endif
status = true;
}
break;
}
}
if (!found) {
/* is it an unconfirmed service? */
for (i = 0; i < MAX_BACNET_UNCONFIRMED_SERVICE; i++) {
if (unconfirmed_service_supported[i] == service_supported) {
if (Unconfirmed_Function[i] != NULL)
status = true;
break;
}
}
}
}
return status;
}
/** Function to translate a SERVICE_SUPPORTED_ enum to its SERVICE_CONFIRMED_
* or SERVICE_UNCONFIRMED_ index.
* Useful with the bactext_confirmed_service_name() functions.
*
* @param service_supported [in] The SERVICE_SUPPORTED_ enum value to convert.
* @param index [out] The SERVICE_CONFIRMED_ or SERVICE_UNCONFIRMED_ index,
* if found.
* @param bIsConfirmed [out] True if index is a SERVICE_CONFIRMED_ type.
* @return True if a match was found and index and bIsConfirmed are valid.
*/
bool apdu_service_supported_to_index(
BACNET_SERVICES_SUPPORTED service_supported,
size_t * index,
bool * bIsConfirmed)
{
int i = 0;
bool found = false;
*bIsConfirmed = false;
if (service_supported < MAX_BACNET_SERVICES_SUPPORTED) {
/* is it a confirmed service? */
for (i = 0; i < MAX_BACNET_CONFIRMED_SERVICE; i++) {
if (confirmed_service_supported[i] == service_supported) {
found = true;
*index = (size_t) i;
*bIsConfirmed = true;
break;
}
}
if (!found) {
/* is it an unconfirmed service? */
for (i = 0; i < MAX_BACNET_UNCONFIRMED_SERVICE; i++) {
if (unconfirmed_service_supported[i] == service_supported) {
found = true;
*index = (size_t) i;
break;
}
}
}
}
return found;
}
/* Confirmed ACK Function Handlers */
static confirmed_ack_function
Confirmed_ACK_Function[MAX_BACNET_CONFIRMED_SERVICE];
void apdu_set_confirmed_simple_ack_handler(
BACNET_CONFIRMED_SERVICE service_choice,
confirmed_simple_ack_function pFunction)
{
switch (service_choice) {
case SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM:
case SERVICE_CONFIRMED_COV_NOTIFICATION:
case SERVICE_CONFIRMED_EVENT_NOTIFICATION:
case SERVICE_CONFIRMED_SUBSCRIBE_COV:
case SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY:
case SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION:
/* Object Access Services */
case SERVICE_CONFIRMED_ADD_LIST_ELEMENT:
case SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT:
case SERVICE_CONFIRMED_DELETE_OBJECT:
case SERVICE_CONFIRMED_WRITE_PROPERTY:
case SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE:
/* Remote Device Management Services */
case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL:
case SERVICE_CONFIRMED_TEXT_MESSAGE:
case SERVICE_CONFIRMED_REINITIALIZE_DEVICE:
/* Virtual Terminal Services */
case SERVICE_CONFIRMED_VT_CLOSE:
/* Security Services */
case SERVICE_CONFIRMED_REQUEST_KEY:
Confirmed_ACK_Function[service_choice] =
(confirmed_ack_function) pFunction;
break;
default:
break;
}
}
void apdu_set_confirmed_ack_handler(
BACNET_CONFIRMED_SERVICE service_choice,
confirmed_ack_function pFunction)
{
switch (service_choice) {
case SERVICE_CONFIRMED_GET_ALARM_SUMMARY:
case SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY:
case SERVICE_CONFIRMED_GET_EVENT_INFORMATION:
/* File Access Services */
case SERVICE_CONFIRMED_ATOMIC_READ_FILE:
case SERVICE_CONFIRMED_ATOMIC_WRITE_FILE:
/* Object Access Services */
case SERVICE_CONFIRMED_CREATE_OBJECT:
case SERVICE_CONFIRMED_READ_PROPERTY:
case SERVICE_CONFIRMED_READ_PROP_CONDITIONAL:
case SERVICE_CONFIRMED_READ_PROP_MULTIPLE:
case SERVICE_CONFIRMED_READ_RANGE:
/* Remote Device Management Services */
case SERVICE_CONFIRMED_PRIVATE_TRANSFER:
/* Virtual Terminal Services */
case SERVICE_CONFIRMED_VT_OPEN:
case SERVICE_CONFIRMED_VT_DATA:
/* Security Services */
case SERVICE_CONFIRMED_AUTHENTICATE:
Confirmed_ACK_Function[service_choice] = pFunction;
break;
default:
break;
}
}
static error_function Error_Function[MAX_BACNET_CONFIRMED_SERVICE];
void apdu_set_error_handler(
BACNET_CONFIRMED_SERVICE service_choice,
error_function pFunction)
{
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE)
Error_Function[service_choice] = pFunction;
}
static abort_function Abort_Function;
void apdu_set_abort_handler(
abort_function pFunction)
{
Abort_Function = pFunction;
}
static reject_function Reject_Function;
void apdu_set_reject_handler(
reject_function pFunction)
{
Reject_Function = pFunction;
}
uint16_t apdu_decode_confirmed_service_request(
uint8_t * apdu, /* APDU data */
uint16_t apdu_len,
BACNET_CONFIRMED_SERVICE_DATA * service_data,
uint8_t * service_choice,
uint8_t ** service_request,
uint16_t * service_request_len)
{
uint16_t len = 0; /* counts where we are in PDU */
service_data->segmented_message = (apdu[0] & BIT(3)) ? true : false;
service_data->more_follows = (apdu[0] & BIT(2)) ? true : false;
service_data->segmented_response_accepted =
(apdu[0] & BIT(1)) ? true : false;
service_data->max_segs = decode_max_segs(apdu[1]);
service_data->max_resp = decode_max_apdu(apdu[1]);
service_data->invoke_id = apdu[2];
len = 3;
if (service_data->segmented_message) {
service_data->sequence_number = apdu[len++];
service_data->proposed_window_number = apdu[len++];
}
*service_choice = apdu[len++];
*service_request = &apdu[len];
*service_request_len = apdu_len - len;
return len;
}
uint16_t apdu_timeout(
void)
{
return Timeout_Milliseconds;
}
void apdu_timeout_set(
uint16_t milliseconds)
{
Timeout_Milliseconds = milliseconds;
}
uint8_t apdu_retries(
void)
{
return Number_Of_Retries;
}
void apdu_retries_set(
uint8_t value)
{
Number_Of_Retries = value;
}
/* When network communications are completely disabled,
only DeviceCommunicationControl and ReinitializeDevice APDUs
shall be processed and no messages shall be initiated.
When the initiation of communications is disabled,
all APDUs shall be processed and responses returned as
required... */
static bool apdu_confirmed_dcc_disabled(
uint8_t service_choice)
{
bool status = false;
if (dcc_communication_disabled()) {
switch (service_choice) {
case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL:
case SERVICE_CONFIRMED_REINITIALIZE_DEVICE:
break;
default:
status = true;
break;
}
}
return status;
}
/* When network communications are completely disabled,
only DeviceCommunicationControl and ReinitializeDevice APDUs
shall be processed and no messages shall be initiated. */
/* If the request is valid and the 'Enable/Disable' parameter is
DISABLE_INITIATION, the responding BACnet-user shall
discontinue the initiation of messages except for I-Am
requests issued in accordance with the Who-Is service procedure.*/
static bool apdu_unconfirmed_dcc_disabled(
uint8_t service_choice)
{
bool status = false;
if (dcc_communication_disabled()) {
/* there are no Unconfirmed messages that
can be processed in this state */
status = true;
} else if (dcc_communication_initiation_disabled()) {
/* WhoIs will be processed and I-Am initiated as response. */
switch (service_choice) {
case SERVICE_UNCONFIRMED_WHO_IS:
break;
default:
status = true;
break;
}
}
return status;
}
/** Process the APDU header and invoke the appropriate service handler
* to manage the received request.
* Almost all requests and ACKs invoke this function.
* @ingroup MISCHNDLR
*
* @param src [in] The BACNET_ADDRESS of the message's source.
* @param apdu [in] The apdu portion of the request, to be processed.
* @param apdu_len [in] The total (remaining) length of the apdu.
*/
void apdu_handler(
BACNET_ADDRESS * src,
uint8_t * apdu, /* APDU data */
uint16_t apdu_len)
{
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
BACNET_CONFIRMED_SERVICE_ACK_DATA service_ack_data = { 0 };
uint8_t invoke_id = 0;
uint8_t service_choice = 0;
uint8_t *service_request = NULL;
uint16_t service_request_len = 0;
int len = 0; /* counts where we are in PDU */
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t error_code = 0;
uint32_t error_class = 0;
uint8_t reason = 0;
bool server = false;
if (apdu) {
/* PDU Type */
switch (apdu[0] & 0xF0) {
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
(void)apdu_decode_confirmed_service_request(&apdu[0],
apdu_len, &service_data, &service_choice, &service_request,
&service_request_len);
if (apdu_confirmed_dcc_disabled(service_choice)) {
/* When network communications are completely disabled,
only DeviceCommunicationControl and ReinitializeDevice APDUs
shall be processed and no messages shall be initiated. */
break;
}
if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) &&
(Confirmed_Function[service_choice]))
Confirmed_Function[service_choice] (service_request,
service_request_len, src, &service_data);
else if (Unrecognized_Service_Handler)
Unrecognized_Service_Handler(service_request,
service_request_len, src, &service_data);
break;
case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST:
service_choice = apdu[1];
service_request = &apdu[2];
service_request_len = apdu_len - 2;
if (apdu_unconfirmed_dcc_disabled(service_choice)) {
/* When network communications are disabled,
only DeviceCommunicationControl and ReinitializeDevice APDUs
shall be processed and no messages shall be initiated.
If communications have been initiation disabled, then
WhoIs may be processed. */
break;
}
if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) {
if (Unconfirmed_Function[service_choice])
Unconfirmed_Function[service_choice] (service_request,
service_request_len, src);
}
break;
case PDU_TYPE_SIMPLE_ACK:
invoke_id = apdu[1];
service_choice = apdu[2];
switch (service_choice) {
case SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM:
case SERVICE_CONFIRMED_COV_NOTIFICATION:
case SERVICE_CONFIRMED_EVENT_NOTIFICATION:
case SERVICE_CONFIRMED_SUBSCRIBE_COV:
case SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY:
case SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION:
/* Object Access Services */
case SERVICE_CONFIRMED_ADD_LIST_ELEMENT:
case SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT:
case SERVICE_CONFIRMED_DELETE_OBJECT:
case SERVICE_CONFIRMED_WRITE_PROPERTY:
case SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE:
/* Remote Device Management Services */
case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL:
case SERVICE_CONFIRMED_REINITIALIZE_DEVICE:
case SERVICE_CONFIRMED_TEXT_MESSAGE:
/* Virtual Terminal Services */
case SERVICE_CONFIRMED_VT_CLOSE:
/* Security Services */
case SERVICE_CONFIRMED_REQUEST_KEY:
if (Confirmed_ACK_Function[service_choice] != NULL) {
((confirmed_simple_ack_function)
Confirmed_ACK_Function[service_choice]) (src,
invoke_id);
}
tsm_free_invoke_id(invoke_id);
break;
default:
break;
}
break;
case PDU_TYPE_COMPLEX_ACK:
service_ack_data.segmented_message =
(apdu[0] & BIT(3)) ? true : false;
service_ack_data.more_follows =
(apdu[0] & BIT(2)) ? true : false;
invoke_id = service_ack_data.invoke_id = apdu[1];
len = 2;
if (service_ack_data.segmented_message) {
service_ack_data.sequence_number = apdu[len++];
service_ack_data.proposed_window_number = apdu[len++];
}
service_choice = apdu[len++];
service_request = &apdu[len];
service_request_len = apdu_len - (uint16_t) len;
switch (service_choice) {
case SERVICE_CONFIRMED_GET_ALARM_SUMMARY:
case SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY:
case SERVICE_CONFIRMED_GET_EVENT_INFORMATION:
/* File Access Services */
case SERVICE_CONFIRMED_ATOMIC_READ_FILE:
case SERVICE_CONFIRMED_ATOMIC_WRITE_FILE:
/* Object Access Services */
case SERVICE_CONFIRMED_CREATE_OBJECT:
case SERVICE_CONFIRMED_READ_PROPERTY:
case SERVICE_CONFIRMED_READ_PROP_CONDITIONAL:
case SERVICE_CONFIRMED_READ_PROP_MULTIPLE:
case SERVICE_CONFIRMED_READ_RANGE:
case SERVICE_CONFIRMED_PRIVATE_TRANSFER:
/* Virtual Terminal Services */
case SERVICE_CONFIRMED_VT_OPEN:
case SERVICE_CONFIRMED_VT_DATA:
/* Security Services */
case SERVICE_CONFIRMED_AUTHENTICATE:
if (Confirmed_ACK_Function[service_choice] != NULL) {
(Confirmed_ACK_Function[service_choice])
(service_request, service_request_len, src,
&service_ack_data);
}
tsm_free_invoke_id(invoke_id);
break;
default:
break;
}
break;
case PDU_TYPE_SEGMENT_ACK:
/* FIXME: what about a denial of service attack here?
we could check src to see if that matched the tsm */
tsm_free_invoke_id(invoke_id);
break;
case PDU_TYPE_ERROR:
invoke_id = apdu[1];
service_choice = apdu[2];
len = 3;
/* FIXME: Currently special case for C_P_T but there are others which may
need consideration such as ChangeList-Error, CreateObject-Error,
WritePropertyMultiple-Error and VTClose_Error but they may be left as
is for now until support for these services is added */
if (service_choice == SERVICE_CONFIRMED_PRIVATE_TRANSFER) { /* skip over opening tag 0 */
if (decode_is_opening_tag_number(&apdu[len], 0)) {
len++; /* a tag number of 0 is not extended so only one octet */
}
}
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
/* FIXME: we could validate that the tag is enumerated... */
len += decode_enumerated(&apdu[len], len_value, &error_class);
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
/* FIXME: we could validate that the tag is enumerated... */
len += decode_enumerated(&apdu[len], len_value, &error_code);
if (service_choice == SERVICE_CONFIRMED_PRIVATE_TRANSFER) { /* skip over closing tag 0 */
if (decode_is_closing_tag_number(&apdu[len], 0)) {
len++; /* a tag number of 0 is not extended so only one octet */
}
}
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
if (Error_Function[service_choice])
Error_Function[service_choice] (src, invoke_id,
(BACNET_ERROR_CLASS) error_class,
(BACNET_ERROR_CODE) error_code);
}
tsm_free_invoke_id(invoke_id);
break;
case PDU_TYPE_REJECT:
invoke_id = apdu[1];
reason = apdu[2];
if (Reject_Function)
Reject_Function(src, invoke_id, reason);
tsm_free_invoke_id(invoke_id);
break;
case PDU_TYPE_ABORT:
server = apdu[0] & 0x01;
invoke_id = apdu[1];
reason = apdu[2];
if (Abort_Function)
Abort_Function(src, invoke_id, reason, server);
tsm_free_invoke_id(invoke_id);
break;
default:
break;
}
}
return;
}
+554
View File
@@ -0,0 +1,554 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "arf.h"
/** @file arf.c Atomic Read File */
/* encode service */
int arf_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_ATOMIC_READ_FILE_DATA * data)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_ATOMIC_READ_FILE; /* service choice */
apdu_len = 4;
apdu_len +=
encode_application_object_id(&apdu[apdu_len], data->object_type,
data->object_instance);
switch (data->access) {
case FILE_STREAM_ACCESS:
apdu_len += encode_opening_tag(&apdu[apdu_len], 0);
apdu_len +=
encode_application_signed(&apdu[apdu_len],
data->type.stream.fileStartPosition);
apdu_len +=
encode_application_unsigned(&apdu[apdu_len],
data->type.stream.requestedOctetCount);
apdu_len += encode_closing_tag(&apdu[apdu_len], 0);
break;
case FILE_RECORD_ACCESS:
apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
apdu_len +=
encode_application_signed(&apdu[apdu_len],
data->type.record.fileStartRecord);
apdu_len +=
encode_application_unsigned(&apdu[apdu_len],
data->type.record.RecordCount);
apdu_len += encode_closing_tag(&apdu[apdu_len], 1);
break;
default:
break;
}
}
return apdu_len;
}
/* decode the service request only */
int arf_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_ATOMIC_READ_FILE_DATA * data)
{
int len = 0;
int tag_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint16_t type = 0; /* for decoding */
/* check for value pointers */
if (apdu_len && data) {
len =
decode_tag_number_and_value(&apdu[0], &tag_number,
&len_value_type);
if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID)
return -1;
len += decode_object_id(&apdu[len], &type, &data->object_instance);
data->object_type = (BACNET_OBJECT_TYPE) type;
if (decode_is_opening_tag_number(&apdu[len], 0)) {
data->access = FILE_STREAM_ACCESS;
/* a tag number is not extended so only one octet */
len++;
/* fileStartPosition */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT)
return -1;
len +=
decode_signed(&apdu[len], len_value_type,
&data->type.stream.fileStartPosition);
/* requestedOctetCount */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
return -1;
len +=
decode_unsigned(&apdu[len], len_value_type,
&data->type.stream.requestedOctetCount);
if (!decode_is_closing_tag_number(&apdu[len], 0))
return -1;
/* a tag number is not extended so only one octet */
len++;
} else if (decode_is_opening_tag_number(&apdu[len], 1)) {
data->access = FILE_RECORD_ACCESS;
/* a tag number is not extended so only one octet */
len++;
/* fileStartRecord */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT)
return -1;
len +=
decode_signed(&apdu[len], len_value_type,
&data->type.record.fileStartRecord);
/* RecordCount */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
return -1;
len +=
decode_unsigned(&apdu[len], len_value_type,
&data->type.record.RecordCount);
if (!decode_is_closing_tag_number(&apdu[len], 1))
return -1;
/* a tag number is not extended so only one octet */
len++;
} else
return -1;
}
return len;
}
int arf_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
BACNET_ATOMIC_READ_FILE_DATA * data)
{
int len = 0;
unsigned offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST)
return -1;
/* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_ATOMIC_READ_FILE)
return -1;
offset = 4;
if (apdu_len > offset) {
len =
arf_decode_service_request(&apdu[offset], apdu_len - offset, data);
}
return len;
}
/* encode service */
int arf_ack_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_ATOMIC_READ_FILE_DATA * data)
{
int apdu_len = 0; /* total length of the apdu, return value */
uint32_t i = 0;
if (apdu) {
apdu[0] = PDU_TYPE_COMPLEX_ACK;
apdu[1] = invoke_id;
apdu[2] = SERVICE_CONFIRMED_ATOMIC_READ_FILE; /* service choice */
apdu_len = 3;
/* endOfFile */
apdu_len +=
encode_application_boolean(&apdu[apdu_len], data->endOfFile);
switch (data->access) {
case FILE_STREAM_ACCESS:
apdu_len += encode_opening_tag(&apdu[apdu_len], 0);
apdu_len +=
encode_application_signed(&apdu[apdu_len],
data->type.stream.fileStartPosition);
apdu_len +=
encode_application_octet_string(&apdu[apdu_len],
&data->fileData[0]);
apdu_len += encode_closing_tag(&apdu[apdu_len], 0);
break;
case FILE_RECORD_ACCESS:
apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
apdu_len +=
encode_application_signed(&apdu[apdu_len],
data->type.record.fileStartRecord);
apdu_len +=
encode_application_unsigned(&apdu[apdu_len],
data->type.record.RecordCount);
for (i = 0; i < data->type.record.RecordCount; i++) {
apdu_len +=
encode_application_octet_string(&apdu[apdu_len],
&data->fileData[i]);
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 1);
break;
default:
break;
}
}
return apdu_len;
}
/* decode the service request only */
int arf_ack_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_ATOMIC_READ_FILE_DATA * data)
{
int len = 0;
int tag_len = 0;
int decoded_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint32_t i = 0;
/* check for value pointers */
if (apdu_len && data) {
len =
decode_tag_number_and_value(&apdu[0], &tag_number,
&len_value_type);
if (tag_number != BACNET_APPLICATION_TAG_BOOLEAN) {
return -1;
}
data->endOfFile = decode_boolean(len_value_type);
if (decode_is_opening_tag_number(&apdu[len], 0)) {
data->access = FILE_STREAM_ACCESS;
/* a tag number is not extended so only one octet */
len++;
/* fileStartPosition */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) {
return -1;
}
len +=
decode_signed(&apdu[len], len_value_type,
&data->type.stream.fileStartPosition);
/* fileData */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) {
return -1;
}
decoded_len =
decode_octet_string(&apdu[len], len_value_type,
&data->fileData[0]);
if ((uint32_t)decoded_len != len_value_type) {
return -1;
}
len += decoded_len;
if (!decode_is_closing_tag_number(&apdu[len], 0)) {
return -1;
}
/* a tag number is not extended so only one octet */
len++;
} else if (decode_is_opening_tag_number(&apdu[len], 1)) {
data->access = FILE_RECORD_ACCESS;
/* a tag number is not extended so only one octet */
len++;
/* fileStartRecord */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) {
return -1;
}
len +=
decode_signed(&apdu[len], len_value_type,
&data->type.record.fileStartRecord);
/* returnedRecordCount */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
return -1;
}
len +=
decode_unsigned(&apdu[len], len_value_type,
&data->type.record.RecordCount);
for (i = 0; i < data->type.record.RecordCount; i++) {
/* fileData */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) {
return -1;
}
decoded_len =
decode_octet_string(&apdu[len], len_value_type,
&data->fileData[i]);
if ((uint32_t)decoded_len != len_value_type) {
return -1;
}
len += decoded_len;
}
if (!decode_is_closing_tag_number(&apdu[len], 1)) {
return -1;
}
/* a tag number is not extended so only one octet */
len++;
} else {
return -1;
}
}
return len;
}
int arf_ack_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
BACNET_ATOMIC_READ_FILE_DATA * data)
{
int len = 0;
unsigned offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_COMPLEX_ACK)
return -1;
*invoke_id = apdu[1]; /* invoke id - filled in by net layer */
if (apdu[2] != SERVICE_CONFIRMED_ATOMIC_READ_FILE)
return -1;
offset = 3;
if (apdu_len > offset) {
len =
arf_ack_decode_service_request(&apdu[offset], apdu_len - offset,
data);
}
return len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testAtomicReadFileAckAccess(
Test * pTest,
BACNET_ATOMIC_READ_FILE_DATA * data)
{
BACNET_ATOMIC_READ_FILE_DATA test_data = { 0 };
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 128;
uint8_t test_invoke_id = 0;
unsigned int i = 0;
len = arf_ack_encode_apdu(&apdu[0], invoke_id, data);
ct_test(pTest, len != 0);
apdu_len = len;
len = arf_ack_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data);
ct_test(pTest, len != -1);
ct_test(pTest, test_data.endOfFile == data->endOfFile);
ct_test(pTest, test_data.access == data->access);
if (test_data.access == FILE_STREAM_ACCESS) {
ct_test(pTest,
test_data.type.stream.fileStartPosition ==
data->type.stream.fileStartPosition);
ct_test(pTest,
octetstring_length(&test_data.fileData[0]) ==
octetstring_length(&data->fileData[0]));
ct_test(pTest, memcmp(octetstring_value(&test_data.fileData[0]),
octetstring_value(&data->fileData[0]),
octetstring_length(&test_data.fileData[0])) == 0);
} else if (test_data.access == FILE_RECORD_ACCESS) {
ct_test(pTest,
test_data.type.record.fileStartRecord ==
data->type.record.fileStartRecord);
ct_test(pTest,
test_data.type.record.RecordCount ==
data->type.record.RecordCount);
for (i = 0; i < data->type.record.RecordCount; i++) {
ct_test(pTest,
octetstring_length(&test_data.fileData[i]) ==
octetstring_length(&data->fileData[i]));
ct_test(pTest, memcmp(octetstring_value(&test_data.fileData[i]),
octetstring_value(&data->fileData[i]),
octetstring_length(&test_data.fileData[i])) == 0);
}
}
}
void testAtomicReadFileAck(
Test * pTest)
{
BACNET_ATOMIC_READ_FILE_DATA data = { 0 };
uint8_t test_octet_string[32] = "Joshua-Mary-Anna-Christopher";
unsigned int i = 0;
data.endOfFile = true;
data.access = FILE_STREAM_ACCESS;
data.type.stream.fileStartPosition = 0;
octetstring_init(&data.fileData[0], test_octet_string,
sizeof(test_octet_string));
testAtomicReadFileAckAccess(pTest, &data);
data.endOfFile = false;
data.access = FILE_RECORD_ACCESS;
data.type.record.fileStartRecord = 1;
data.type.record.RecordCount = BACNET_READ_FILE_RECORD_COUNT;
for (i = 0; i < data.type.record.RecordCount; i++) {
octetstring_init(&data.fileData[i], test_octet_string,
sizeof(test_octet_string));
}
testAtomicReadFileAckAccess(pTest, &data);
return;
}
void testAtomicReadFileAccess(
Test * pTest,
BACNET_ATOMIC_READ_FILE_DATA * data)
{
BACNET_ATOMIC_READ_FILE_DATA test_data = { 0 };
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 128;
uint8_t test_invoke_id = 0;
len = arf_encode_apdu(&apdu[0], invoke_id, data);
ct_test(pTest, len != 0);
apdu_len = len;
len = arf_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data);
ct_test(pTest, len != -1);
ct_test(pTest, test_data.object_type == data->object_type);
ct_test(pTest, test_data.object_instance == data->object_instance);
ct_test(pTest, test_data.access == data->access);
if (test_data.access == FILE_STREAM_ACCESS) {
ct_test(pTest,
test_data.type.stream.fileStartPosition ==
data->type.stream.fileStartPosition);
ct_test(pTest,
test_data.type.stream.requestedOctetCount ==
data->type.stream.requestedOctetCount);
} else if (test_data.access == FILE_RECORD_ACCESS) {
ct_test(pTest,
test_data.type.record.fileStartRecord ==
data->type.record.fileStartRecord);
ct_test(pTest,
test_data.type.record.RecordCount ==
data->type.record.RecordCount);
}
}
void testAtomicReadFile(
Test * pTest)
{
BACNET_ATOMIC_READ_FILE_DATA data = { 0 };
data.object_type = OBJECT_FILE;
data.object_instance = 1;
data.access = FILE_STREAM_ACCESS;
data.type.stream.fileStartPosition = 0;
data.type.stream.requestedOctetCount = 128;
testAtomicReadFileAccess(pTest, &data);
data.object_type = OBJECT_FILE;
data.object_instance = 2;
data.access = FILE_RECORD_ACCESS;
data.type.record.fileStartRecord = 1;
data.type.record.RecordCount = 2;
testAtomicReadFileAccess(pTest, &data);
return;
}
#ifdef TEST_ATOMIC_READ_FILE
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet AtomicReadFile", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAtomicReadFile);
assert(rc);
rc = ct_addTestFunction(pTest, testAtomicReadFileAck);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_xxx */
#endif /* TEST */
+131
View File
@@ -0,0 +1,131 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include "assigned_access_rights.h"
#include "bacdcode.h"
int bacapp_encode_assigned_access_rights(
uint8_t * apdu,
BACNET_ASSIGNED_ACCESS_RIGHTS * aar)
{
int len;
int apdu_len = 0;
len =
bacapp_encode_context_device_obj_ref(&apdu[apdu_len], 0,
&aar->assigned_access_rights);
if (len < 0)
return -1;
else
apdu_len += len;
len = encode_context_boolean(&apdu[apdu_len], 1, aar->enable);
if (len < 0)
return -1;
else
apdu_len += len;
return apdu_len;
}
int bacapp_encode_context_assigned_access_rights(
uint8_t * apdu,
uint8_t tag,
BACNET_ASSIGNED_ACCESS_RIGHTS * aar)
{
int len;
int apdu_len = 0;
len = encode_opening_tag(&apdu[apdu_len], tag);
apdu_len += len;
len = bacapp_encode_assigned_access_rights(&apdu[apdu_len], aar);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], tag);
apdu_len += len;
return apdu_len;
}
int bacapp_decode_assigned_access_rights(
uint8_t * apdu,
BACNET_ASSIGNED_ACCESS_RIGHTS * aar)
{
int len;
int apdu_len = 0;
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
len =
bacapp_decode_context_device_obj_ref(&apdu[apdu_len], 0,
&aar->assigned_access_rights);
if (len < 0)
return -1;
else
apdu_len += len;
} else
return -1;
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
len = decode_context_boolean2(&apdu[apdu_len], 1, &aar->enable);
if (len < 0)
return -1;
else
apdu_len += len;
} else
return -1;
return apdu_len;
}
int bacapp_decode_context_assigned_access_rights(
uint8_t * apdu,
uint8_t tag,
BACNET_ASSIGNED_ACCESS_RIGHTS * aar)
{
int len = 0;
int section_length;
if (decode_is_opening_tag_number(&apdu[len], tag)) {
len++;
section_length = bacapp_decode_assigned_access_rights(&apdu[len], aar);
if (section_length == -1) {
len = -1;
} else {
len += section_length;
if (decode_is_closing_tag_number(&apdu[len], tag)) {
len++;
} else {
len = -1;
}
}
} else {
len = -1;
}
return len;
}
+142
View File
@@ -0,0 +1,142 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include "authentication_factor.h"
#include "bacdcode.h"
int bacapp_encode_authentication_factor(
uint8_t * apdu,
BACNET_AUTHENTICATION_FACTOR * af)
{
int len;
int apdu_len = 0;
len = encode_context_enumerated(&apdu[apdu_len], 0, af->format_type);
if (len < 0)
return -1;
else
apdu_len += len;
len = encode_context_unsigned(&apdu[apdu_len], 1, af->format_class);
if (len < 0)
return -1;
else
apdu_len += len;
len = encode_context_octet_string(&apdu[apdu_len], 2, &af->value);
if (len < 0)
return -1;
else
apdu_len += len;
return apdu_len;
}
int bacapp_encode_context_authentication_factor(
uint8_t * apdu,
uint8_t tag,
BACNET_AUTHENTICATION_FACTOR * af)
{
int len;
int apdu_len = 0;
len = encode_opening_tag(&apdu[apdu_len], tag);
apdu_len += len;
len = bacapp_encode_authentication_factor(&apdu[apdu_len], af);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], tag);
apdu_len += len;
return apdu_len;
}
int bacapp_decode_authentication_factor(
uint8_t * apdu,
BACNET_AUTHENTICATION_FACTOR * af)
{
int len;
int apdu_len = 0;
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
len = decode_context_enumerated(&apdu[apdu_len], 0, &af->format_type);
if (len < 0)
return -1;
else
apdu_len += len;
} else
return -1;
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
len = decode_context_unsigned(&apdu[apdu_len], 1, &af->format_class);
if (len < 0)
return -1;
else
apdu_len += len;
} else
return -1;
if (decode_is_context_tag(&apdu[apdu_len], 2)) {
len = decode_context_octet_string(&apdu[apdu_len], 2, &af->value);
if (len < 0)
return -1;
else
apdu_len += len;
} else
return -1;
return apdu_len;
}
int bacapp_decode_context_authentication_factor(
uint8_t * apdu,
uint8_t tag,
BACNET_AUTHENTICATION_FACTOR * af)
{
int len = 0;
int section_length;
if (decode_is_opening_tag_number(&apdu[len], tag)) {
len++;
section_length = bacapp_decode_authentication_factor(&apdu[len], af);
if (section_length == -1) {
len = -1;
} else {
len += section_length;
if (decode_is_closing_tag_number(&apdu[len], tag)) {
len++;
} else {
len = -1;
}
}
} else {
len = -1;
}
return len;
}
+151
View File
@@ -0,0 +1,151 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include "bacdcode.h"
#include "authentication_factor_format.h"
int bacapp_encode_authentication_factor_format(
uint8_t * apdu,
BACNET_AUTHENTICATION_FACTOR_FORMAT * aff)
{
int len;
int apdu_len = 0;
len = encode_context_enumerated(&apdu[apdu_len], 0, aff->format_type);
if (len < 0)
return -1;
else
apdu_len += len;
if (aff->format_type == AUTHENTICATION_FACTOR_CUSTOM) {
len = encode_context_unsigned(&apdu[apdu_len], 1, aff->vendor_id);
if (len < 0)
return -1;
else
apdu_len += len;
len = encode_context_unsigned(&apdu[apdu_len], 2, aff->vendor_format);
if (len < 0)
return -1;
else
apdu_len += len;
}
return apdu_len;
}
int bacapp_encode_context_authentication_factor_format(
uint8_t * apdu,
uint8_t tag,
BACNET_AUTHENTICATION_FACTOR_FORMAT * aff)
{
int len;
int apdu_len = 0;
len = encode_opening_tag(&apdu[apdu_len], tag);
apdu_len += len;
len = bacapp_encode_authentication_factor_format(&apdu[apdu_len], aff);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], tag);
apdu_len += len;
return apdu_len;
}
int bacapp_decode_authentication_factor_format(
uint8_t * apdu,
BACNET_AUTHENTICATION_FACTOR_FORMAT * aff)
{
int len;
int apdu_len = 0;
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
len =
decode_context_enumerated(&apdu[apdu_len], 0,
&aff->format_type);
if (len < 0)
return -1;
else
apdu_len += len;
} else
return -1;
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
len = decode_context_unsigned(&apdu[apdu_len], 1, &aff->vendor_id);
if (len < 0)
return -1;
else
apdu_len += len;
if ((aff->format_type != AUTHENTICATION_FACTOR_CUSTOM)
&& (aff->vendor_id != 0))
return -1;
}
if (decode_is_context_tag(&apdu[apdu_len], 2)) {
len = decode_context_unsigned(&apdu[apdu_len], 2, &aff->vendor_format);
if (len < 0)
return -1;
else
apdu_len += len;
if ((aff->format_type != AUTHENTICATION_FACTOR_CUSTOM)
&& (aff->vendor_format != 0))
return -1;
}
return apdu_len;
}
int bacapp_decode_context_authentication_factor_format(
uint8_t * apdu,
uint8_t tag,
BACNET_AUTHENTICATION_FACTOR_FORMAT * aff)
{
int len = 0;
int section_length;
if (decode_is_opening_tag_number(&apdu[len], tag)) {
len++;
section_length =
bacapp_decode_authentication_factor_format(&apdu[len], aff);
if (section_length == -1) {
len = -1;
} else {
len += section_length;
if (decode_is_closing_tag_number(&apdu[len], tag)) {
len++;
} else {
len = -1;
}
}
} else {
len = -1;
}
return len;
}
+457
View File
@@ -0,0 +1,457 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "awf.h"
/** @file awf.c Atomic Write File */
/* encode service */
int awf_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_ATOMIC_WRITE_FILE_DATA * data)
{
int apdu_len = 0; /* total length of the apdu, return value */
uint32_t i = 0;
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_ATOMIC_WRITE_FILE; /* service choice */
apdu_len = 4;
apdu_len +=
encode_application_object_id(&apdu[apdu_len], data->object_type,
data->object_instance);
switch (data->access) {
case FILE_STREAM_ACCESS:
apdu_len += encode_opening_tag(&apdu[apdu_len], 0);
apdu_len +=
encode_application_signed(&apdu[apdu_len],
data->type.stream.fileStartPosition);
apdu_len +=
encode_application_octet_string(&apdu[apdu_len],
&data->fileData[0]);
apdu_len += encode_closing_tag(&apdu[apdu_len], 0);
break;
case FILE_RECORD_ACCESS:
apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
apdu_len +=
encode_application_signed(&apdu[apdu_len],
data->type.record.fileStartRecord);
apdu_len +=
encode_application_unsigned(&apdu[apdu_len],
data->type.record.returnedRecordCount);
for (i = 0; i < data->type.record.returnedRecordCount; i++) {
apdu_len +=
encode_application_octet_string(&apdu[apdu_len],
&data->fileData[i]);
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 1);
break;
default:
break;
}
}
return apdu_len;
}
/* decode the service request only */
int awf_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_ATOMIC_WRITE_FILE_DATA * data)
{
int len = 0;
int tag_len = 0;
int decoded_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
int32_t signed_value = 0;
uint32_t unsigned_value = 0;
uint16_t type = 0; /* for decoding */
uint32_t i = 0;
/* check for value pointers */
if (apdu_len && data) {
len =
decode_tag_number_and_value(&apdu[0], &tag_number,
&len_value_type);
if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID)
return -1;
len += decode_object_id(&apdu[len], &type, &data->object_instance);
data->object_type = (BACNET_OBJECT_TYPE) type;
if (decode_is_opening_tag_number(&apdu[len], 0)) {
data->access = FILE_STREAM_ACCESS;
/* a tag number of 2 is not extended so only one octet */
len++;
/* fileStartPosition */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT)
return -1;
len += decode_signed(&apdu[len], len_value_type, &signed_value);
data->type.stream.fileStartPosition = signed_value;
/* fileData */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING)
return -1;
decoded_len =
decode_octet_string(&apdu[len], len_value_type,
&data->fileData[0]);
if ((uint32_t)decoded_len != len_value_type) {
return -1;
}
len += decoded_len;
if (!decode_is_closing_tag_number(&apdu[len], 0))
return -1;
/* a tag number is not extended so only one octet */
len++;
} else if (decode_is_opening_tag_number(&apdu[len], 1)) {
data->access = FILE_RECORD_ACCESS;
/* a tag number is not extended so only one octet */
len++;
/* fileStartRecord */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT)
return -1;
len += decode_signed(&apdu[len], len_value_type, &signed_value);
data->type.record.fileStartRecord = signed_value;
/* returnedRecordCount */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
return -1;
len +=
decode_unsigned(&apdu[len], len_value_type, &unsigned_value);
data->type.record.returnedRecordCount = unsigned_value;
/* fileData */
for (i = 0; i < data->type.record.returnedRecordCount; i++) {
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING)
return -1;
decoded_len =
decode_octet_string(&apdu[len], len_value_type,
&data->fileData[i]);
if ((uint32_t)decoded_len != len_value_type) {
return -1;
}
len += decoded_len;
}
if (!decode_is_closing_tag_number(&apdu[len], 1))
return -1;
/* a tag number is not extended so only one octet */
len++;
} else
return -1;
}
return len;
}
int awf_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
BACNET_ATOMIC_WRITE_FILE_DATA * data)
{
int len = 0;
unsigned offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST)
return -1;
/* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_ATOMIC_WRITE_FILE)
return -1;
offset = 4;
if (apdu_len > offset) {
len =
awf_decode_service_request(&apdu[offset], apdu_len - offset, data);
}
return len;
}
int awf_ack_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_ATOMIC_WRITE_FILE_DATA * data)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_COMPLEX_ACK;
apdu[1] = invoke_id;
apdu[2] = SERVICE_CONFIRMED_ATOMIC_WRITE_FILE; /* service choice */
apdu_len = 3;
switch (data->access) {
case FILE_STREAM_ACCESS:
apdu_len +=
encode_context_signed(&apdu[apdu_len], 0,
data->type.stream.fileStartPosition);
break;
case FILE_RECORD_ACCESS:
apdu_len +=
encode_context_signed(&apdu[apdu_len], 1,
data->type.record.fileStartRecord);
break;
default:
break;
}
}
return apdu_len;
}
/* decode the service request only */
int awf_ack_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_ATOMIC_WRITE_FILE_DATA * data)
{
int len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
/* check for value pointers */
if (apdu_len && data) {
len =
decode_tag_number_and_value(&apdu[0], &tag_number,
&len_value_type);
if (tag_number == 0) {
data->access = FILE_STREAM_ACCESS;
len +=
decode_signed(&apdu[len], len_value_type,
&data->type.stream.fileStartPosition);
} else if (tag_number == 1) {
data->access = FILE_RECORD_ACCESS;
len +=
decode_signed(&apdu[len], len_value_type,
&data->type.record.fileStartRecord);
} else
return -1;
}
return len;
}
int awf_ack_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
BACNET_ATOMIC_WRITE_FILE_DATA * data)
{
int len = 0;
unsigned offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_COMPLEX_ACK)
return -1;
*invoke_id = apdu[1]; /* invoke id - filled in by net layer */
if (apdu[2] != SERVICE_CONFIRMED_ATOMIC_WRITE_FILE)
return -1;
offset = 3;
if (apdu_len > offset) {
len =
awf_ack_decode_service_request(&apdu[offset], apdu_len - offset,
data);
}
return len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testAtomicWriteFileAccess(
Test * pTest,
BACNET_ATOMIC_WRITE_FILE_DATA * data)
{
BACNET_ATOMIC_WRITE_FILE_DATA test_data = { 0 };
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 128;
uint8_t test_invoke_id = 0;
len = awf_encode_apdu(&apdu[0], invoke_id, data);
ct_test(pTest, len != 0);
apdu_len = len;
len = awf_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data);
ct_test(pTest, len != -1);
ct_test(pTest, test_data.object_type == data->object_type);
ct_test(pTest, test_data.object_instance == data->object_instance);
ct_test(pTest, test_data.access == data->access);
if (test_data.access == FILE_STREAM_ACCESS) {
ct_test(pTest,
test_data.type.stream.fileStartPosition ==
data->type.stream.fileStartPosition);
} else if (test_data.access == FILE_RECORD_ACCESS) {
ct_test(pTest,
test_data.type.record.fileStartRecord ==
data->type.record.fileStartRecord);
ct_test(pTest,
test_data.type.record.returnedRecordCount ==
data->type.record.returnedRecordCount);
}
ct_test(pTest,
octetstring_length(&test_data.fileData[0]) ==
octetstring_length(&data->fileData[0]));
ct_test(pTest, memcmp(octetstring_value(&test_data.fileData[0]),
octetstring_value(&data->fileData[0]),
octetstring_length(&test_data.fileData[0])) == 0);
}
void testAtomicWriteFile(
Test * pTest)
{
BACNET_ATOMIC_WRITE_FILE_DATA data = { 0 };
uint8_t test_octet_string[32] = "Joshua-Mary-Anna-Christopher";
data.object_type = OBJECT_FILE;
data.object_instance = 1;
data.access = FILE_STREAM_ACCESS;
data.type.stream.fileStartPosition = 0;
octetstring_init(&data.fileData[0], test_octet_string,
sizeof(test_octet_string));
testAtomicWriteFileAccess(pTest, &data);
data.object_type = OBJECT_FILE;
data.object_instance = 1;
data.access = FILE_RECORD_ACCESS;
data.type.record.fileStartRecord = 1;
data.type.record.returnedRecordCount = 1;
octetstring_init(&data.fileData[0], test_octet_string,
sizeof(test_octet_string));
testAtomicWriteFileAccess(pTest, &data);
return;
}
void testAtomicWriteFileAckAccess(
Test * pTest,
BACNET_ATOMIC_WRITE_FILE_DATA * data)
{
BACNET_ATOMIC_WRITE_FILE_DATA test_data = { 0 };
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 128;
uint8_t test_invoke_id = 0;
len = awf_encode_apdu(&apdu[0], invoke_id, data);
ct_test(pTest, len != 0);
apdu_len = len;
len = awf_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data);
ct_test(pTest, len != -1);
ct_test(pTest, test_data.access == data->access);
if (test_data.access == FILE_STREAM_ACCESS) {
ct_test(pTest,
test_data.type.stream.fileStartPosition ==
data->type.stream.fileStartPosition);
} else if (test_data.access == FILE_RECORD_ACCESS) {
ct_test(pTest,
test_data.type.record.fileStartRecord ==
data->type.record.fileStartRecord);
}
}
void testAtomicWriteFileAck(
Test * pTest)
{
BACNET_ATOMIC_WRITE_FILE_DATA data = { 0 };
data.access = FILE_STREAM_ACCESS;
data.type.stream.fileStartPosition = 42;
testAtomicWriteFileAckAccess(pTest, &data);
data.access = FILE_RECORD_ACCESS;
data.type.record.fileStartRecord = 54;
testAtomicWriteFileAckAccess(pTest, &data);
return;
}
#ifdef TEST_ATOMIC_WRITE_FILE
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet AtomicWriteFile", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAtomicWriteFile);
assert(rc);
rc = ct_addTestFunction(pTest, testAtomicWriteFileAck);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_WRITE_PROPERTY */
#endif /* TEST */
+97
View File
@@ -0,0 +1,97 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2007 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "config.h"
#include "bacdef.h"
#include "bacaddr.h"
/** @file bacaddr.c BACnet Address structure utilities */
void bacnet_address_copy(
BACNET_ADDRESS * dest,
BACNET_ADDRESS * src)
{
int i = 0;
if (dest && src) {
dest->mac_len = src->mac_len;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->mac[i] = src->mac[i];
}
dest->net = src->net;
dest->len = src->len;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = src->adr[i];
}
}
}
bool bacnet_address_same(
BACNET_ADDRESS * dest,
BACNET_ADDRESS * src)
{
uint8_t i = 0; /* loop counter */
uint8_t max_len = 0; /* used for dynamic max */
if (dest == src) /* same ? */
return true;
if (dest->net != src->net)
return false;
if (dest->len != src->len)
return false;
max_len = dest->len;
if (max_len > MAX_MAC_LEN)
max_len = MAX_MAC_LEN;
for (i = 0; i < max_len; i++) {
if (dest->adr[i] != src->adr[i])
return false;
}
if (dest->net == 0) {
if (dest->mac_len != src->mac_len)
return false;
max_len = dest->mac_len;
if (max_len > MAX_MAC_LEN)
max_len = MAX_MAC_LEN;
for (i = 0; i < max_len; i++) {
if (dest->mac[i] != src->mac[i])
return false;
}
}
return true;
}
+2296
View File
File diff suppressed because it is too large Load Diff
+3195
View File
File diff suppressed because it is too large Load Diff
+457
View File
@@ -0,0 +1,457 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2008 John Minack
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include <stdbool.h>
#include "bacdcode.h"
#include "npdu.h"
#include "timestamp.h"
#include "bacdevobjpropref.h"
/** @file bacdevobjpropref.c BACnet Application Device Object (Property) Reference */
int bacapp_encode_context_device_obj_property_ref(
uint8_t * apdu,
uint8_t tag_number,
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value)
{
int len;
int apdu_len = 0;
len = encode_opening_tag(&apdu[apdu_len], tag_number);
apdu_len += len;
len = bacapp_encode_device_obj_property_ref(&apdu[apdu_len], value);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], tag_number);
apdu_len += len;
return apdu_len;
}
/* BACnetDeviceObjectPropertyReference ::= SEQUENCE {
object-identifier [0] BACnetObjectIdentifier,
property-identifier [1] BACnetPropertyIdentifier,
property-array-index [2] Unsigned OPTIONAL,
-- used only with array datatype
-- if omitted with an array then
-- the entire array is referenced
device-identifier [3] BACnetObjectIdentifier OPTIONAL
}
*/
int bacapp_encode_device_obj_property_ref(
uint8_t * apdu,
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value)
{
int len;
int apdu_len = 0;
/* object-identifier [0] BACnetObjectIdentifier */
len =
encode_context_object_id(&apdu[apdu_len], 0,
(int) value->objectIdentifier.type, value->objectIdentifier.instance);
apdu_len += len;
/* property-identifier [1] BACnetPropertyIdentifier */
len =
encode_context_enumerated(&apdu[apdu_len], 1,
value->propertyIdentifier);
apdu_len += len;
/* property-array-index [2] Unsigned OPTIONAL */
/* Check if needed before inserting */
if (value->arrayIndex != BACNET_ARRAY_ALL) {
len = encode_context_unsigned(&apdu[apdu_len], 2, value->arrayIndex);
apdu_len += len;
}
/* device-identifier [3] BACnetObjectIdentifier OPTIONAL */
/* Likewise, device id is optional so see if needed
* (set type to BACNET_NO_DEV_TYPE or something other than OBJECT_DEVICE to
* omit */
if (value->deviceIdentifier.type == OBJECT_DEVICE) {
len =
encode_context_object_id(&apdu[apdu_len], 3,
(int) value->deviceIdentifier.type,
value->deviceIdentifier.instance);
apdu_len += len;
}
return apdu_len;
}
/* BACnetDeviceObjectPropertyReference ::= SEQUENCE {
object-identifier [0] BACnetObjectIdentifier,
property-identifier [1] BACnetPropertyIdentifier,
property-array-index [2] Unsigned OPTIONAL,
-- used only with array datatype
-- if omitted with an array then
-- the entire array is referenced
device-identifier [3] BACnetObjectIdentifier OPTIONAL
}
*/
int bacapp_decode_device_obj_property_ref(
uint8_t * apdu,
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value)
{
int len;
int apdu_len = 0;
uint32_t enumValue;
/* object-identifier [0] BACnetObjectIdentifier */
if (-1 == (len =
decode_context_object_id(&apdu[apdu_len], 0,
&value->objectIdentifier.type,
&value->objectIdentifier.instance))) {
return -1;
}
apdu_len += len;
/* property-identifier [1] BACnetPropertyIdentifier */
if (-1 == (len =
decode_context_enumerated(&apdu[apdu_len], 1, &enumValue))) {
return -1;
}
value->propertyIdentifier = (BACNET_PROPERTY_ID) enumValue;
apdu_len += len;
/* property-array-index [2] Unsigned OPTIONAL */
if (decode_is_context_tag(&apdu[apdu_len], 2) &&
!decode_is_closing_tag(&apdu[apdu_len])) {
if (-1 == (len =
decode_context_unsigned(&apdu[apdu_len], 2,
&value->arrayIndex))) {
return -1;
}
apdu_len += len;
} else {
value->arrayIndex = BACNET_ARRAY_ALL;
}
/* device-identifier [3] BACnetObjectIdentifier OPTIONAL */
if (decode_is_context_tag(&apdu[apdu_len], 3) &&
!decode_is_closing_tag(&apdu[apdu_len])) {
if (-1 == (len =
decode_context_object_id(&apdu[apdu_len], 3,
&value->deviceIdentifier.type,
&value->deviceIdentifier.instance))) {
return -1;
}
apdu_len += len;
} else {
value->deviceIdentifier.type = BACNET_NO_DEV_TYPE;
value->deviceIdentifier.instance = BACNET_NO_DEV_ID;
}
return apdu_len;
}
int bacapp_decode_context_device_obj_property_ref(
uint8_t * apdu,
uint8_t tag_number,
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value)
{
int len = 0;
int section_length;
if (decode_is_opening_tag_number(&apdu[len], tag_number)) {
len++;
section_length =
bacapp_decode_device_obj_property_ref(&apdu[len], value);
if (section_length == -1) {
len = -1;
} else {
len += section_length;
if (decode_is_closing_tag_number(&apdu[len], tag_number)) {
len++;
} else {
len = -1;
}
}
} else {
len = -1;
}
return len;
}
/* BACnetDeviceObjectReference ::= SEQUENCE {
device-identifier [0] BACnetObjectIdentifier OPTIONAL,
object-identifier [1] BACnetObjectIdentifier
}*/
int bacapp_encode_context_device_obj_ref(
uint8_t * apdu,
uint8_t tag_number,
BACNET_DEVICE_OBJECT_REFERENCE * value)
{
int len;
int apdu_len = 0;
len = encode_opening_tag(&apdu[apdu_len], tag_number);
apdu_len += len;
len = bacapp_encode_device_obj_ref(&apdu[apdu_len], value);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], tag_number);
apdu_len += len;
return apdu_len;
}
/* BACnetDeviceObjectReference ::= SEQUENCE {
device-identifier [0] BACnetObjectIdentifier OPTIONAL,
object-identifier [1] BACnetObjectIdentifier
}*/
int bacapp_encode_device_obj_ref(
uint8_t * apdu,
BACNET_DEVICE_OBJECT_REFERENCE * value)
{
int len;
int apdu_len = 0;
/* device-identifier [0] BACnetObjectIdentifier OPTIONAL */
/* Device id is optional so see if needed
* (set type to BACNET_NO_DEV_TYPE or something other than OBJECT_DEVICE to
* omit */
if (value->deviceIdentifier.type == OBJECT_DEVICE) {
len =
encode_context_object_id(&apdu[apdu_len], 0,
(int) value->deviceIdentifier.type,
value->deviceIdentifier.instance);
apdu_len += len;
}
/* object-identifier [1] BACnetObjectIdentifier */
len =
encode_context_object_id(&apdu[apdu_len], 1,
(int) value->objectIdentifier.type, value->objectIdentifier.instance);
apdu_len += len;
return apdu_len;
}
/* BACnetDeviceObjectReference ::= SEQUENCE {
device-identifier [0] BACnetObjectIdentifier OPTIONAL,
object-identifier [1] BACnetObjectIdentifier
}*/
int bacapp_decode_device_obj_ref(
uint8_t * apdu,
BACNET_DEVICE_OBJECT_REFERENCE * value)
{
int len;
int apdu_len = 0;
/* device-identifier [0] BACnetObjectIdentifier OPTIONAL */
if (decode_is_context_tag(&apdu[apdu_len], 0) &&
!decode_is_closing_tag(&apdu[apdu_len])) {
if (-1 == (len =
decode_context_object_id(&apdu[apdu_len], 0,
&value->deviceIdentifier.type,
&value->deviceIdentifier.instance))) {
return -1;
}
apdu_len += len;
} else {
value->deviceIdentifier.type = BACNET_NO_DEV_TYPE;
value->deviceIdentifier.instance = BACNET_NO_DEV_ID;
}
/* object-identifier [1] BACnetObjectIdentifier */
if (-1 == (len =
decode_context_object_id(&apdu[apdu_len], 1,
&value->objectIdentifier.type,
&value->objectIdentifier.instance))) {
return -1;
}
apdu_len += len;
return apdu_len;
}
int bacapp_decode_context_device_obj_ref(
uint8_t * apdu,
uint8_t tag_number,
BACNET_DEVICE_OBJECT_REFERENCE * value)
{
int len = 0;
int section_length;
if (decode_is_opening_tag_number(&apdu[len], tag_number)) {
len++;
section_length = bacapp_decode_device_obj_ref(&apdu[len], value);
if (section_length == -1) {
len = -1;
} else {
len += section_length;
if (decode_is_closing_tag_number(&apdu[len], tag_number)) {
len++;
} else {
len = -1;
}
}
} else {
len = -1;
}
return len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
static void testDevObjPropRef(
Test * pTest,
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *inData)
{
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE outData;
uint8_t buffer[MAX_APDU] = {0};
int inLen = 0;
int outLen = 0;
/* encode */
inLen = bacapp_encode_device_obj_property_ref(buffer, inData);
/* add a closing tag at the end of the buffer to verify proper handling
when that is encountered in real packets */
encode_closing_tag(&buffer[inLen], 3);
/* decode */
outLen = bacapp_decode_device_obj_property_ref(buffer, &outData);
ct_test(pTest, outLen == inLen);
ct_test(pTest,
inData->objectIdentifier.instance == outData.objectIdentifier.instance);
ct_test(pTest,
inData->objectIdentifier.type == outData.objectIdentifier.type);
ct_test(pTest, inData->propertyIdentifier == outData.propertyIdentifier);
if (inData->arrayIndex != BACNET_ARRAY_ALL) {
ct_test(pTest, inData->arrayIndex == outData.arrayIndex);
} else {
ct_test(pTest, outData.arrayIndex == BACNET_ARRAY_ALL);
}
if (inData->deviceIdentifier.type == OBJECT_DEVICE) {
ct_test(pTest,
inData->deviceIdentifier.instance ==
outData.deviceIdentifier.instance);
ct_test(pTest,
inData->deviceIdentifier.type == outData.deviceIdentifier.type);
} else {
ct_test(pTest,outData.deviceIdentifier.instance == BACNET_NO_DEV_ID);
ct_test(pTest,outData.deviceIdentifier.type == BACNET_NO_DEV_TYPE);
}
}
static void testDevIdPropRef(
Test * pTest)
{
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE inData;
/* everything encoded */
inData.objectIdentifier.instance = 0x1234;
inData.objectIdentifier.type = 15;
inData.propertyIdentifier = 25;
inData.arrayIndex = 0x5678;
inData.deviceIdentifier.instance = 0x4343;
inData.deviceIdentifier.type = OBJECT_DEVICE;
testDevObjPropRef(pTest, &inData);
/* optional array */
inData.objectIdentifier.instance = 0x1234;
inData.objectIdentifier.type = 15;
inData.propertyIdentifier = 25;
inData.arrayIndex = BACNET_ARRAY_ALL;
inData.deviceIdentifier.instance = 0x4343;
inData.deviceIdentifier.type = OBJECT_DEVICE;
testDevObjPropRef(pTest, &inData);
/* optional device ID */
inData.objectIdentifier.instance = 0x1234;
inData.objectIdentifier.type = 15;
inData.propertyIdentifier = 25;
inData.arrayIndex = 1;
inData.deviceIdentifier.instance = 0;
inData.deviceIdentifier.type = BACNET_NO_DEV_TYPE;
testDevObjPropRef(pTest, &inData);
/* optional array + optional device ID */
inData.objectIdentifier.instance = 0x1234;
inData.objectIdentifier.type = 15;
inData.propertyIdentifier = 25;
inData.arrayIndex = BACNET_ARRAY_ALL;
inData.deviceIdentifier.instance = 0;
inData.deviceIdentifier.type = BACNET_NO_DEV_TYPE;
testDevObjPropRef(pTest, &inData);
}
static void testDevIdRef(
Test * pTest)
{
BACNET_DEVICE_OBJECT_REFERENCE inData;
BACNET_DEVICE_OBJECT_REFERENCE outData;
uint8_t buffer[MAX_APDU];
int inLen;
int outLen;
inData.deviceIdentifier.instance = 0x4343;
inData.deviceIdentifier.type = OBJECT_DEVICE;
inLen = bacapp_encode_device_obj_ref(buffer, &inData);
outLen = bacapp_decode_device_obj_ref(buffer, &outData);
ct_test(pTest, outLen == inLen);
ct_test(pTest,
inData.deviceIdentifier.instance ==
outData.deviceIdentifier.instance);
ct_test(pTest,
inData.deviceIdentifier.type == outData.deviceIdentifier.type);
}
void testBACnetDeviceObjectPropertyReference(
Test * pTest)
{
bool rc;
/* individual tests */
rc = ct_addTestFunction(pTest, testDevIdPropRef);
assert(rc);
rc = ct_addTestFunction(pTest, testDevIdRef);
assert(rc);
}
#ifdef TEST_DEV_ID_PROP_REF
#include <assert.h>
int main(
void)
{
Test *pTest;
pTest = ct_create("BACnet Prop Ref", NULL);
testBACnetDeviceObjectPropertyReference(pTest);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_DEV_ID_PROP_REF */
#endif /* TEST */
+274
View File
@@ -0,0 +1,274 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "bacerror.h"
/** @file bacerror.c Encode/Decode BACnet Errors */
/* encode service */
int bacerror_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_CONFIRMED_SERVICE service,
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_ERROR;
apdu[1] = invoke_id;
apdu[2] = service;
apdu_len = 3;
/* service parameters */
apdu_len +=
encode_application_enumerated(&apdu[apdu_len], error_class);
apdu_len += encode_application_enumerated(&apdu[apdu_len], error_code);
}
return apdu_len;
}
#if !BACNET_SVC_SERVER
/* decode the application class and code */
int bacerror_decode_error_class_and_code(
uint8_t * apdu,
unsigned apdu_len,
BACNET_ERROR_CLASS * error_class,
BACNET_ERROR_CODE * error_code)
{
int len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint32_t decoded_value = 0;
if (apdu_len) {
/* error class */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED)
return 0;
len += decode_enumerated(&apdu[len], len_value_type, &decoded_value);
if (error_class)
*error_class = (BACNET_ERROR_CLASS) decoded_value;
/* error code */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED)
return 0;
len += decode_enumerated(&apdu[len], len_value_type, &decoded_value);
if (error_code)
*error_code = (BACNET_ERROR_CODE) decoded_value;
}
return len;
}
/* decode the service request only */
int bacerror_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
BACNET_CONFIRMED_SERVICE * service,
BACNET_ERROR_CLASS * error_class,
BACNET_ERROR_CODE * error_code)
{
int len = 0;
if (apdu_len > 2) {
if (invoke_id)
*invoke_id = apdu[0];
if (service)
*service = (BACNET_CONFIRMED_SERVICE) apdu[1];
/* decode the application class and code */
len =
bacerror_decode_error_class_and_code(&apdu[2], apdu_len - 2,
error_class, error_code);
}
return len;
}
#endif
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
/* decode the whole APDU - mainly used for unit testing */
int bacerror_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
BACNET_CONFIRMED_SERVICE * service,
BACNET_ERROR_CLASS * error_class,
BACNET_ERROR_CODE * error_code)
{
int len = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu_len) {
if (apdu[0] != PDU_TYPE_ERROR)
return -1;
if (apdu_len > 1) {
len =
bacerror_decode_service_request(&apdu[1], apdu_len - 1,
invoke_id, service, error_class, error_code);
}
}
return len;
}
void testBACError(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 0;
BACNET_CONFIRMED_SERVICE service = 0;
BACNET_ERROR_CLASS error_class = 0;
BACNET_ERROR_CODE error_code = 0;
uint8_t test_invoke_id = 0;
BACNET_CONFIRMED_SERVICE test_service = 0;
BACNET_ERROR_CLASS test_error_class = 0;
BACNET_ERROR_CODE test_error_code = 0;
len =
bacerror_encode_apdu(&apdu[0], invoke_id, service, error_class,
error_code);
ct_test(pTest, len != 0);
apdu_len = len;
len =
bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_service, &test_error_class, &test_error_code);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, test_service == service);
ct_test(pTest, test_error_class == error_class);
ct_test(pTest, test_error_code == error_code);
/* change type to get negative response */
apdu[0] = PDU_TYPE_ABORT;
len =
bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_service, &test_error_class, &test_error_code);
ct_test(pTest, len == -1);
/* test NULL APDU */
len =
bacerror_decode_apdu(NULL, apdu_len, &test_invoke_id, &test_service,
&test_error_class, &test_error_code);
ct_test(pTest, len == -1);
/* force a zero length */
len =
bacerror_decode_apdu(&apdu[0], 0, &test_invoke_id, &test_service,
&test_error_class, &test_error_code);
ct_test(pTest, len == 0);
/* check them all... */
for (service = 0; service < MAX_BACNET_CONFIRMED_SERVICE; service++) {
for (error_class = 0; error_class < ERROR_CLASS_PROPRIETARY_FIRST;
error_class++) {
for (error_code = 0; error_code < ERROR_CODE_PROPRIETARY_FIRST;
error_code++) {
len =
bacerror_encode_apdu(&apdu[0], invoke_id, service,
error_class, error_code);
apdu_len = len;
ct_test(pTest, len != 0);
len =
bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_service, &test_error_class, &test_error_code);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, test_service == service);
ct_test(pTest, test_error_class == error_class);
ct_test(pTest, test_error_code == error_code);
}
}
}
/* max boundaries */
service = 255;
error_class = ERROR_CLASS_PROPRIETARY_LAST;
error_code = ERROR_CODE_PROPRIETARY_LAST;
len =
bacerror_encode_apdu(&apdu[0], invoke_id, service, error_class,
error_code);
apdu_len = len;
ct_test(pTest, len != 0);
len =
bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_service, &test_error_class, &test_error_code);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, test_service == service);
ct_test(pTest, test_error_class == error_class);
ct_test(pTest, test_error_code == error_code);
}
#ifdef TEST_BACERROR
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Error", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBACError);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ERROR */
#endif /* TEST */
+440
View File
@@ -0,0 +1,440 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2004 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
/* BACnet Integer encoding and decoding */
#include <stdint.h>
#include <stdbool.h>
#include "config.h"
#include "bacint.h"
/** @file bacint.c Encode/Decode Integer Types */
int encode_unsigned16(
uint8_t * apdu,
uint16_t value)
{
apdu[0] = (uint8_t) ((value & 0xff00) >> 8);
apdu[1] = (uint8_t) (value & 0x00ff);
return 2;
}
int decode_unsigned16(
uint8_t * apdu,
uint16_t * value)
{
if (value) {
*value = (uint16_t) ((((uint16_t) apdu[0]) << 8) & 0xff00);
*value |= ((uint16_t) (((uint16_t) apdu[1]) & 0x00ff));
}
return 2;
}
int encode_unsigned24(
uint8_t * apdu,
uint32_t value)
{
apdu[0] = (uint8_t) ((value & 0xff0000) >> 16);
apdu[1] = (uint8_t) ((value & 0x00ff00) >> 8);
apdu[2] = (uint8_t) (value & 0x0000ff);
return 3;
}
int decode_unsigned24(
uint8_t * apdu,
uint32_t * value)
{
if (value) {
*value = ((uint32_t) ((((uint32_t) apdu[0]) << 16) & 0x00ff0000));
*value |= (uint32_t) ((((uint32_t) apdu[1]) << 8) & 0x0000ff00);
*value |= ((uint32_t) (((uint32_t) apdu[2]) & 0x000000ff));
}
return 3;
}
int encode_unsigned32(
uint8_t * apdu,
uint32_t value)
{
apdu[0] = (uint8_t) ((value & 0xff000000) >> 24);
apdu[1] = (uint8_t) ((value & 0x00ff0000) >> 16);
apdu[2] = (uint8_t) ((value & 0x0000ff00) >> 8);
apdu[3] = (uint8_t) (value & 0x000000ff);
return 4;
}
int decode_unsigned32(
uint8_t * apdu,
uint32_t * value)
{
if (value) {
*value = ((uint32_t) ((((uint32_t) apdu[0]) << 24) & 0xff000000));
*value |= ((uint32_t) ((((uint32_t) apdu[1]) << 16) & 0x00ff0000));
*value |= ((uint32_t) ((((uint32_t) apdu[2]) << 8) & 0x0000ff00));
*value |= ((uint32_t) (((uint32_t) apdu[3]) & 0x000000ff));
}
return 4;
}
#ifdef UINT64_MAX
/**
* @brief Encode a 64-bit unsigned value into buffer
* @param buffer - pointer to bytes for storing encoding
* @param value - 16-bit value to encode
* @return Returns the number of bytes encoded
*/
int encode_unsigned64(
uint8_t * buffer,
uint64_t value)
{
buffer[0] = (uint8_t) ((value & 0xff00000000000000) >> 56);
buffer[1] = (uint8_t) ((value & 0x00ff000000000000) >> 48);
buffer[2] = (uint8_t) ((value & 0x0000ff0000000000) >> 40);
buffer[3] = (uint8_t) ((value & 0x000000ff00000000) >> 32);
buffer[4] = (uint8_t) ((value & 0x00000000ff000000) >> 24);
buffer[5] = (uint8_t) ((value & 0x0000000000ff0000) >> 16);
buffer[6] = (uint8_t) ((value & 0x000000000000ff00) >> 8);
buffer[7] = (uint8_t) (value & 0x00000000000000ff);
return 8;
}
/**
* @brief Decode a 64-bit unsigned value from a buffer
* @param buffer - pointer to bytes for used for decoding
* @param value - pointer to 64-bit value to store the decoded value
* @return Returns the number of bytes decoded
*/
int decode_unsigned64(
uint8_t * buffer,
uint64_t * value)
{
if (value) {
*value = ((uint64_t) ((((uint64_t) buffer[0]) << 56) & 0xff00000000000000));
*value |= ((uint64_t) ((((uint64_t) buffer[1]) << 48) & 0x00ff000000000000));
*value |= ((uint64_t) ((((uint64_t) buffer[2]) << 40) & 0x0000ff0000000000));
*value |= ((uint64_t) ((((uint64_t) buffer[3]) << 32) & 0x000000ff00000000));
*value |= ((uint64_t) ((((uint64_t) buffer[4]) << 24) & 0x00000000ff000000));
*value |= ((uint64_t) ((((uint64_t) buffer[5]) << 16) & 0x0000000000ff0000));
*value |= ((uint64_t) ((((uint64_t) buffer[6]) << 8) & 0x000000000000ff00));
*value |= ((uint64_t) (((uint64_t) buffer[7]) & 0x00000000000000ff));
}
return 8;
}
#endif
#if BACNET_USE_SIGNED
int encode_signed8(
uint8_t * apdu,
int8_t value)
{
apdu[0] = (uint8_t) value;
return 1;
}
int decode_signed8(
uint8_t * apdu,
int32_t * value)
{
if (value) {
/* negative - bit 7 is set */
if (apdu[0] & 0x80)
*value = 0xFFFFFF00;
else
*value = 0;
*value |= ((int32_t) (((int32_t) apdu[0]) & 0x000000ff));
}
return 1;
}
int encode_signed16(
uint8_t * apdu,
int16_t value)
{
apdu[0] = (uint8_t) ((value & 0xff00) >> 8);
apdu[1] = (uint8_t) (value & 0x00ff);
return 2;
}
int decode_signed16(
uint8_t * apdu,
int32_t * value)
{
if (value) {
/* negative - bit 7 is set */
if (apdu[0] & 0x80)
*value = 0xFFFF0000;
else
*value = 0;
*value |= ((int32_t) ((((int32_t) apdu[0]) << 8) & 0x0000ff00));
*value |= ((int32_t) (((int32_t) apdu[1]) & 0x000000ff));
}
return 2;
}
int encode_signed24(
uint8_t * apdu,
int32_t value)
{
apdu[0] = (uint8_t) ((value & 0xff0000) >> 16);
apdu[1] = (uint8_t) ((value & 0x00ff00) >> 8);
apdu[2] = (uint8_t) (value & 0x0000ff);
return 3;
}
int decode_signed24(
uint8_t * apdu,
int32_t * value)
{
if (value) {
/* negative - bit 7 is set */
if (apdu[0] & 0x80)
*value = 0xFF000000;
else
*value = 0;
*value |= ((int32_t) ((((int32_t) apdu[0]) << 16) & 0x00ff0000));
*value |= ((int32_t) ((((int32_t) apdu[1]) << 8) & 0x0000ff00));
*value |= ((int32_t) (((int32_t) apdu[2]) & 0x000000ff));
}
return 3;
}
int encode_signed32(
uint8_t * apdu,
int32_t value)
{
apdu[0] = (uint8_t) ((value & 0xff000000) >> 24);
apdu[1] = (uint8_t) ((value & 0x00ff0000) >> 16);
apdu[2] = (uint8_t) ((value & 0x0000ff00) >> 8);
apdu[3] = (uint8_t) (value & 0x000000ff);
return 4;
}
int decode_signed32(
uint8_t * apdu,
int32_t * value)
{
if (value) {
*value = ((int32_t) ((((int32_t) apdu[0]) << 24) & 0xff000000));
*value |= ((int32_t) ((((int32_t) apdu[1]) << 16) & 0x00ff0000));
*value |= ((int32_t) ((((int32_t) apdu[2]) << 8) & 0x0000ff00));
*value |= ((int32_t) (((int32_t) apdu[3]) & 0x000000ff));
}
return 4;
}
#endif
/* end of decoding_encoding.c */
#ifdef TEST
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include "ctest.h"
static void testBACnetUnsigned16(
Test * pTest)
{
uint8_t apdu[32] = { 0 };
uint16_t value = 0, test_value = 0;
int len = 0;
for (value = 0;; value++) {
len = encode_unsigned16(&apdu[0], value);
ct_test(pTest, len == 2);
len = decode_unsigned16(&apdu[0], &test_value);
ct_test(pTest, value == test_value);
if (value == 0xFFFF)
break;
}
}
static void testBACnetUnsigned24(
Test * pTest)
{
uint8_t apdu[32] = { 0 };
uint32_t value = 0, test_value = 0;
int len = 0;
for (value = 0;; value += 0xf) {
len = encode_unsigned24(&apdu[0], value);
ct_test(pTest, len == 3);
len = decode_unsigned24(&apdu[0], &test_value);
ct_test(pTest, value == test_value);
if (value == 0xffffff)
break;
}
}
static void testBACnetUnsigned32(
Test * pTest)
{
uint8_t apdu[32] = { 0 };
uint32_t value = 0, test_value = 0;
int len = 0;
for (value = 0;; value += 0xff) {
len = encode_unsigned32(&apdu[0], value);
ct_test(pTest, len == 4);
len = decode_unsigned32(&apdu[0], &test_value);
ct_test(pTest, value == test_value);
if (value == 0xffffffff)
break;
}
}
static void testBACnetSigned8(
Test * pTest)
{
uint8_t apdu[32] = { 0 };
int32_t value = 0, test_value = 0;
int len = 0;
for (value = -127;; value++) {
len = encode_signed8(&apdu[0], value);
ct_test(pTest, len == 1);
len = decode_signed8(&apdu[0], &test_value);
ct_test(pTest, value == test_value);
if (value == 127)
break;
}
}
static void testBACnetSigned16(
Test * pTest)
{
uint8_t apdu[32] = { 0 };
int32_t value = 0, test_value = 0;
int len = 0;
for (value = -32767;; value++) {
len = encode_signed16(&apdu[0], value);
ct_test(pTest, len == 2);
len = decode_signed16(&apdu[0], &test_value);
ct_test(pTest, value == test_value);
if (value == 32767)
break;
}
}
static void testBACnetSigned24(
Test * pTest)
{
uint8_t apdu[32] = { 0 };
int32_t value = 0, test_value = 0;
int len = 0;
for (value = -8388607; value <= 8388607; value += 15) {
len = encode_signed24(&apdu[0], value);
ct_test(pTest, len == 3);
len = decode_signed24(&apdu[0], &test_value);
ct_test(pTest, value == test_value);
}
}
static void testBACnetSigned32(
Test * pTest)
{
uint8_t apdu[32] = { 0 };
int32_t value = 0, test_value = 0;
int len = 0;
for (value = -2147483647; value < 0; value += 127) {
len = encode_signed32(&apdu[0], value);
ct_test(pTest, len == 4);
len = decode_signed32(&apdu[0], &test_value);
ct_test(pTest, value == test_value);
}
for (value = 2147483647; value > 0; value -= 127) {
len = encode_signed32(&apdu[0], value);
ct_test(pTest, len == 4);
len = decode_signed32(&apdu[0], &test_value);
ct_test(pTest, value == test_value);
}
}
void testBACnetIntegers(
Test * pTest)
{
bool rc;
assert(pTest);
/* individual tests */
rc = ct_addTestFunction(pTest, testBACnetUnsigned16);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetUnsigned24);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetUnsigned32);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetSigned8);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetSigned16);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetSigned24);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetSigned32);
assert(rc);
}
#ifdef TEST_BACINT
int main(
void)
{
Test *pTest;
pTest = ct_create("BACint", NULL);
testBACnetIntegers(pTest);
/* configure output */
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_BACINT */
#endif /* TEST */
+121
View File
@@ -0,0 +1,121 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 John Goulah
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdbool.h>
#include <string.h>
#if PRINT_ENABLED
#include <stdio.h>
#endif
#include "bacprop.h"
/** @file bacprop.c Lookup BACnet Property Tags */
PROP_TAG_DATA bacnet_object_device_property_tag_map[] = {
{PROP_OBJECT_IDENTIFIER, BACNET_APPLICATION_TAG_OBJECT_ID}
,
{PROP_OBJECT_NAME, BACNET_APPLICATION_TAG_CHARACTER_STRING}
,
{PROP_OBJECT_TYPE, BACNET_APPLICATION_TAG_ENUMERATED}
,
{PROP_SYSTEM_STATUS, BACNET_APPLICATION_TAG_ENUMERATED}
,
{PROP_VENDOR_NAME, BACNET_APPLICATION_TAG_CHARACTER_STRING}
,
{PROP_VENDOR_IDENTIFIER, BACNET_APPLICATION_TAG_UNSIGNED_INT}
,
{PROP_MODEL_NAME, BACNET_APPLICATION_TAG_CHARACTER_STRING}
,
{PROP_FIRMWARE_REVISION, BACNET_APPLICATION_TAG_CHARACTER_STRING}
,
{PROP_APPLICATION_SOFTWARE_VERSION,
BACNET_APPLICATION_TAG_CHARACTER_STRING}
,
{PROP_PROTOCOL_VERSION, BACNET_APPLICATION_TAG_UNSIGNED_INT}
,
{PROP_PROTOCOL_CONFORMANCE_CLASS, BACNET_APPLICATION_TAG_UNSIGNED_INT}
,
{PROP_PROTOCOL_SERVICES_SUPPORTED, BACNET_APPLICATION_TAG_BIT_STRING}
,
{PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED,
BACNET_APPLICATION_TAG_BIT_STRING}
,
{PROP_MAX_APDU_LENGTH_ACCEPTED, BACNET_APPLICATION_TAG_UNSIGNED_INT}
,
{PROP_SEGMENTATION_SUPPORTED, BACNET_APPLICATION_TAG_ENUMERATED}
,
{PROP_APDU_TIMEOUT, BACNET_APPLICATION_TAG_UNSIGNED_INT}
,
{PROP_NUMBER_OF_APDU_RETRIES, BACNET_APPLICATION_TAG_UNSIGNED_INT}
,
{-1, -1}
};
signed bacprop_tag_by_index_default(
PROP_TAG_DATA * data_list,
signed index,
signed default_ret)
{
signed pUnsigned = 0;
if (data_list) {
while (data_list->prop_id != -1) {
if (data_list->prop_id == index) {
pUnsigned = data_list->tag_id;
break;
}
data_list++;
}
}
return pUnsigned ? pUnsigned : default_ret;
}
signed bacprop_property_tag(
BACNET_OBJECT_TYPE type,
signed prop)
{
switch (type) {
case OBJECT_DEVICE:
return
bacprop_tag_by_index_default
(bacnet_object_device_property_tag_map, prop, -1);
default:
#if PRINT_ENABLED
fprintf(stderr, "Unsupported object type");
#endif
break;
}
return -1;
}
+445
View File
@@ -0,0 +1,445 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2008 John Minack
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include "bacdcode.h"
#include "npdu.h"
#include "timestamp.h"
#include "bacpropstates.h"
/** @file bacpropstates.c Encode/Decode BACnet Application Property States */
int bacapp_decode_property_state(
uint8_t * apdu,
BACNET_PROPERTY_STATE * value)
{
int len = 0;
uint32_t len_value_type;
int section_length;
uint32_t enumValue;
uint8_t tagnum;
section_length =
decode_tag_number_and_value(&apdu[len], &tagnum, &len_value_type);
if (-1 == section_length) {
return -1;
}
value->tag = tagnum;
len += section_length;
switch (value->tag) {
case BOOLEAN_VALUE:
value->state.booleanValue = decode_boolean(len_value_type);
break;
case BINARY_VALUE:
if (-1 == (section_length =
decode_enumerated(&apdu[len], len_value_type,
&enumValue))) {
return -1;
}
value->state.binaryValue = (BACNET_BINARY_PV) enumValue;
break;
case EVENT_TYPE:
if (-1 == (section_length =
decode_enumerated(&apdu[len], len_value_type,
&enumValue))) {
return -1;
}
value->state.eventType = (BACNET_EVENT_TYPE) enumValue;
break;
case POLARITY:
if (-1 == (section_length =
decode_enumerated(&apdu[len], len_value_type,
&enumValue))) {
return -1;
}
value->state.polarity = (BACNET_POLARITY) enumValue;
break;
case PROGRAM_CHANGE:
if (-1 == (section_length =
decode_enumerated(&apdu[len], len_value_type,
&enumValue))) {
return -1;
}
value->state.programChange = (BACNET_PROGRAM_REQUEST) enumValue;
break;
case PROGRAM_STATE:
if (-1 == (section_length =
decode_enumerated(&apdu[len], len_value_type,
&enumValue))) {
return -1;
}
value->state.programState = (BACNET_PROGRAM_STATE) enumValue;
break;
case REASON_FOR_HALT:
if (-1 == (section_length =
decode_enumerated(&apdu[len], len_value_type,
&enumValue))) {
return -1;
}
value->state.programError = (BACNET_PROGRAM_ERROR) enumValue;
break;
case RELIABILITY:
if (-1 == (section_length =
decode_enumerated(&apdu[len], len_value_type,
&enumValue))) {
return -1;
}
value->state.reliability = (BACNET_RELIABILITY) enumValue;
break;
case STATE:
if (-1 == (section_length =
decode_enumerated(&apdu[len], len_value_type,
&enumValue))) {
return -1;
}
value->state.state = (BACNET_EVENT_STATE) enumValue;
break;
case SYSTEM_STATUS:
if (-1 == (section_length =
decode_enumerated(&apdu[len], len_value_type,
&enumValue))) {
return -1;
}
value->state.systemStatus = (BACNET_DEVICE_STATUS) enumValue;
break;
case UNITS:
if (-1 == (section_length =
decode_enumerated(&apdu[len], len_value_type,
&enumValue))) {
return -1;
}
value->state.units = (BACNET_ENGINEERING_UNITS) enumValue;
break;
case UNSIGNED_VALUE:
if (-1 == (section_length =
decode_unsigned(&apdu[len], len_value_type,
&value->state.unsignedValue))) {
return -1;
}
break;
case LIFE_SAFETY_MODE:
if (-1 == (section_length =
decode_enumerated(&apdu[len], len_value_type,
&enumValue))) {
return -1;
}
value->state.lifeSafetyMode = (BACNET_LIFE_SAFETY_MODE) enumValue;
break;
case LIFE_SAFETY_STATE:
if (-1 == (section_length =
decode_enumerated(&apdu[len], len_value_type,
&enumValue))) {
return -1;
}
value->state.lifeSafetyState =
(BACNET_LIFE_SAFETY_STATE) enumValue;
break;
default:
return -1;
}
len += section_length;
return len;
}
int bacapp_decode_context_property_state(
uint8_t * apdu,
uint8_t tag_number,
BACNET_PROPERTY_STATE * value)
{
int len = 0;
int section_length;
if (decode_is_opening_tag_number(&apdu[len], tag_number)) {
len++;
section_length = bacapp_decode_property_state(&apdu[len], value);
if (section_length == -1) {
len = -1;
} else {
len += section_length;
if (decode_is_closing_tag_number(&apdu[len], tag_number)) {
len++;
} else {
len = -1;
}
}
} else {
len = -1;
}
return len;
}
int bacapp_encode_property_state(
uint8_t * apdu,
BACNET_PROPERTY_STATE * value)
{
int len = 0; /* length of each encoding */
if (value && apdu) {
switch (value->tag) {
case BOOLEAN_VALUE:
len =
encode_context_boolean(&apdu[0], 0,
value->state.booleanValue);
break;
case BINARY_VALUE:
len =
encode_context_enumerated(&apdu[0], 1,
value->state.binaryValue);
break;
case EVENT_TYPE:
len =
encode_context_enumerated(&apdu[0], 2,
value->state.eventType);
break;
case POLARITY:
len =
encode_context_enumerated(&apdu[0], 3,
value->state.polarity);
break;
case PROGRAM_CHANGE:
len =
encode_context_enumerated(&apdu[0], 4,
value->state.programChange);
break;
case PROGRAM_STATE:
len =
encode_context_enumerated(&apdu[0], 5,
value->state.programState);
break;
case REASON_FOR_HALT:
len =
encode_context_enumerated(&apdu[0], 6,
value->state.programError);
break;
case RELIABILITY:
len =
encode_context_enumerated(&apdu[0], 7,
value->state.reliability);
break;
case STATE:
len =
encode_context_enumerated(&apdu[0], 8, value->state.state);
break;
case SYSTEM_STATUS:
len =
encode_context_enumerated(&apdu[0], 9,
value->state.systemStatus);
break;
case UNITS:
len =
encode_context_enumerated(&apdu[0], 10,
value->state.units);
break;
case UNSIGNED_VALUE:
len =
encode_context_unsigned(&apdu[0], 11,
value->state.unsignedValue);
break;
case LIFE_SAFETY_MODE:
len =
encode_context_enumerated(&apdu[0], 12,
value->state.lifeSafetyMode);
break;
case LIFE_SAFETY_STATE:
len =
encode_context_enumerated(&apdu[0], 13,
value->state.lifeSafetyState);
break;
default:
/* FIXME: assert(0); - return a negative len? */
break;
}
}
return len;
}
#ifdef TEST
#include <string.h> /* for memset */
void testPropStates(
Test * pTest)
{
BACNET_PROPERTY_STATE inData;
BACNET_PROPERTY_STATE outData;
uint8_t appMsg[MAX_APDU];
int inLen;
int outLen;
inData.tag = BOOLEAN_VALUE;
inData.state.booleanValue = true;
inLen = bacapp_encode_property_state(appMsg, &inData);
memset(&outData, 0, sizeof(outData));
outLen = bacapp_decode_property_state(appMsg, &outData);
ct_test(pTest, outLen == inLen);
ct_test(pTest, inData.tag == outData.tag);
ct_test(pTest, inData.state.booleanValue == outData.state.booleanValue);
/****************
*****************
****************/
inData.tag = BINARY_VALUE;
inData.state.binaryValue = BINARY_ACTIVE;
inLen = bacapp_encode_property_state(appMsg, &inData);
memset(&outData, 0, sizeof(outData));
outLen = bacapp_decode_property_state(appMsg, &outData);
ct_test(pTest, outLen == inLen);
ct_test(pTest, inData.tag == outData.tag);
ct_test(pTest, inData.state.binaryValue == outData.state.binaryValue);
/****************
*****************
****************/
inData.tag = EVENT_TYPE;
inData.state.eventType = EVENT_BUFFER_READY;
inLen = bacapp_encode_property_state(appMsg, &inData);
memset(&outData, 0, sizeof(outData));
outLen = bacapp_decode_property_state(appMsg, &outData);
ct_test(pTest, outLen == inLen);
ct_test(pTest, inData.tag == outData.tag);
ct_test(pTest, inData.state.eventType == outData.state.eventType);
/****************
*****************
****************/
inData.tag = POLARITY;
inData.state.polarity = POLARITY_REVERSE;
inLen = bacapp_encode_property_state(appMsg, &inData);
memset(&outData, 0, sizeof(outData));
outLen = bacapp_decode_property_state(appMsg, &outData);
ct_test(pTest, outLen == inLen);
ct_test(pTest, inData.tag == outData.tag);
ct_test(pTest, inData.state.polarity == outData.state.polarity);
/****************
*****************
****************/
inData.tag = PROGRAM_CHANGE;
inData.state.programChange = PROGRAM_REQUEST_RESTART;
inLen = bacapp_encode_property_state(appMsg, &inData);
memset(&outData, 0, sizeof(outData));
outLen = bacapp_decode_property_state(appMsg, &outData);
ct_test(pTest, outLen == inLen);
ct_test(pTest, inData.tag == outData.tag);
ct_test(pTest, inData.state.programChange == outData.state.programChange);
/****************
*****************
****************/
inData.tag = UNSIGNED_VALUE;
inData.state.unsignedValue = 0xdeadbeef;
inLen = bacapp_encode_property_state(appMsg, &inData);
memset(&outData, 0, sizeof(outData));
outLen = bacapp_decode_property_state(appMsg, &outData);
ct_test(pTest, outLen == inLen);
ct_test(pTest, inData.tag == outData.tag);
ct_test(pTest, inData.state.unsignedValue == outData.state.unsignedValue);
}
#ifdef TEST_PROP_STATES
#include <assert.h>
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Event", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testPropStates);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_PROP_STATES */
#endif /* TEST */
+306
View File
@@ -0,0 +1,306 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2004-2008 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <string.h>
#include "bacdef.h"
#include "bacdcode.h"
#include "bacenum.h"
#include "bits.h"
#include "bacstr.h"
#include "bacint.h"
#include "bacreal.h"
/** @file bacreal.c Encode/Decode Floating Point (Real) Types */
/* NOTE: byte order plays a role in decoding multibyte values */
/* http://www.unixpapa.com/incnote/byteorder.html */
#ifndef BIG_ENDIAN
#error Define BIG_ENDIAN=0 or BIG_ENDIAN=1 for BACnet Stack in compiler settings
#endif
/* from clause 20.2.6 Encoding of a Real Number Value */
/* returns the number of apdu bytes consumed */
int decode_real(
uint8_t * apdu,
float *real_value)
{
union {
uint8_t byte[4];
float real_value;
} my_data;
/* NOTE: assumes the compiler stores float as IEEE-754 float */
#if BIG_ENDIAN
my_data.byte[0] = apdu[0];
my_data.byte[1] = apdu[1];
my_data.byte[2] = apdu[2];
my_data.byte[3] = apdu[3];
#else
my_data.byte[0] = apdu[3];
my_data.byte[1] = apdu[2];
my_data.byte[2] = apdu[1];
my_data.byte[3] = apdu[0];
#endif
*real_value = my_data.real_value;
return 4;
}
int decode_real_safe(
uint8_t * apdu,
uint32_t len_value,
float *real_value)
{
if (len_value != 4) {
*real_value = 0.0f;
return (int) len_value;
} else {
return decode_real(apdu, real_value);
}
}
int decode_context_real(
uint8_t * apdu,
uint8_t tag_number,
float *real_value)
{
uint32_t len_value;
int len = 0;
if (decode_is_context_tag(&apdu[len], tag_number)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
len += decode_real(&apdu[len], real_value);
} else {
len = -1;
}
return len;
}
/* from clause 20.2.6 Encoding of a Real Number Value */
/* returns the number of apdu bytes consumed */
int encode_bacnet_real(
float value,
uint8_t * apdu)
{
union {
uint8_t byte[4];
float real_value;
} my_data;
/* NOTE: assumes the compiler stores float as IEEE-754 float */
my_data.real_value = value;
#if BIG_ENDIAN
apdu[0] = my_data.byte[0];
apdu[1] = my_data.byte[1];
apdu[2] = my_data.byte[2];
apdu[3] = my_data.byte[3];
#else
apdu[0] = my_data.byte[3];
apdu[1] = my_data.byte[2];
apdu[2] = my_data.byte[1];
apdu[3] = my_data.byte[0];
#endif
return 4;
}
#if BACNET_USE_DOUBLE
/* from clause 20.2.7 Encoding of a Double Precision Real Number Value */
/* returns the number of apdu bytes consumed */
int decode_double(
uint8_t * apdu,
double *double_value)
{
union {
uint8_t byte[8];
double double_value;
} my_data;
/* NOTE: assumes the compiler stores float as IEEE-754 float */
#if BIG_ENDIAN
my_data.byte[0] = apdu[0];
my_data.byte[1] = apdu[1];
my_data.byte[2] = apdu[2];
my_data.byte[3] = apdu[3];
my_data.byte[4] = apdu[4];
my_data.byte[5] = apdu[5];
my_data.byte[6] = apdu[6];
my_data.byte[7] = apdu[7];
#else
my_data.byte[0] = apdu[7];
my_data.byte[1] = apdu[6];
my_data.byte[2] = apdu[5];
my_data.byte[3] = apdu[4];
my_data.byte[4] = apdu[3];
my_data.byte[5] = apdu[2];
my_data.byte[6] = apdu[1];
my_data.byte[7] = apdu[0];
#endif
*double_value = my_data.double_value;
return 8;
}
int decode_double_safe(
uint8_t * apdu,
uint32_t len_value,
double *double_value)
{
if (len_value != 8) {
*double_value = 0.0;
return (int) len_value;
} else {
return decode_double(apdu, double_value);
}
}
/* from clause 20.2.7 Encoding of a Double Precision Real Number Value */
/* returns the number of apdu bytes consumed */
int encode_bacnet_double(
double value,
uint8_t * apdu)
{
union {
uint8_t byte[8];
double double_value;
} my_data;
/* NOTE: assumes the compiler stores float as IEEE-754 float */
my_data.double_value = value;
#if BIG_ENDIAN
apdu[0] = my_data.byte[0];
apdu[1] = my_data.byte[1];
apdu[2] = my_data.byte[2];
apdu[3] = my_data.byte[3];
apdu[4] = my_data.byte[4];
apdu[5] = my_data.byte[5];
apdu[6] = my_data.byte[6];
apdu[7] = my_data.byte[7];
#else
apdu[0] = my_data.byte[7];
apdu[1] = my_data.byte[6];
apdu[2] = my_data.byte[5];
apdu[3] = my_data.byte[4];
apdu[4] = my_data.byte[3];
apdu[5] = my_data.byte[2];
apdu[6] = my_data.byte[1];
apdu[7] = my_data.byte[0];
#endif
return 8;
}
int decode_context_double(
uint8_t * apdu,
uint8_t tag_number,
double *double_value)
{
uint32_t len_value;
int len = 0;
if (decode_is_context_tag(&apdu[len], tag_number)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
len += decode_double(&apdu[len], double_value);
} else {
len = -1;
}
return len;
}
#endif
/* end of decoding_encoding.c */
#ifdef TEST
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include "ctest.h"
void testBACreal(
Test * pTest)
{
float real_value = 3.14159F, test_real_value = 0.0;
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0, test_len = 0;
len = encode_bacnet_real(real_value, &apdu[0]);
ct_test(pTest, len == 4);
test_len = decode_real(&apdu[0], &test_real_value);
ct_test(pTest, test_len == len);
ct_test(pTest, test_real_value == real_value);
}
void testBACdouble(
Test * pTest)
{
double double_value = 3.1415927, test_double_value = 0.0;
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0, test_len = 0;
len = encode_bacnet_double(double_value, &apdu[0]);
ct_test(pTest, len == 8);
test_len = decode_double(&apdu[0], &test_double_value);
ct_test(pTest, test_len == len);
ct_test(pTest, test_double_value == double_value);
}
#ifdef TEST_BACNET_REAL
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACreal", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBACreal);
assert(rc);
rc = ct_addTestFunction(pTest, testBACdouble);
assert(rc);
/* configure output */
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_BACNET_REAL */
#endif /* TEST */
+834
View File
@@ -0,0 +1,834 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "bacdcode.h"
#include "bacsec.h"
BACNET_KEY_IDENTIFIER_ALGORITHM key_algorithm(uint16_t id)
{
return (BACNET_KEY_IDENTIFIER_ALGORITHM) ((id >> 8) & 0xFF);
}
BACNET_KEY_IDENTIFIER_KEY_NUMBER key_number(uint16_t id)
{
return (BACNET_KEY_IDENTIFIER_KEY_NUMBER) (id & 0xFF);
}
int encode_security_wrapper(int bytes_before,
uint8_t * apdu,
BACNET_SECURITY_WRAPPER * wrapper)
{
int curr = 0;
int enc_begin = 0;
BACNET_KEY_ENTRY key;
BACNET_SECURITY_RESPONSE_CODE res = SEC_RESP_SUCCESS;
apdu[curr] = 0;
/* control byte */
if (wrapper->payload_net_or_bvll_flag) {
apdu[curr] |= 1 << 7;
}
/* encryption flag will be set after signature calculation */
/* bit 5 is reserved and shall be 0 */
if (wrapper->authentication_flag) {
apdu[curr] |= 1 << 4;
}
if (wrapper->do_not_unwrap_flag) {
apdu[curr] |= 1 << 3;
}
if (wrapper->do_not_decrypt_flag) {
apdu[curr] |= 1 << 2;
}
if (wrapper->non_trusted_source_flag) {
apdu[curr] |= 1 << 1;
}
if (wrapper->secured_by_router_flag) {
apdu[curr] |= 1;
}
curr++;
/* basic integrity checks */
if (wrapper->do_not_decrypt_flag && !wrapper->do_not_unwrap_flag) {
return -SEC_RESP_MALFORMED_MESSAGE;
}
if (!wrapper->encrypted_flag && wrapper->do_not_decrypt_flag) {
return -SEC_RESP_MALFORMED_MESSAGE;
}
/* key */
apdu[curr++] = wrapper->key_revision;
curr += encode_unsigned16(&apdu[curr], wrapper->key_identifier);
/* find appropriate key */
key.key_identifier = wrapper->key_identifier;
res = bacnet_find_key(wrapper->key_revision, &key);
if (res != SEC_RESP_SUCCESS) {
return -res;
}
/* source device instance */
curr += encode_unsigned24(&apdu[curr], wrapper->source_device_instance);
/* message id */
curr += encode_unsigned32(&apdu[curr], wrapper->message_id);
/* timestamp */
curr += encode_unsigned32(&apdu[curr], wrapper->timestamp);
/* begin encryption starting from destination device instance */
enc_begin = curr;
/* destination device instance */
curr +=
encode_unsigned24(&apdu[curr], wrapper->destination_device_instance);
/* dst address */
curr += encode_unsigned16(&apdu[curr], wrapper->dnet);
apdu[curr++] = wrapper->dlen;
memcpy(&apdu[curr], wrapper->dadr, wrapper->dlen);
curr += wrapper->dlen;
/* src address */
curr += encode_unsigned16(&apdu[curr], wrapper->snet);
apdu[curr++] = wrapper->slen;
memcpy(&apdu[curr], wrapper->sadr, wrapper->slen);
curr += wrapper->slen;
/* authentication */
if (wrapper->authentication_flag) {
apdu[curr++] = wrapper->authentication_mechanism;
/* authentication data */
curr += encode_unsigned16(&apdu[curr], wrapper->user_id);
apdu[curr++] = wrapper->user_role;
if ((wrapper->authentication_mechanism >= 1) &&
(wrapper->authentication_mechanism <= 199)) {
curr +=
encode_unsigned16(&apdu[curr],
wrapper->authentication_data_length + 5);
memcpy(&apdu[curr], wrapper->authentication_data,
wrapper->authentication_data_length);
curr += wrapper->authentication_data_length;
} else if (wrapper->authentication_mechanism >= 200) {
curr +=
encode_unsigned16(&apdu[curr],
wrapper->authentication_data_length + 7);
curr += encode_unsigned16(&apdu[curr], wrapper->vendor_id);
memcpy(&apdu[curr], wrapper->authentication_data,
wrapper->authentication_data_length);
curr += wrapper->authentication_data_length;
}
}
memcpy(&apdu[curr], wrapper->service_data, wrapper->service_data_len);
curr += wrapper->service_data_len;
/* signature calculation */
key_sign_msg(&key, &apdu[-bytes_before], (uint32_t)(bytes_before + curr),
wrapper->signature);
/* padding and encryption */
if (wrapper->encrypted_flag) {
/* set encryption flag, signing is done */
apdu[0] |= 1 << 6;
/* handle padding */
key_set_padding(&key, curr - enc_begin, &wrapper->padding_len,
wrapper->padding);
if (wrapper->padding_len > 2) {
memcpy(&apdu[curr], wrapper->padding, wrapper->padding_len - 2);
curr += wrapper->padding_len - 2;
}
curr += encode_unsigned16(&apdu[curr], wrapper->padding_len);
/* encryption */
key_encrypt_msg(&key, &apdu[enc_begin], (uint32_t)(curr - enc_begin),
wrapper->signature);
}
memcpy(&apdu[curr], wrapper->signature, SIGNATURE_LEN);
curr += SIGNATURE_LEN;
return curr;
}
int encode_challenge_request(uint8_t * apdu,
BACNET_CHALLENGE_REQUEST * bc_req)
{
int curr = 0;
apdu[curr++] = bc_req->message_challenge;
curr += encode_unsigned32(&apdu[curr], bc_req->orig_message_id);
curr += encode_unsigned32(&apdu[curr], bc_req->orig_timestamp);
return curr;
}
int encode_security_payload(uint8_t * apdu,
BACNET_SECURITY_PAYLOAD * payload)
{
encode_unsigned16(&apdu[0], payload->payload_length);
memcpy(&apdu[2], payload->payload, payload->payload_length);
return (int)(2 + payload->payload_length);
}
int encode_security_response(uint8_t * apdu,
BACNET_SECURITY_RESPONSE * resp)
{
int curr = 0;
int i;
apdu[curr++] = resp->response_code;
curr += encode_unsigned32(&apdu[curr], resp->orig_message_id);
curr += encode_unsigned32(&apdu[curr], resp->orig_timestamp);
switch ((BACNET_SECURITY_RESPONSE_CODE) resp->response_code) {
case SEC_RESP_BAD_TIMESTAMP:
curr +=
encode_unsigned32(&apdu[curr],
resp->response.bad_timestamp.expected_timestamp);
break;
case SEC_RESP_CANNOT_USE_KEY:
curr +=
encode_unsigned16(&apdu[curr],
resp->response.cannot_use_key.key);
break;
case SEC_RESP_INCORRECT_KEY:
apdu[curr++] = resp->response.incorrect_key.number_of_keys;
for (i = 0; i < (int)resp->response.incorrect_key.number_of_keys; i++) {
curr +=
encode_unsigned16(&apdu[curr],
resp->response.incorrect_key.keys[i]);
}
break;
case SEC_RESP_UNKNOWN_AUTHENTICATION_TYPE:
apdu[curr++] =
resp->response.unknown_authentication_type.
original_authentication_type;
curr +=
encode_unsigned16(&apdu[curr],
resp->response.unknown_authentication_type.vendor_id);
break;
case SEC_RESP_UNKNOWN_KEY:
curr +=
encode_unsigned16(&apdu[curr],
resp->response.unknown_key.original_key);
break;
case SEC_RESP_UNKNOWN_KEY_REVISION:
apdu[curr++] =
resp->response.unknown_key_revision.original_key_revision;
break;
case SEC_RESP_TOO_MANY_KEYS:
apdu[curr++] = resp->response.too_many_keys.max_num_of_keys;
break;
case SEC_RESP_INVALID_KEY_DATA:
curr +=
encode_unsigned16(&apdu[curr],
resp->response.invalid_key_data.key);
break;
case SEC_RESP_SUCCESS:
case SEC_RESP_ACCESS_DENIED:
case SEC_RESP_BAD_DESTINATION_ADDRESS:
case SEC_RESP_BAD_DESTINATION_DEVICE_ID:
case SEC_RESP_BAD_SIGNATURE:
case SEC_RESP_BAD_SOURCE_ADDRESS:
case SEC_RESP_CANNOT_VERIFY_MESSAGE_ID:
case SEC_RESP_CORRECT_KEY_REVISION:
case SEC_RESP_DESTINATION_DEVICE_ID_REQUIRED:
case SEC_RESP_DUPLICATE_MESSAGE:
case SEC_RESP_ENCRYPTION_NOT_CONFIGURED:
case SEC_RESP_ENCRYPTION_REQUIRED:
case SEC_RESP_KEY_UPDATE_IN_PROGRESS:
case SEC_RESP_MALFORMED_MESSAGE:
case SEC_RESP_NOT_KEY_SERVER:
case SEC_RESP_SECURITY_NOT_CONFIGURED:
case SEC_RESP_SOURCE_SECURITY_REQUIRED:
case SEC_RESP_UNKNOWN_SOURCE_MESSAGE:
break;
default:
return -1; /* unknown message type */
}
return curr;
}
int encode_request_key_update(uint8_t * apdu,
BACNET_REQUEST_KEY_UPDATE * req)
{
int curr = 0;
apdu[curr++] = req->set_1_key_revision;
curr += encode_unsigned32(&apdu[curr], req->set_1_activation_time);
curr += encode_unsigned32(&apdu[curr], req->set_1_expiration_time);
apdu[curr++] = req->set_2_key_revision;
curr += encode_unsigned32(&apdu[curr], req->set_2_activation_time);
curr += encode_unsigned32(&apdu[curr], req->set_2_expiration_time);
apdu[curr++] = req->distribution_key_revision;
return curr;
}
int encode_key_entry(uint8_t * apdu,
BACNET_KEY_ENTRY * entry)
{
int curr = 0;
curr += encode_unsigned16(&apdu[curr], entry->key_identifier);
apdu[curr++] = entry->key_len;
memcpy(&apdu[curr], entry->key, entry->key_len);
curr += entry->key_len;
return curr;
}
int encode_update_key_set(uint8_t * apdu,
BACNET_UPDATE_KEY_SET * key_set)
{
int curr = 0;
int i, res;
apdu[curr] = 0;
if (key_set->remove) {
apdu[curr] |= 1;
}
if (key_set->more) {
apdu[curr] |= 1 << 1;
}
if (key_set->set_clr[1]) {
apdu[curr] |= 1 << 2;
}
if (key_set->set_ck[1]) {
apdu[curr] |= 1 << 3;
}
if (key_set->set_rae[1]) {
apdu[curr] |= 1 << 4;
}
if (key_set->set_clr[0]) {
apdu[curr] |= 1 << 5;
}
if (key_set->set_ck[0]) {
apdu[curr] |= 1 << 6;
}
if (key_set->set_rae[0]) {
apdu[curr] |= 1 << 7;
}
curr++;
if (key_set->set_rae[0]) {
apdu[curr++] = key_set->set_key_revision[0];
curr +=
encode_unsigned32(&apdu[curr], key_set->set_activation_time[0]);
curr +=
encode_unsigned32(&apdu[curr], key_set->set_expiration_time[0]);
}
if (key_set->set_ck[0]) {
apdu[curr++] = key_set->set_key_count[0];
if (key_set->set_key_count[0] > MAX_UPDATE_KEY_COUNT) {
return -1;
}
for (i = 0; i < (int)key_set->set_key_count[0]; i++) {
res = encode_key_entry(&apdu[curr], &key_set->set_keys[0][i]);
if (res < 0) {
return -1;
}
curr += res;
}
}
if (key_set->set_rae[1]) {
apdu[curr++] = key_set->set_key_revision[1];
curr +=
encode_unsigned32(&apdu[curr], key_set->set_activation_time[1]);
curr +=
encode_unsigned32(&apdu[curr], key_set->set_expiration_time[1]);
}
if (key_set->set_ck[1]) {
apdu[curr++] = key_set->set_key_count[1];
if (key_set->set_key_count[1] > MAX_UPDATE_KEY_COUNT) {
return -1;
}
for (i = 0; i < (int)key_set->set_key_count[1]; i++) {
res = encode_key_entry(&apdu[curr], &key_set->set_keys[1][i]);
if (res < 0) {
return -1;
}
curr += res;
}
}
return curr;
}
int encode_update_distribution_key(uint8_t * apdu,
BACNET_UPDATE_DISTRIBUTION_KEY * dist_key)
{
int curr = 0;
int res;
apdu[curr++] = dist_key->key_revision;
res = encode_key_entry(&apdu[curr], &dist_key->key);
if (res < 0) {
return -1;
}
return curr + res;
}
int encode_request_master_key(uint8_t * apdu,
BACNET_REQUEST_MASTER_KEY * req_master_key)
{
int curr = 0;
apdu[curr++] = req_master_key->no_supported_algorithms;
memcpy(&apdu[curr], req_master_key->es_algorithms,
req_master_key->no_supported_algorithms);
return (int)(curr + req_master_key->no_supported_algorithms);
}
int encode_set_master_key(uint8_t * apdu,
BACNET_SET_MASTER_KEY * set_master_key)
{
return encode_key_entry(apdu, &set_master_key->key);
}
int decode_security_wrapper_safe(int bytes_before,
uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_SECURITY_WRAPPER * wrapper)
{
int curr = 0;
int enc_begin = 0;
int real_len = (int)(apdu_len_remaining - SIGNATURE_LEN);
BACNET_KEY_ENTRY key;
BACNET_SECURITY_RESPONSE_CODE res = SEC_RESP_SUCCESS;
if (apdu_len_remaining < 40) {
return -SEC_RESP_MALFORMED_MESSAGE;
}
wrapper->payload_net_or_bvll_flag = ((apdu[curr] & (1 << 7)) != 0);
wrapper->encrypted_flag = ((apdu[curr] & (1 << 6)) != 0);
wrapper->authentication_flag = ((apdu[curr] & (1 << 4)) != 0);
wrapper->do_not_unwrap_flag = ((apdu[curr] & (1 << 3)) != 0);
wrapper->do_not_decrypt_flag = ((apdu[curr] & (1 << 2)) != 0);
wrapper->non_trusted_source_flag = ((apdu[curr] & (1 << 1)) != 0);
wrapper->secured_by_router_flag = ((apdu[curr] & 1) != 0);
/* basic integrity checks */
if (wrapper->do_not_decrypt_flag && !wrapper->do_not_unwrap_flag) {
return -SEC_RESP_MALFORMED_MESSAGE;
}
if (!wrapper->encrypted_flag && wrapper->do_not_decrypt_flag) {
return -SEC_RESP_MALFORMED_MESSAGE;
}
/* remove encryption flag for signature validation */
apdu[curr] &= ~((uint8_t) (1 << 6));
curr++;
/* key */
wrapper->key_revision = apdu[curr++];
curr += decode_unsigned16(&apdu[curr], &wrapper->key_identifier);
/* find appropriate key */
key.key_identifier = wrapper->key_identifier;
res = bacnet_find_key(wrapper->key_revision, &key);
if (res != SEC_RESP_SUCCESS) {
return -res;
}
/* source device instance */
curr += decode_unsigned24(&apdu[curr], &wrapper->source_device_instance);
/* message id */
curr += decode_unsigned32(&apdu[curr], &wrapper->message_id);
/* timestamp */
curr += decode_unsigned32(&apdu[curr], &wrapper->timestamp);
/* begin decryption starting from destination device instance */
enc_begin = curr;
/* read signature */
memcpy(wrapper->signature, &apdu[real_len], SIGNATURE_LEN);
if (wrapper->encrypted_flag) {
if (!key_decrypt_msg(&key, &apdu[enc_begin],
(uint32_t)(real_len - enc_begin), wrapper->signature)) {
return -SEC_RESP_MALFORMED_MESSAGE;
}
curr += decode_unsigned16(&apdu[real_len - 2], &wrapper->padding_len);
real_len -= wrapper->padding_len;
memcpy(wrapper->padding, &apdu[wrapper->padding_len],
wrapper->padding_len - 2);
}
/* destination device instance */
curr +=
decode_unsigned24(&apdu[curr], &wrapper->destination_device_instance);
/* dst address */
curr += decode_unsigned16(&apdu[curr], &wrapper->dnet);
wrapper->dlen = apdu[curr++];
memcpy(wrapper->dadr, &apdu[curr], wrapper->dlen);
curr += wrapper->dlen;
/* src address */
curr += decode_unsigned16(&apdu[curr], &wrapper->snet);
wrapper->slen = apdu[curr++];
memcpy(wrapper->sadr, &apdu[curr], wrapper->slen);
curr += wrapper->slen;
/* authentication */
if (wrapper->authentication_flag) {
wrapper->authentication_mechanism = apdu[curr++];
/* authentication data */
curr += decode_unsigned16(&apdu[curr], &wrapper->user_id);
wrapper->user_role = apdu[curr++];
if ((wrapper->authentication_mechanism >= 1) &&
(wrapper->authentication_mechanism <= 199)) {
curr +=
decode_unsigned16(&apdu[curr],
&wrapper->authentication_data_length);
wrapper->authentication_data_length -= 5;
memcpy(wrapper->authentication_data, &apdu[curr],
wrapper->authentication_data_length);
curr += wrapper->authentication_data_length;
} else if (wrapper->authentication_mechanism >= 200) {
curr +=
decode_unsigned16(&apdu[curr],
&wrapper->authentication_data_length);
wrapper->authentication_data_length -= 7;
curr += decode_unsigned16(&apdu[curr], &wrapper->vendor_id);
memcpy(wrapper->authentication_data, &apdu[curr],
wrapper->authentication_data_length);
curr += wrapper->authentication_data_length;
}
}
wrapper->service_data_len = (uint16_t)(real_len - curr);
memcpy(wrapper->service_data, &apdu[curr], wrapper->service_data_len);
curr += wrapper->service_data_len;
if (!key_verify_sign_msg(&key, &apdu[-bytes_before],
(uint32_t)(bytes_before + real_len), wrapper->signature)) {
return -SEC_RESP_BAD_SIGNATURE;
}
return curr;
}
int decode_challenge_request_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_CHALLENGE_REQUEST * bc_req)
{
int curr = 0;
if (apdu_len_remaining < 9) {
return -1;
}
bc_req->message_challenge = apdu[curr++];
curr += decode_unsigned32(&apdu[curr], &bc_req->orig_message_id);
curr += decode_unsigned32(&apdu[curr], &bc_req->orig_timestamp);
return curr; /* always 9! */
}
int decode_security_payload_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_SECURITY_PAYLOAD * payload)
{
if (apdu_len_remaining < 2) {
return -1;
}
decode_unsigned16(&apdu[0], &payload->payload_length);
if (apdu_len_remaining - 2 < payload->payload_length) {
return -1;
}
memcpy(payload->payload, &apdu[2], payload->payload_length);
return (int)(2 + payload->payload_length);
}
int decode_security_response_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_SECURITY_RESPONSE * resp)
{
int curr = 0;
int i;
if (apdu_len_remaining < 9) {
return -1;
}
resp->response_code = apdu[curr++];
curr += decode_unsigned32(&apdu[curr], &resp->orig_message_id);
curr += decode_unsigned32(&apdu[curr], &resp->orig_timestamp);
switch ((BACNET_SECURITY_RESPONSE_CODE) resp->response_code) {
case SEC_RESP_BAD_TIMESTAMP:
if (apdu_len_remaining < 13) {
return -1;
}
curr +=
decode_unsigned32(&apdu[curr],
&resp->response.bad_timestamp.expected_timestamp);
break;
case SEC_RESP_CANNOT_USE_KEY:
if (apdu_len_remaining < 11) {
return -1;
}
curr +=
decode_unsigned16(&apdu[curr],
&resp->response.cannot_use_key.key);
break;
case SEC_RESP_INCORRECT_KEY:
if (apdu_len_remaining < 10) {
return -1;
}
resp->response.incorrect_key.number_of_keys = apdu[curr++];
if (apdu_len_remaining - 10 <
resp->response.incorrect_key.number_of_keys * 2) {
return -1;
}
for (i = 0; i < (int)resp->response.incorrect_key.number_of_keys; i++) {
curr +=
decode_unsigned16(&apdu[curr],
&resp->response.incorrect_key.keys[i]);
}
break;
case SEC_RESP_UNKNOWN_AUTHENTICATION_TYPE:
if (apdu_len_remaining < 12) {
return -1;
}
resp->response.unknown_authentication_type.
original_authentication_type = apdu[curr++];
curr +=
decode_unsigned16(&apdu[curr],
&resp->response.unknown_authentication_type.vendor_id);
if (resp->response.unknown_authentication_type.
original_authentication_type < 200 &&
resp->response.unknown_authentication_type.vendor_id != 0) {
return -1;
}
break;
case SEC_RESP_UNKNOWN_KEY:
if (apdu_len_remaining < 11) {
return -1;
}
curr +=
decode_unsigned16(&apdu[curr],
&resp->response.unknown_key.original_key);
break;
case SEC_RESP_UNKNOWN_KEY_REVISION:
if (apdu_len_remaining < 10) {
return -1;
}
resp->response.unknown_key_revision.original_key_revision =
apdu[curr++];
break;
case SEC_RESP_TOO_MANY_KEYS:
if (apdu_len_remaining < 10) {
return -1;
}
resp->response.too_many_keys.max_num_of_keys = apdu[curr++];
break;
case SEC_RESP_INVALID_KEY_DATA:
if (apdu_len_remaining < 11) {
return -1;
}
curr +=
decode_unsigned16(&apdu[curr],
&resp->response.invalid_key_data.key);
break;
case SEC_RESP_SUCCESS:
case SEC_RESP_ACCESS_DENIED:
case SEC_RESP_BAD_DESTINATION_ADDRESS:
case SEC_RESP_BAD_DESTINATION_DEVICE_ID:
case SEC_RESP_BAD_SIGNATURE:
case SEC_RESP_BAD_SOURCE_ADDRESS:
case SEC_RESP_CANNOT_VERIFY_MESSAGE_ID:
case SEC_RESP_CORRECT_KEY_REVISION:
case SEC_RESP_DESTINATION_DEVICE_ID_REQUIRED:
case SEC_RESP_DUPLICATE_MESSAGE:
case SEC_RESP_ENCRYPTION_NOT_CONFIGURED:
case SEC_RESP_ENCRYPTION_REQUIRED:
case SEC_RESP_KEY_UPDATE_IN_PROGRESS:
case SEC_RESP_MALFORMED_MESSAGE:
case SEC_RESP_NOT_KEY_SERVER:
case SEC_RESP_SECURITY_NOT_CONFIGURED:
case SEC_RESP_SOURCE_SECURITY_REQUIRED:
case SEC_RESP_UNKNOWN_SOURCE_MESSAGE:
break;
default:
return -1; /* unknown message type */
}
return curr;
}
int decode_request_key_update_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_REQUEST_KEY_UPDATE * req)
{
int curr = 0;
if (apdu_len_remaining < 19) {
return -1;
}
req->set_1_key_revision = apdu[curr++];
curr += decode_unsigned32(&apdu[curr], &req->set_1_activation_time);
curr += decode_unsigned32(&apdu[curr], &req->set_1_expiration_time);
req->set_2_key_revision = apdu[curr++];
curr += decode_unsigned32(&apdu[curr], &req->set_2_activation_time);
curr += decode_unsigned32(&apdu[curr], &req->set_2_expiration_time);
req->distribution_key_revision = apdu[curr++];
return curr;
}
int decode_key_entry_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_KEY_ENTRY * entry)
{
int curr = 0;
if (apdu_len_remaining < 3) {
return -1;
}
curr += decode_unsigned16(&apdu[curr], &entry->key_identifier);
entry->key_len = apdu[curr++];
if (apdu_len_remaining - 3 < entry->key_len ||
entry->key_len > MAX_KEY_LEN) {
return -1;
}
memcpy(entry->key, &apdu[curr], entry->key_len);
curr += entry->key_len;
return curr;
}
int decode_update_key_set_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_UPDATE_KEY_SET * key_set)
{
int curr = 0;
int i, res;
if (apdu_len_remaining < 1) {
return -1;
}
if (apdu[curr] & 1) {
key_set->remove = true;
}
if ((apdu[curr] >> 1) & 1) {
key_set->more = true;
}
if ((apdu[curr] >> 2) & 1) {
key_set->set_clr[1] = true;
}
if ((apdu[curr] >> 3) & 1) {
key_set->set_ck[1] = true;
}
if ((apdu[curr] >> 4) & 1) {
key_set->set_rae[1] = true;
}
if ((apdu[curr] >> 5) & 1) {
key_set->set_clr[0] = true;
}
if ((apdu[curr] >> 6) & 1) {
key_set->set_ck[0] = true;
}
if ((apdu[curr] >> 7) & 1) {
key_set->set_rae[0] = true;
}
curr++;
if (key_set->set_rae[0]) {
if (apdu_len_remaining - curr < 9) {
return -1;
}
key_set->set_key_revision[0] = apdu[curr++];
curr +=
decode_unsigned32(&apdu[curr], &key_set->set_activation_time[0]);
curr +=
decode_unsigned32(&apdu[curr], &key_set->set_expiration_time[0]);
}
if (key_set->set_ck[0]) {
if (apdu_len_remaining - curr < 1) {
return -1;
}
key_set->set_key_count[0] = apdu[curr++];
if (key_set->set_key_count[0] > MAX_UPDATE_KEY_COUNT) {
return -1;
}
for (i = 0; i < (int)key_set->set_key_count[0]; i++) {
res =
decode_key_entry_safe(apdu + curr, apdu_len_remaining - curr,
&key_set->set_keys[0][i]);
if (res < 0) {
return -1;
}
curr += res;
}
}
if (key_set->set_rae[1]) {
if (apdu_len_remaining - curr < 9) {
return -1;
}
key_set->set_key_revision[1] = apdu[curr++];
curr +=
decode_unsigned32(&apdu[curr], &key_set->set_activation_time[1]);
curr +=
decode_unsigned32(&apdu[curr], &key_set->set_expiration_time[1]);
}
if (key_set->set_ck[1]) {
if (apdu_len_remaining - curr < 1) {
return -1;
}
key_set->set_key_count[1] = apdu[curr++];
if (key_set->set_key_count[1] > MAX_UPDATE_KEY_COUNT) {
return -1;
}
for (i = 0; i < (int)key_set->set_key_count[1]; i++) {
res =
decode_key_entry_safe(apdu + curr, apdu_len_remaining - curr,
&key_set->set_keys[1][i]);
if (res < 0) {
return -1;
}
curr += res;
}
}
return curr;
}
int decode_update_distribution_key_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_UPDATE_DISTRIBUTION_KEY * dist_key)
{
int curr = 0;
int res;
if (apdu_len_remaining < 1) {
return -1;
}
dist_key->key_revision = apdu[curr++];
res =
decode_key_entry_safe(&apdu[curr], apdu_len_remaining - curr,
&dist_key->key);
if (res < 0) {
return -1;
}
return curr + res;
}
int decode_request_master_key_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_REQUEST_MASTER_KEY * req_master_key)
{
uint32_t curr = 0;
if (apdu_len_remaining < 1) {
return -1;
}
req_master_key->no_supported_algorithms = apdu[curr++];
if (apdu_len_remaining < curr + req_master_key->no_supported_algorithms) {
return -1;
}
memcpy(req_master_key->es_algorithms, &apdu[curr],
req_master_key->no_supported_algorithms);
return (int)(curr + req_master_key->no_supported_algorithms);
}
int decode_set_master_key_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_SET_MASTER_KEY * set_master_key)
{
return decode_key_entry_safe(apdu, apdu_len_remaining,
&set_master_key->key);
}
+1122
View File
File diff suppressed because it is too large Load Diff
+2569
View File
File diff suppressed because it is too large Load Diff
+117
View File
@@ -0,0 +1,117 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2015 Nikola Jelic
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdbool.h>
#include <stdint.h>
#include "bacdcode.h"
#include "bactimevalue.h"
int bacapp_encode_time_value(uint8_t * apdu,
BACNET_TIME_VALUE * value)
{
int len;
int apdu_len = 0;
len = encode_application_time(&apdu[apdu_len], &value->Time);
apdu_len += len;
len = bacapp_encode_application_data(&apdu[apdu_len], &value->Value);
apdu_len += len;
return apdu_len;
}
int bacapp_encode_context_time_value(uint8_t * apdu,
uint8_t tag_number,
BACNET_TIME_VALUE * value)
{
int len;
int apdu_len = 0;
len = encode_opening_tag(&apdu[apdu_len], tag_number);
apdu_len += len;
len = bacapp_encode_time_value(&apdu[apdu_len], value);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], tag_number);
apdu_len += len;
return apdu_len;
}
int bacapp_decode_time_value(uint8_t * apdu,
BACNET_TIME_VALUE * value)
{
int len;
int apdu_len = 0;
len = decode_application_time(&apdu[apdu_len], &value->Time);
if (len <= 0)
return -1;
apdu_len += len;
len = bacapp_decode_application_data(&apdu[apdu_len], 2048, &value->Value);
if (len <= 0)
return -1;
apdu_len += len;
return apdu_len;
}
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;
}
+49
View File
@@ -0,0 +1,49 @@
/* Derived from "Unix Incompatibility Notes: Byte Order" by Jan Wolter */
/* http://unixpapa.com/incnote/byteorder.html */
/** @file bigend.c Determination of Endianess */
#include "bigend.h"
/* Big-Endian systems save the most significant byte first. */
/* Sun and Motorola processors, IBM-370s and PDP-10s are big-endian. */
/* "Network Byte Order" is also know as "Big-Endian Byte Order" */
/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */
/* x[0] = 0x04 */
/* x[1] = 0x03 */
/* x[2] = 0x02 */
/* x[3] = 0x01 */
/* Little-Endian systems save the least significant byte first. */
/* The entire Intel x86 family, Vaxes, Alphas and PDP-11s are little-endian. */
/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */
/* x[0] = 0x01 */
/* x[1] = 0x02 */
/* x[2] = 0x03 */
/* x[3] = 0x04 */
/* Note: Endianness doesn't apply to all variable manipulation.
If you use bitwise or bitshift operations on integers,
you can avoid having to check for endianness. */
/* The names are derived from Jonathon Swift's book Gulliver's Travels,
where they describe Lilliputian political parties who disagree
vehemently over which end to start eating an egg from.
This terminology was popularized for byte order by a less than
completely serious paper authored by Danny Cohen which appeared
on April 1, 1980 and was entitled "On Holy Wars and a Plea for Peace" */
/* function to return true on Big-Endian architectures */
/* (based on Harbison & Steele) */
int big_endian(
void)
{
union {
long l;
char c[sizeof(long)];
} u;
u.l = 1;
return (u.c[sizeof(long) - 1] == 1);
}
+399
View File
@@ -0,0 +1,399 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include "bacdcode.h"
#include "bacint.h"
#include "bip.h"
#include "bvlc.h"
#include "net.h" /* custom per port */
#if PRINT_ENABLED
#include <stdio.h> /* for standard i/o, like printing */
#endif
/** @file bip.c Configuration and Operations for BACnet/IP */
static int BIP_Socket = -1;
/* port to use - stored in network byte order */
static uint16_t BIP_Port = 0; /* this will force initialization in demos */
/* IP Address - stored in network byte order */
static struct in_addr BIP_Address;
/* Broadcast Address - stored in network byte order */
static struct in_addr BIP_Broadcast_Address;
/** Setter for the BACnet/IP socket handle.
*
* @param sock_fd [in] Handle for the BACnet/IP socket.
*/
void bip_set_socket(
int sock_fd)
{
BIP_Socket = sock_fd;
}
/** Getter for the BACnet/IP socket handle.
*
* @return The handle to the BACnet/IP socket.
*/
int bip_socket(
void)
{
return BIP_Socket;
}
bool bip_valid(
void)
{
return (BIP_Socket != -1);
}
void bip_set_addr(
uint32_t net_address)
{ /* in network byte order */
BIP_Address.s_addr = net_address;
}
/* returns network byte order */
uint32_t bip_get_addr(
void)
{
return BIP_Address.s_addr;
}
void bip_set_broadcast_addr(
uint32_t net_address)
{ /* in network byte order */
BIP_Broadcast_Address.s_addr = net_address;
}
/* returns network byte order */
uint32_t bip_get_broadcast_addr(
void)
{
return BIP_Broadcast_Address.s_addr;
}
void bip_set_port(
uint16_t port)
{ /* in network byte order */
BIP_Port = port;
}
/* returns network byte order */
uint16_t bip_get_port(
void)
{
return BIP_Port;
}
static int bip_decode_bip_address(
BACNET_ADDRESS * bac_addr,
struct in_addr *address, /* in network format */
uint16_t * port)
{ /* in network format */
int len = 0;
if (bac_addr) {
memcpy(&address->s_addr, &bac_addr->mac[0], 4);
memcpy(port, &bac_addr->mac[4], 2);
len = 6;
}
return len;
}
/** Function to send a packet out the BACnet/IP socket (Annex J).
* @ingroup DLBIP
*
* @param dest [in] Destination address (may encode an IP address and port #).
* @param npdu_data [in] The NPDU header (Network) information (not used).
* @param pdu [in] Buffer of data to be sent - may be null (why?).
* @param pdu_len [in] Number of bytes in the pdu buffer.
* @return Number of bytes sent on success, negative number on failure.
*/
int bip_send_pdu(
BACNET_ADDRESS * dest, /* destination address */
BACNET_NPDU_DATA * npdu_data, /* network information */
uint8_t * pdu, /* any data to be sent - may be null */
unsigned pdu_len)
{ /* number of bytes of data */
struct sockaddr_in bip_dest;
uint8_t mtu[MAX_MPDU] = { 0 };
int mtu_len = 0;
int bytes_sent = 0;
/* addr and port in host format */
struct in_addr address;
uint16_t port = 0;
(void) npdu_data;
/* assumes that the driver has already been initialized */
if (BIP_Socket < 0) {
return BIP_Socket;
}
mtu[0] = BVLL_TYPE_BACNET_IP;
bip_dest.sin_family = AF_INET;
if ((dest->net == BACNET_BROADCAST_NETWORK) || (dest->mac_len == 0)) {
/* broadcast */
address.s_addr = BIP_Broadcast_Address.s_addr;
port = BIP_Port;
mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
} else if ((dest->net > 0) && (dest->len == 0)) {
/* network specific broadcast */
if (dest->mac_len == 6) {
bip_decode_bip_address(dest, &address, &port);
} else {
address.s_addr = BIP_Broadcast_Address.s_addr;
port = BIP_Port;
}
mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
} else if (dest->mac_len == 6) {
bip_decode_bip_address(dest, &address, &port);
mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU;
} else {
/* invalid address */
return -1;
}
bip_dest.sin_addr.s_addr = address.s_addr;
bip_dest.sin_port = port;
memset(&(bip_dest.sin_zero), '\0', 8);
mtu_len = 2;
mtu_len +=
encode_unsigned16(&mtu[mtu_len],
(uint16_t) (pdu_len + 4 /*inclusive */ ));
memcpy(&mtu[mtu_len], pdu, pdu_len);
mtu_len += pdu_len;
/* Send the packet */
bytes_sent =
sendto(BIP_Socket, (char *) mtu, mtu_len, 0,
(struct sockaddr *) &bip_dest, sizeof(struct sockaddr));
return bytes_sent;
}
/** Implementation of the receive() function for BACnet/IP; receives one
* packet, verifies its BVLC header, and removes the BVLC header from
* the PDU data before returning.
*
* @param src [out] Source of the packet - who should receive any response.
* @param pdu [out] A buffer to hold the PDU portion of the received packet,
* after the BVLC portion has been stripped off.
* @param max_pdu [in] Size of the pdu[] buffer.
* @param timeout [in] The number of milliseconds to wait for a packet.
* @return The number of octets (remaining) in the PDU, or zero on failure.
*/
uint16_t bip_receive(
BACNET_ADDRESS * src, /* source address */
uint8_t * pdu, /* PDU data */
uint16_t max_pdu, /* amount of space available in the PDU */
unsigned timeout)
{
int received_bytes = 0;
uint16_t pdu_len = 0; /* return value */
fd_set read_fds;
int max = 0;
struct timeval select_timeout;
struct sockaddr_in sin = { 0 };
socklen_t sin_len = sizeof(sin);
uint16_t i = 0;
int function = 0;
/* Make sure the socket is open */
if (BIP_Socket < 0)
return 0;
/* we could just use a non-blocking socket, but that consumes all
the CPU time. We can use a timeout; it is only supported as
a select. */
if (timeout >= 1000) {
select_timeout.tv_sec = timeout / 1000;
select_timeout.tv_usec =
1000 * (timeout - select_timeout.tv_sec * 1000);
} else {
select_timeout.tv_sec = 0;
select_timeout.tv_usec = 1000 * timeout;
}
FD_ZERO(&read_fds);
FD_SET(BIP_Socket, &read_fds);
max = BIP_Socket;
/* see if there is a packet for us */
if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0)
received_bytes =
recvfrom(BIP_Socket, (char *) &pdu[0], max_pdu, 0,
(struct sockaddr *) &sin, &sin_len);
else
return 0;
/* See if there is a problem */
if (received_bytes < 0) {
return 0;
}
/* no problem, just no bytes */
if (received_bytes == 0)
return 0;
/* the signature of a BACnet/IP packet */
if (pdu[0] != BVLL_TYPE_BACNET_IP)
return 0;
if (bvlc_for_non_bbmd(&sin, pdu, received_bytes) > 0) {
/* Handled, usually with a NACK. */
#if PRINT_ENABLED
fprintf(stderr, "BIP: BVLC discarded!\n");
#endif
return 0;
}
function = bvlc_get_function_code(); /* aka, pdu[1] */
if ((function == BVLC_ORIGINAL_UNICAST_NPDU) ||
(function == BVLC_ORIGINAL_BROADCAST_NPDU)) {
/* ignore messages from me */
if ((sin.sin_addr.s_addr == BIP_Address.s_addr) &&
(sin.sin_port == BIP_Port)) {
pdu_len = 0;
#if 0
fprintf(stderr, "BIP: src is me. Discarded!\n");
#endif
} else {
/* data in src->mac[] is in network format */
src->mac_len = 6;
memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4);
memcpy(&src->mac[4], &sin.sin_port, 2);
/* FIXME: check destination address */
/* see if it is broadcast or for us */
/* decode the length of the PDU - length is inclusive of BVLC */
(void) decode_unsigned16(&pdu[2], &pdu_len);
/* subtract off the BVLC header */
pdu_len -= 4;
if (pdu_len < max_pdu) {
#if 0
fprintf(stderr, "BIP: NPDU[%hu]:", pdu_len);
#endif
/* shift the buffer to return a valid PDU */
for (i = 0; i < pdu_len; i++) {
pdu[i] = pdu[4 + i];
#if 0
fprintf(stderr, "%02X ", pdu[i]);
#endif
}
#if 0
fprintf(stderr, "\n");
#endif
}
/* ignore packets that are too large */
/* clients should check my max-apdu first */
else {
pdu_len = 0;
#if PRINT_ENABLED
fprintf(stderr, "BIP: PDU too large. Discarded!.\n");
#endif
}
}
} else if (function == BVLC_FORWARDED_NPDU) {
memcpy(&sin.sin_addr.s_addr, &pdu[4], 4);
memcpy(&sin.sin_port, &pdu[8], 2);
if ((sin.sin_addr.s_addr == BIP_Address.s_addr) &&
(sin.sin_port == BIP_Port)) {
/* ignore messages from me */
pdu_len = 0;
} else {
/* data in src->mac[] is in network format */
src->mac_len = 6;
memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4);
memcpy(&src->mac[4], &sin.sin_port, 2);
/* FIXME: check destination address */
/* see if it is broadcast or for us */
/* decode the length of the PDU - length is inclusive of BVLC */
(void) decode_unsigned16(&pdu[2], &pdu_len);
/* subtract off the BVLC header */
pdu_len -= 10;
if (pdu_len < max_pdu) {
/* shift the buffer to return a valid PDU */
for (i = 0; i < pdu_len; i++) {
pdu[i] = pdu[4 + 6 + i];
}
} else {
/* ignore packets that are too large */
/* clients should check my max-apdu first */
pdu_len = 0;
}
}
}
return pdu_len;
}
void bip_get_my_address(
BACNET_ADDRESS * my_address)
{
int i = 0;
if (my_address) {
my_address->mac_len = 6;
memcpy(&my_address->mac[0], &BIP_Address.s_addr, 4);
memcpy(&my_address->mac[4], &BIP_Port, 2);
my_address->net = 0; /* local only, no routing */
my_address->len = 0; /* no SLEN */
for (i = 0; i < MAX_MAC_LEN; i++) {
/* no SADR */
my_address->adr[i] = 0;
}
}
return;
}
void bip_get_broadcast_address(
BACNET_ADDRESS * dest)
{ /* destination address */
int i = 0; /* counter */
if (dest) {
dest->mac_len = 6;
memcpy(&dest->mac[0], &BIP_Broadcast_Address.s_addr, 4);
memcpy(&dest->mac[4], &BIP_Port, 2);
dest->net = BACNET_BROADCAST_NETWORK;
dest->len = 0; /* no SLEN */
for (i = 0; i < MAX_MAC_LEN; i++) {
/* no SADR */
dest->adr[i] = 0;
}
}
return;
}
+1924
View File
File diff suppressed because it is too large Load Diff
+2337
View File
File diff suppressed because it is too large Load Diff
+1104
View File
File diff suppressed because it is too large Load Diff
+302
View File
@@ -0,0 +1,302 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2004 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307
USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "crc.h"
/** @file crc.c Calculate CRCs */
#if defined(CRC_USE_TABLE)
/* note: table is created using unit test below */
static const uint8_t HeaderCRC[256] = {
0x00, 0xfe, 0xff, 0x01, 0xfd, 0x03, 0x02, 0xfc,
0xf9, 0x07, 0x06, 0xf8, 0x04, 0xfa, 0xfb, 0x05,
0xf1, 0x0f, 0x0e, 0xf0, 0x0c, 0xf2, 0xf3, 0x0d,
0x08, 0xf6, 0xf7, 0x09, 0xf5, 0x0b, 0x0a, 0xf4,
0xe1, 0x1f, 0x1e, 0xe0, 0x1c, 0xe2, 0xe3, 0x1d,
0x18, 0xe6, 0xe7, 0x19, 0xe5, 0x1b, 0x1a, 0xe4,
0x10, 0xee, 0xef, 0x11, 0xed, 0x13, 0x12, 0xec,
0xe9, 0x17, 0x16, 0xe8, 0x14, 0xea, 0xeb, 0x15,
0xc1, 0x3f, 0x3e, 0xc0, 0x3c, 0xc2, 0xc3, 0x3d,
0x38, 0xc6, 0xc7, 0x39, 0xc5, 0x3b, 0x3a, 0xc4,
0x30, 0xce, 0xcf, 0x31, 0xcd, 0x33, 0x32, 0xcc,
0xc9, 0x37, 0x36, 0xc8, 0x34, 0xca, 0xcb, 0x35,
0x20, 0xde, 0xdf, 0x21, 0xdd, 0x23, 0x22, 0xdc,
0xd9, 0x27, 0x26, 0xd8, 0x24, 0xda, 0xdb, 0x25,
0xd1, 0x2f, 0x2e, 0xd0, 0x2c, 0xd2, 0xd3, 0x2d,
0x28, 0xd6, 0xd7, 0x29, 0xd5, 0x2b, 0x2a, 0xd4,
0x81, 0x7f, 0x7e, 0x80, 0x7c, 0x82, 0x83, 0x7d,
0x78, 0x86, 0x87, 0x79, 0x85, 0x7b, 0x7a, 0x84,
0x70, 0x8e, 0x8f, 0x71, 0x8d, 0x73, 0x72, 0x8c,
0x89, 0x77, 0x76, 0x88, 0x74, 0x8a, 0x8b, 0x75,
0x60, 0x9e, 0x9f, 0x61, 0x9d, 0x63, 0x62, 0x9c,
0x99, 0x67, 0x66, 0x98, 0x64, 0x9a, 0x9b, 0x65,
0x91, 0x6f, 0x6e, 0x90, 0x6c, 0x92, 0x93, 0x6d,
0x68, 0x96, 0x97, 0x69, 0x95, 0x6b, 0x6a, 0x94,
0x40, 0xbe, 0xbf, 0x41, 0xbd, 0x43, 0x42, 0xbc,
0xb9, 0x47, 0x46, 0xb8, 0x44, 0xba, 0xbb, 0x45,
0xb1, 0x4f, 0x4e, 0xb0, 0x4c, 0xb2, 0xb3, 0x4d,
0x48, 0xb6, 0xb7, 0x49, 0xb5, 0x4b, 0x4a, 0xb4,
0xa1, 0x5f, 0x5e, 0xa0, 0x5c, 0xa2, 0xa3, 0x5d,
0x58, 0xa6, 0xa7, 0x59, 0xa5, 0x5b, 0x5a, 0xa4,
0x50, 0xae, 0xaf, 0x51, 0xad, 0x53, 0x52, 0xac,
0xa9, 0x57, 0x56, 0xa8, 0x54, 0xaa, 0xab, 0x55
};
uint8_t CRC_Calc_Header(
uint8_t dataValue,
uint8_t crcValue)
{
return HeaderCRC[crcValue ^ dataValue];
}
/* note: table is created using unit test below */
static const uint16_t DataCRC[256] = {
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
uint16_t CRC_Calc_Data(
uint8_t dataValue,
uint16_t crcValue)
{
return ((crcValue >> 8) ^ DataCRC[(crcValue & 0x00FF) ^ dataValue]);
}
#else
/* Accumulate "dataValue" into the CRC in crcValue. */
/* Return value is updated CRC */
/* */
/* The ^ operator means exclusive OR. */
/* Note: This function is copied directly from the BACnet standard. */
uint8_t CRC_Calc_Header(
uint8_t dataValue,
uint8_t crcValue)
{
uint16_t crc;
crc = crcValue ^ dataValue; /* XOR C7..C0 with D7..D0 */
/* Exclusive OR the terms in the table (top down) */
crc = crc ^ (crc << 1) ^ (crc << 2) ^ (crc << 3)
^ (crc << 4) ^ (crc << 5) ^ (crc << 6)
^ (crc << 7);
/* Combine bits shifted out left hand end */
return (crc & 0xfe) ^ ((crc >> 8) & 1);
}
/* Accumulate "dataValue" into the CRC in crcValue. */
/* Return value is updated CRC */
/* */
/* The ^ operator means exclusive OR. */
/* Note: This function is copied directly from the BACnet standard. */
uint16_t CRC_Calc_Data(
uint8_t dataValue,
uint16_t crcValue)
{
uint16_t crcLow;
crcLow = (crcValue & 0xff) ^ dataValue; /* XOR C7..C0 with D7..D0 */
/* Exclusive OR the terms in the table (top down) */
return (crcValue >> 8) ^ (crcLow << 8) ^ (crcLow << 3)
^ (crcLow << 12) ^ (crcLow >> 4)
^ (crcLow & 0x0f) ^ ((crcLow & 0x0f) << 7);
}
#endif
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
#include "bytes.h"
/* test from Annex G 1.0 of BACnet Standard */
void testCRC8(
Test * pTest)
{
uint8_t crc = 0xff; /* accumulates the crc value */
uint8_t frame_crc; /* appended to the end of the frame */
crc = CRC_Calc_Header(0x00, crc);
ct_test(pTest, crc == 0x55);
crc = CRC_Calc_Header(0x10, crc);
ct_test(pTest, crc == 0xC2);
crc = CRC_Calc_Header(0x05, crc);
ct_test(pTest, crc == 0xBC);
crc = CRC_Calc_Header(0x00, crc);
ct_test(pTest, crc == 0x95);
crc = CRC_Calc_Header(0x00, crc);
ct_test(pTest, crc == 0x73);
/* send the ones complement of the CRC in place of */
/* the CRC, and the resulting CRC will always equal 0x55. */
frame_crc = ~crc;
ct_test(pTest, frame_crc == 0x8C);
/* use the ones complement value and the next to last CRC value */
crc = CRC_Calc_Header(frame_crc, crc);
ct_test(pTest, crc == 0x55);
}
/* test from Annex G 2.0 of BACnet Standard */
void testCRC16(
Test * pTest)
{
uint16_t crc = 0xffff;
uint16_t data_crc;
crc = CRC_Calc_Data(0x01, crc);
ct_test(pTest, crc == 0x1E0E);
crc = CRC_Calc_Data(0x22, crc);
ct_test(pTest, crc == 0xEB70);
crc = CRC_Calc_Data(0x30, crc);
ct_test(pTest, crc == 0x42EF);
/* send the ones complement of the CRC in place of */
/* the CRC, and the resulting CRC will always equal 0xF0B8. */
data_crc = ~crc;
ct_test(pTest, data_crc == 0xBD10);
crc = CRC_Calc_Data(LO_BYTE(data_crc), crc);
ct_test(pTest, crc == 0x0F3A);
crc = CRC_Calc_Data(HI_BYTE(data_crc), crc);
ct_test(pTest, crc == 0xF0B8);
}
void testCRC8CreateTable(
Test * pTest)
{
uint8_t crc = 0xff; /* accumulates the crc value */
int i;
(void) pTest;
printf("static const uint8_t HeaderCRC[256] =\n");
printf("{\n");
printf(" ");
for (i = 0; i < 256; i++) {
crc = CRC_Calc_Header(i, 0);
printf("0x%02x, ", crc);
if (!((i + 1) % 8)) {
printf("\n");
if (i != 255) {
printf(" ");
}
}
}
printf("};\n");
}
void testCRC16CreateTable(
Test * pTest)
{
uint16_t crc;
int i;
(void) pTest;
printf("static const uint16_t DataCRC[256] =\n");
printf("{\n");
printf(" ");
for (i = 0; i < 256; i++) {
crc = CRC_Calc_Data(i, 0);
printf("0x%04x, ", crc);
if (!((i + 1) % 8)) {
printf("\n");
if (i != 255) {
printf(" ");
}
}
}
printf("};\n");
}
#endif
#ifdef TEST_CRC
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("crc", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testCRC8);
assert(rc);
rc = ct_addTestFunction(pTest, testCRC16);
assert(rc);
rc = ct_addTestFunction(pTest, testCRC8CreateTable);
assert(rc);
rc = ct_addTestFunction(pTest, testCRC16CreateTable);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
+132
View File
@@ -0,0 +1,132 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include "credential_authentication_factor.h"
#include "bacdcode.h"
int bacapp_encode_credential_authentication_factor(
uint8_t * apdu,
BACNET_CREDENTIAL_AUTHENTICATION_FACTOR * caf)
{
int len;
int apdu_len = 0;
len = encode_context_enumerated(&apdu[apdu_len], 0, caf->disable);
if (len < 0)
return -1;
else
apdu_len += len;
len =
bacapp_encode_context_authentication_factor(&apdu[apdu_len], 1,
&caf->authentication_factor);
if (len < 0)
return -1;
else
apdu_len += len;
return apdu_len;
}
int bacapp_encode_context_credential_authentication_factor(
uint8_t * apdu,
uint8_t tag,
BACNET_CREDENTIAL_AUTHENTICATION_FACTOR * caf)
{
int len;
int apdu_len = 0;
len = encode_opening_tag(&apdu[apdu_len], tag);
apdu_len += len;
len = bacapp_encode_credential_authentication_factor(&apdu[apdu_len], caf);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], tag);
apdu_len += len;
return apdu_len;
}
int bacapp_decode_credential_authentication_factor(
uint8_t * apdu,
BACNET_CREDENTIAL_AUTHENTICATION_FACTOR * caf)
{
int len;
int apdu_len = 0;
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
len = decode_context_enumerated(&apdu[apdu_len], 0, &caf->disable);
if (len < 0)
return -1;
else
apdu_len += len;
} else
return -1;
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
len =
bacapp_decode_context_authentication_factor(&apdu[apdu_len], 1,
&caf->authentication_factor);
if (len < 0)
return -1;
else
apdu_len += len;
} else
return -1;
return apdu_len;
}
int bacapp_decode_context_credential_authentication_factor(
uint8_t * apdu,
uint8_t tag,
BACNET_CREDENTIAL_AUTHENTICATION_FACTOR * caf)
{
int len = 0;
int section_length;
if (decode_is_opening_tag_number(&apdu[len], tag)) {
len++;
section_length =
bacapp_decode_credential_authentication_factor(&apdu[len], caf);
if (section_length == -1) {
len = -1;
} else {
len += section_length;
if (decode_is_closing_tag_number(&apdu[len], tag)) {
len++;
} else {
len = -1;
}
}
} else {
len = -1;
}
return len;
}
+192
View File
@@ -0,0 +1,192 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2007 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
/** @file datalink.c Optional run-time assignment of datalink transport */
#include "datalink.h"
#if defined(BACDL_ALL) || defined FOR_DOXYGEN
#include "ethernet.h"
#include "bip.h"
#include "bip6.h"
#include "bvlc.h"
#include "arcnet.h"
#include "dlmstp.h"
#include <string.h>
/* Function pointers - point to your datalink */
/** Function template to Initialize the DataLink services at the given interface.
* @ingroup DLTemplates
*
* @note For Linux, ifname is eth0, ath0, arc0, ttyS0, and others.
For Windows, ifname is the COM port or dotted ip address of the interface.
* @param ifname [in] The named interface to use for the network layer.
* @return True if the interface is successfully initialized,
* else False if the initialization fails.
*/
bool(*datalink_init) (char *ifname);
/** Function template to send a packet via the DataLink.
* @ingroup DLTemplates
*
* @param dest [in] Destination address.
* @param npdu_data [in] The NPDU header (Network) information.
* @param pdu [in] Buffer of data to be sent - may be null.
* @param pdu_len [in] Number of bytes in the pdu buffer.
* @return Number of bytes sent on success, negative number on failure.
*/
int (
*datalink_send_pdu) (
BACNET_ADDRESS * dest,
BACNET_NPDU_DATA * npdu_data,
uint8_t * pdu,
unsigned pdu_len);
uint16_t(*datalink_receive) (BACNET_ADDRESS * src, uint8_t * pdu,
uint16_t max_pdu, unsigned timeout);
/** Function template to close the DataLink services and perform any cleanup.
* @ingroup DLTemplates
*/
void (
*datalink_cleanup) (
void);
void (
*datalink_get_broadcast_address) (
BACNET_ADDRESS * dest);
void (
*datalink_get_my_address) (
BACNET_ADDRESS * my_address);
void datalink_set(
char *datalink_string)
{
if (strcasecmp("bip", datalink_string) == 0) {
datalink_init = bip_init;
datalink_send_pdu = bip_send_pdu;
datalink_receive = bip_receive;
datalink_cleanup = bip_cleanup;
datalink_get_broadcast_address = bip_get_broadcast_address;
datalink_get_my_address = bip_get_my_address;
} else if (strcasecmp("bvlc", datalink_string) == 0) {
datalink_init = bip_init;
datalink_send_pdu = bvlc_send_pdu;
datalink_receive = bvlc_receive;
datalink_cleanup = bip_cleanup;
datalink_get_broadcast_address = bip_get_broadcast_address;
datalink_get_my_address = bip_get_my_address;
} else if (strcasecmp("bip6", datalink_string) == 0) {
datalink_init = bip6_init;
datalink_send_pdu = bip6_send_pdu;
datalink_receive = bip6_receive;
datalink_cleanup = bip6_cleanup;
datalink_get_broadcast_address = bip6_get_broadcast_address;
datalink_get_my_address = bip6_get_my_address;
} else if (strcasecmp("bvlc6", datalink_string) == 0) {
datalink_init = bip6_init;
datalink_send_pdu = bvlc6_send_pdu;
datalink_receive = bvlc6_receive;
datalink_cleanup = bip6_cleanup;
datalink_get_broadcast_address = bip6_get_broadcast_address;
datalink_get_my_address = bip6_get_my_address;
} else if (strcasecmp("ethernet", datalink_string) == 0) {
datalink_init = ethernet_init;
datalink_send_pdu = ethernet_send_pdu;
datalink_receive = ethernet_receive;
datalink_cleanup = ethernet_cleanup;
datalink_get_broadcast_address = ethernet_get_broadcast_address;
datalink_get_my_address = ethernet_get_my_address;
} else if (strcasecmp("arcnet", datalink_string) == 0) {
datalink_init = arcnet_init;
datalink_send_pdu = arcnet_send_pdu;
datalink_receive = arcnet_receive;
datalink_cleanup = arcnet_cleanup;
datalink_get_broadcast_address = arcnet_get_broadcast_address;
datalink_get_my_address = arcnet_get_my_address;
} else if (strcasecmp("mstp", datalink_string) == 0) {
datalink_init = dlmstp_init;
datalink_send_pdu = dlmstp_send_pdu;
datalink_receive = dlmstp_receive;
datalink_cleanup = dlmstp_cleanup;
datalink_get_broadcast_address = dlmstp_get_broadcast_address;
datalink_get_my_address = dlmstp_get_my_address;
}
}
#endif
#if defined(BACDL_NONE)
int datalink_send_pdu(
BACNET_ADDRESS * dest,
BACNET_NPDU_DATA * npdu_data,
uint8_t * pdu,
unsigned pdu_len)
{
return 0;
}
uint16_t datalink_receive(
BACNET_ADDRESS * src,
uint8_t * pdu,
uint16_t max_pdu,
unsigned timeout)
{
return 0;
}
void datalink_cleanup(
void)
{
}
void datalink_get_broadcast_address(
BACNET_ADDRESS * dest)
{
}
void datalink_get_my_address(
BACNET_ADDRESS * my_address)
{
}
void datalink_set_interface(
char *ifname)
{
}
void datalink_set(
char *datalink_string)
{
}
#endif
+1514
View File
File diff suppressed because it is too large Load Diff
+339
View File
@@ -0,0 +1,339 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2006 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "dcc.h"
/** @file dcc.c Enable/Disable Device Communication Control (DCC) */
/* note: the disable and time are not expected to survive
over a power cycle or reinitialization. */
/* note: time duration is given in Minutes, but in order to be accurate,
we need to count down in seconds. */
/* infinite time duration is defined as 0 */
static uint32_t DCC_Time_Duration_Seconds = 0;
static BACNET_COMMUNICATION_ENABLE_DISABLE DCC_Enable_Disable =
COMMUNICATION_ENABLE;
/* password is optionally supported */
BACNET_COMMUNICATION_ENABLE_DISABLE dcc_enable_status(
void)
{
return DCC_Enable_Disable;
}
bool dcc_communication_enabled(
void)
{
return (DCC_Enable_Disable == COMMUNICATION_ENABLE);
}
/* When network communications are completely disabled,
only DeviceCommunicationControl and ReinitializeDevice APDUs
shall be processed and no messages shall be initiated.*/
bool dcc_communication_disabled(
void)
{
return (DCC_Enable_Disable == COMMUNICATION_DISABLE);
}
/* When the initiation of communications is disabled,
all APDUs shall be processed and responses returned as
required and no messages shall be initiated with the
exception of I-Am requests, which shall be initiated only in
response to Who-Is messages. In this state, a device that
supports I-Am request initiation shall send one I-Am request
for any Who-Is request that is received if and only if
the Who-Is request does not contain an address range or
the device is included in the address range. */
bool dcc_communication_initiation_disabled(
void)
{
return (DCC_Enable_Disable == COMMUNICATION_DISABLE_INITIATION);
}
/* note: 0 indicates either expired, or infinite duration */
uint32_t dcc_duration_seconds(
void)
{
return DCC_Time_Duration_Seconds;
}
/* called every second or so. If more than one second,
then seconds should be the number of seconds to tick away */
void dcc_timer_seconds(
uint32_t seconds)
{
if (DCC_Time_Duration_Seconds) {
if (DCC_Time_Duration_Seconds > seconds)
DCC_Time_Duration_Seconds -= seconds;
else
DCC_Time_Duration_Seconds = 0;
/* just expired - do something */
if (DCC_Time_Duration_Seconds == 0)
DCC_Enable_Disable = COMMUNICATION_ENABLE;
}
}
bool dcc_set_status_duration(
BACNET_COMMUNICATION_ENABLE_DISABLE status,
uint16_t minutes)
{
bool valid = false;
/* valid? */
if (status < MAX_BACNET_COMMUNICATION_ENABLE_DISABLE) {
DCC_Enable_Disable = status;
if (status == COMMUNICATION_ENABLE) {
DCC_Time_Duration_Seconds = 0;
} else {
DCC_Time_Duration_Seconds = minutes * 60;
}
valid = true;
}
return valid;
}
#if BACNET_SVC_DCC_A
/* encode service */
int dcc_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
uint16_t timeDuration, /* 0=optional */
BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable,
BACNET_CHARACTER_STRING * password)
{ /* NULL=optional */
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL;
apdu_len = 4;
/* optional timeDuration */
if (timeDuration) {
len = encode_context_unsigned(&apdu[apdu_len], 0, timeDuration);
apdu_len += len;
}
/* enable disable */
len = encode_context_enumerated(&apdu[apdu_len], 1, enable_disable);
apdu_len += len;
/* optional password */
if (password) {
/* FIXME: must be at least 1 character, limited to 20 characters */
len =
encode_context_character_string(&apdu[apdu_len], 2, password);
apdu_len += len;
}
}
return apdu_len;
}
#endif
/* decode the service request only */
int dcc_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
uint16_t * timeDuration,
BACNET_COMMUNICATION_ENABLE_DISABLE * enable_disable,
BACNET_CHARACTER_STRING * password)
{
unsigned len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint32_t value32 = 0;
/* check for value pointers */
if (apdu_len) {
/* Tag 0: timeDuration, in minutes --optional--
* But if not included, take it as indefinite,
* which we return as "very large" */
if (decode_is_context_tag(&apdu[len], 0)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += decode_unsigned(&apdu[len], len_value_type, &value32);
if (timeDuration) {
*timeDuration = (uint16_t) value32;
}
} else if (timeDuration) {
/* zero indicates infinite duration and
results in no timeout */
*timeDuration = 0;
}
/* Tag 1: enable_disable */
if (!decode_is_context_tag(&apdu[len], 1)) {
return -1;
}
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += decode_enumerated(&apdu[len], len_value_type, &value32);
if (enable_disable) {
*enable_disable = (BACNET_COMMUNICATION_ENABLE_DISABLE) value32;
}
/* Tag 2: password --optional-- */
if (len < apdu_len) {
if (!decode_is_context_tag(&apdu[len], 2)) {
return -1;
}
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len +=
decode_character_string(&apdu[len], len_value_type, password);
} else if (password) {
characterstring_init_ansi(password, NULL);
}
}
return (int) len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
int dcc_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
uint16_t * timeDuration,
BACNET_COMMUNICATION_ENABLE_DISABLE * enable_disable,
BACNET_CHARACTER_STRING * password)
{
int len = 0;
unsigned offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST)
return -1;
/* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL)
return -1;
offset = 4;
if (apdu_len > offset) {
len =
dcc_decode_service_request(&apdu[offset], apdu_len - offset,
timeDuration, enable_disable, password);
}
return len;
}
void test_DeviceCommunicationControlData(
Test * pTest,
uint8_t invoke_id,
uint16_t timeDuration,
BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable,
BACNET_CHARACTER_STRING * password)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t test_invoke_id = 0;
uint16_t test_timeDuration = 0;
BACNET_COMMUNICATION_ENABLE_DISABLE test_enable_disable;
BACNET_CHARACTER_STRING test_password;
len =
dcc_encode_apdu(&apdu[0], invoke_id, timeDuration, enable_disable,
password);
ct_test(pTest, len != 0);
apdu_len = len;
len =
dcc_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_timeDuration, &test_enable_disable, &test_password);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, test_timeDuration == timeDuration);
ct_test(pTest, test_enable_disable == enable_disable);
ct_test(pTest, characterstring_same(&test_password, password));
}
void test_DeviceCommunicationControl(
Test * pTest)
{
uint8_t invoke_id = 128;
uint16_t timeDuration = 0;
BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable;
BACNET_CHARACTER_STRING password;
timeDuration = 0;
enable_disable = COMMUNICATION_DISABLE_INITIATION;
characterstring_init_ansi(&password, "John 3:16");
test_DeviceCommunicationControlData(pTest, invoke_id, timeDuration,
enable_disable, &password);
timeDuration = 12345;
enable_disable = COMMUNICATION_DISABLE;
test_DeviceCommunicationControlData(pTest, invoke_id, timeDuration,
enable_disable, NULL);
return;
}
#ifdef TEST_DEVICE_COMMUNICATION_CONTROL
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet DeviceCommunicationControl", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, test_DeviceCommunicationControl);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_DEVICE_COMMUNICATION_CONTROL */
#endif /* TEST */
+65
View File
@@ -0,0 +1,65 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2008 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include <stdio.h> /* Standard I/O */
#include <stdlib.h> /* Standard Library */
#include <stdarg.h>
#include "debug.h"
/** @file debug.c Debug print function */
#if DEBUG_ENABLED
void debug_printf(
const char *format,
...)
{
va_list ap;
va_start(ap, format);
vfprintf(stdout, format, ap);
va_end(ap);
fflush(stdout);
return;
}
#else
void debug_printf(
const char *format,
...)
{
format = format;
}
#endif
+1522
View File
File diff suppressed because it is too large Load Diff
Executable
+502
View File
@@ -0,0 +1,502 @@
/**
* @file
* @author Steve Karg
* @date 2004
* @brief Generic interrupt safe FIFO library for deeply embedded system.
*
* @section LICENSE
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to:
* The Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*
* As a special exception, if other files instantiate templates or
* use macros or inline functions from this file, or you compile
* this file and link it with other works to produce a work based
* on this file, this file does not by itself cause the resulting
* work to be covered by the GNU General Public License. However
* the source code for this file must still be made available in
* accordance with section (3) of the GNU General Public License.
*
* This exception does not invalidate any other reasons why a work
* based on this file might be covered by the GNU General Public
* License.
*
* @section DESCRIPTION
*
* Generic interrupt safe FIFO library for deeply embedded system
* This library only uses a byte sized chunk for a data element.
* It uses a data store whose size is a power of 2 (8, 16, 32, 64, ...)
* and doesn't waste any data bytes. It has very low overhead, and
* utilizes modulo for indexing the data in the data store.
*
* To use this library, first declare a data store, sized for a power of 2:
* {@code
* static volatile uint8_t data_store[64];
* }
*
* Then declare the FIFO tracking structure:
* {@code
* static FIFO_BUFFER queue;
* }
*
* Initialize the queue with the data store:
* {@code
* FIFO_Init(&queue, data_store, sizeof(data_store));
* }
*
* Then begin to use the FIFO queue by giving it data, retreiving data,
* and checking the FIFO queue to see if it is empty or full:
* {@code
* uint8_t in_data = 0;
* uint8_t out_data = 0;
* uint8_t add_data[5] = {0};
* uint8_t pull_data[5] = {0};
* unsigned count = 0;
* bool status = false;
*
* status = FIFO_Put(&queue, in_data);
* if (!FIFO_Empty(&queue)) {
* out_data = FIFO_Get(&queue);
* }
* if (FIFO_Available(&queue, sizeof(add_data))) {
* status = FIFO_Add(&queue, add_data, sizeof(add_data));
* }
* count = FIFO_Count(&queue);
* if (count == sizeof(add_data)) {
* count = FIFO_Pull(&queue, &pull_data[0], sizeof(pull_data));
* }
*
* }
*
* Normally the FIFO is used by a producer, such as in interrupt service
* routine, which places data into the queue using FIFO_Put(), and a consumer,
* such as a main loop handler, which pulls data from the queue by first
* checking the queue for data using FIFO_Empty(), and then pulling data from
* the queue using FIFO_Get().
*
*/
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "fifo.h"
/**
* Returns the number of bytes in the FIFO
*
* @param b - pointer to FIFO_BUFFER structure
*
* @return Number of bytes in the FIFO
*/
unsigned FIFO_Count(
FIFO_BUFFER const *b)
{
unsigned head, tail; /* used to avoid volatile decision */
if (b) {
head = b->head;
tail = b->tail;
return head - tail;
} else {
return 0;
}
}
/**
* Returns the full status of the FIFO
*
* @param b - pointer to FIFO_BUFFER structure
*
* @return true if the FIFO is full, false if it is not.
*/
bool FIFO_Full(
FIFO_BUFFER const *b)
{
return (b ? (FIFO_Count(b) == b->buffer_len) : true);
}
/**
* Tests to see if space is available in the FIFO
*
* @param b - pointer to FIFO_BUFFER structure
* @param count [in] - number of bytes tested for availability
*
* @return true if the number of bytes sought is available
*/
bool FIFO_Available(
FIFO_BUFFER const *b,
unsigned count)
{
return (b ? (count <= (b->buffer_len - FIFO_Count(b))) : false);
}
/**
* Returns the empty status of the FIFO
*
* @param b - pointer to FIFO_BUFFER structure
* @return true if the FIFO is empty, false if it is not.
*/
bool FIFO_Empty(
FIFO_BUFFER const *b)
{
return (b ? (FIFO_Count(b) == 0) : true);
}
/**
* Peeks at the data from the front of the FIFO without removing it.
* Use FIFO_Empty() or FIFO_Available() function to see if there is
* data to retrieve since this function doesn't return a flag indicating
* success or failure.
*
* @param b - pointer to FIFO_BUFFER structure
*
* @return byte of data, or zero if nothing in the list
*/
uint8_t FIFO_Peek(
FIFO_BUFFER const *b)
{
unsigned index;
if (b) {
index = b->tail % b->buffer_len;
return (b->buffer[index]);
}
return 0;
}
/**
* Gets a byte from the front of the FIFO, and removes it.
* Use FIFO_Empty() or FIFO_Available() function to see if there is
* data to retrieve since this function doesn't return a flag indicating
* success or failure.
*
* @param b - pointer to FIFO_BUFFER structure
*
* @return the data
*/
uint8_t FIFO_Get(
FIFO_BUFFER * b)
{
uint8_t data_byte = 0;
unsigned index;
if (!FIFO_Empty(b)) {
index = b->tail % b->buffer_len;
data_byte = b->buffer[index];
b->tail++;
}
return data_byte;
}
/**
* Pulls one or more bytes from the front of the FIFO, and removes them
* from the FIFO. If less bytes are available, only the available bytes
* are retrieved.
*
* @param b - pointer to FIFO_BUFFER structure
* @param buffer [out] - buffer to hold the pulled bytes
* @param length [in] - number of bytes to pull from the FIFO
*
* @return the number of bytes actually pulled from the FIFO
*/
unsigned FIFO_Pull(
FIFO_BUFFER * b,
uint8_t * buffer,
unsigned length)
{
unsigned count;
uint8_t data_byte;
unsigned index;
count = FIFO_Count(b);
if (count > length) {
/* adjust to limit the number of bytes pulled */
count = length;
}
if (length > count) {
/* adjust the return value */
length = count;
}
while (count) {
index = b->tail % b->buffer_len;
data_byte = b->buffer[index];
b->tail++;
if (buffer) {
*buffer = data_byte;
buffer++;
}
count--;
}
return length;
}
/**
* Adds a byte of data to the FIFO
*
* @param b - pointer to FIFO_BUFFER structure
* @param data_byte [in] - data to put into the FIFO
*
* @return true on successful add, false if not added
*/
bool FIFO_Put(
FIFO_BUFFER * b,
uint8_t data_byte)
{
bool status = false; /* return value */
unsigned index;
if (b) {
/* limit the buffer to prevent overwriting */
if (!FIFO_Full(b)) {
index = b->head % b->buffer_len;
b->buffer[index] = data_byte;
b->head++;
status = true;
}
}
return status;
}
/**
* Adds one or more bytes of data to the FIFO
*
* @param b - pointer to FIFO_BUFFER structure
* @param buffer [out] - data bytes to add to the FIFO
* @param count [in] - number of bytes to add to the FIFO
*
* @return true if space available and added, false if not added
*/
bool FIFO_Add(
FIFO_BUFFER * b,
uint8_t * buffer,
unsigned count)
{
bool status = false; /* return value */
unsigned index;
/* limit the buffer to prevent overwriting */
if (FIFO_Available(b, count) && buffer) {
while (count) {
index = b->head % b->buffer_len;
b->buffer[index] = *buffer;
b->head++;
buffer++;
count--;
}
status = true;
}
return status;
}
/**
* Flushes any data in the FIFO buffer
*
* @param b - pointer to FIFO_BUFFER structure
*
* @return none
*/
void FIFO_Flush(
FIFO_BUFFER * b)
{
unsigned head; /* used to avoid volatile decision */
if (b) {
head = b->head;
b->tail = head;
}
}
/**
* Initializes the FIFO buffer with a data store
*
* @param b - pointer to FIFO_BUFFER structure
* @param buffer [in] - data bytes used to store bytes used by the FIFO
* @param buffer_len [in] - size of the buffer in bytes - must be power of 2.
*
* @return none
*/
void FIFO_Init(
FIFO_BUFFER * b,
volatile uint8_t * buffer,
unsigned buffer_len)
{
if (b && buffer && buffer_len) {
b->head = 0;
b->tail = 0;
b->buffer = buffer;
b->buffer_len = buffer_len;
}
return;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include "ctest.h"
/**
* Unit Test for the FIFO buffer
*
* @param pTest - test tracking pointer
*/
void testFIFOBuffer(
Test * pTest)
{
/* FIFO data structure */
FIFO_BUFFER test_buffer = { 0 };
/* FIFO data store. Note: size must be a power of two! */
volatile uint8_t data_store[64] = { 0 };
uint8_t add_data[40] = { "RoseSteveLouPatRachelJessicaDaniAmyHerb" };
uint8_t test_add_data[40] = { 0 };
uint8_t test_data = 0;
unsigned index = 0;
unsigned count = 0;
bool status = 0;
FIFO_Init(&test_buffer, data_store, sizeof(data_store));
ct_test(pTest, FIFO_Empty(&test_buffer));
/* load the buffer */
for (test_data = 0; test_data < sizeof(data_store); test_data++) {
ct_test(pTest, !FIFO_Full(&test_buffer));
ct_test(pTest, FIFO_Available(&test_buffer, 1));
status = FIFO_Put(&test_buffer, test_data);
ct_test(pTest, status == true);
ct_test(pTest, !FIFO_Empty(&test_buffer));
}
/* not able to put any more */
ct_test(pTest, FIFO_Full(&test_buffer));
ct_test(pTest, !FIFO_Available(&test_buffer, 1));
status = FIFO_Put(&test_buffer, 42);
ct_test(pTest, status == false);
/* unload the buffer */
for (index = 0; index < sizeof(data_store); index++) {
ct_test(pTest, !FIFO_Empty(&test_buffer));
test_data = FIFO_Peek(&test_buffer);
ct_test(pTest, test_data == index);
test_data = FIFO_Get(&test_buffer);
ct_test(pTest, test_data == index);
ct_test(pTest, FIFO_Available(&test_buffer, 1));
ct_test(pTest, !FIFO_Full(&test_buffer));
}
ct_test(pTest, FIFO_Empty(&test_buffer));
test_data = FIFO_Get(&test_buffer);
ct_test(pTest, test_data == 0);
test_data = FIFO_Peek(&test_buffer);
ct_test(pTest, test_data == 0);
ct_test(pTest, FIFO_Empty(&test_buffer));
/* test the ring around the buffer */
for (index = 0; index < sizeof(data_store); index++) {
ct_test(pTest, FIFO_Empty(&test_buffer));
ct_test(pTest, FIFO_Available(&test_buffer, 4));
for (count = 1; count < 4; count++) {
test_data = count;
status = FIFO_Put(&test_buffer, test_data);
ct_test(pTest, status == true);
ct_test(pTest, !FIFO_Empty(&test_buffer));
}
for (count = 1; count < 4; count++) {
ct_test(pTest, !FIFO_Empty(&test_buffer));
test_data = FIFO_Peek(&test_buffer);
ct_test(pTest, test_data == count);
test_data = FIFO_Get(&test_buffer);
ct_test(pTest, test_data == count);
}
}
ct_test(pTest, FIFO_Empty(&test_buffer));
/* test Add */
ct_test(pTest, FIFO_Available(&test_buffer, sizeof(add_data)));
status = FIFO_Add(&test_buffer, add_data, sizeof(add_data));
ct_test(pTest, status == true);
count = FIFO_Count(&test_buffer);
ct_test(pTest, count == sizeof(add_data));
ct_test(pTest, !FIFO_Empty(&test_buffer));
for (index = 0; index < sizeof(add_data); index++) {
/* unload the buffer */
ct_test(pTest, !FIFO_Empty(&test_buffer));
test_data = FIFO_Peek(&test_buffer);
ct_test(pTest, test_data == add_data[index]);
test_data = FIFO_Get(&test_buffer);
ct_test(pTest, test_data == add_data[index]);
}
ct_test(pTest, FIFO_Empty(&test_buffer));
/* test Pull */
ct_test(pTest, FIFO_Available(&test_buffer, sizeof(add_data)));
status = FIFO_Add(&test_buffer, add_data, sizeof(add_data));
ct_test(pTest, status == true);
count = FIFO_Count(&test_buffer);
ct_test(pTest, count == sizeof(add_data));
ct_test(pTest, !FIFO_Empty(&test_buffer));
count = FIFO_Pull(&test_buffer, &test_add_data[0], sizeof(test_add_data));
ct_test(pTest, FIFO_Empty(&test_buffer));
ct_test(pTest, count == sizeof(test_add_data));
for (index = 0; index < sizeof(add_data); index++) {
ct_test(pTest, test_add_data[index] == add_data[index]);
}
ct_test(pTest, FIFO_Available(&test_buffer, sizeof(add_data)));
status = FIFO_Add(&test_buffer, test_add_data, sizeof(add_data));
ct_test(pTest, status == true);
ct_test(pTest, !FIFO_Empty(&test_buffer));
for (index = 0; index < sizeof(add_data); index++) {
count = FIFO_Pull(&test_buffer, &test_add_data[0], 1);
ct_test(pTest, count == 1);
ct_test(pTest, test_add_data[0] == add_data[index]);
}
ct_test(pTest, FIFO_Empty(&test_buffer));
/* test flush */
status = FIFO_Add(&test_buffer, test_add_data, sizeof(test_add_data));
ct_test(pTest, status == true);
ct_test(pTest, !FIFO_Empty(&test_buffer));
FIFO_Flush(&test_buffer);
ct_test(pTest, FIFO_Empty(&test_buffer));
return;
}
#ifdef TEST_FIFO_BUFFER
/**
* Main program entry for Unit Test
*
* @return returns 0 on success, and non-zero on fail.
*/
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("FIFO Buffer", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testFIFOBuffer);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif
+115
View File
@@ -0,0 +1,115 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2006 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdio.h>
#include <string.h>
#include "filename.h"
/** @file filename.c Function for filename manipulation */
char *filename_remove_path(
const char *filename_in)
{
char *filename_out = (char *) filename_in;
/* allow the device ID to be set */
if (filename_in) {
filename_out = strrchr(filename_in, '\\');
if (!filename_out) {
filename_out = strrchr(filename_in, '/');
}
/* go beyond the slash */
if (filename_out) {
filename_out++;
} else {
/* no slash in filename */
filename_out = (char *) filename_in;
}
}
return filename_out;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testFilename(
Test * pTest)
{
char *data1 = "c:\\Joshua\\run";
char *data2 = "/home/Anna/run";
char *data3 = "c:\\Program Files\\Christopher\\run.exe";
char *data4 = "//Mary/data/run";
char *data5 = "bin\\run";
char *filename = NULL;
filename = filename_remove_path(data1);
ct_test(pTest, strcmp("run", filename) == 0);
filename = filename_remove_path(data2);
ct_test(pTest, strcmp("run", filename) == 0);
filename = filename_remove_path(data3);
ct_test(pTest, strcmp("run.exe", filename) == 0);
filename = filename_remove_path(data4);
ct_test(pTest, strcmp("run", filename) == 0);
filename = filename_remove_path(data5);
ct_test(pTest, strcmp("run", filename) == 0);
return;
}
#ifdef TEST_FILENAME
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("filename remove path", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testFilename);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_FILENAME */
#endif /* TEST */
+174
View File
@@ -0,0 +1,174 @@
/**
* @file
* @author Krzysztof Malorny <malornykrzysztof@gmail.com>
* @date 2011
* @brief GetAlarmSummary service encoding and decoding
*
* @section LICENSE
*
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @section DESCRIPTION
*
* The GetAlarmSummary service is used by a client BACnet-user
* to obtain a summary of "active alarms." The term "active alarm" refers
* to BACnet standard objects that have an Event_State property whose value
* is not equal to NORMAL and a Notify_Type property whose value is ALARM.
* The GetEnrollmentSummary service provides a more sophisticated approach
* with various kinds of filters
*/
#include <assert.h>
#include "bacdcode.h"
#include "get_alarm_sum.h"
#include "npdu.h"
/* encode service */
int get_alarm_summary_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id
) {
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_GET_ALARM_SUMMARY;
apdu_len = 4;
}
return apdu_len;
}
/** Helper function encode the beginning of a GetAlarmSummary ACK.
*
* @param apdu - buffer where to put encoding
* @param invoke_id - unique sequence number sent with the message
*
* @return number of bytes encoded
*/
int get_alarm_summary_ack_encode_apdu_init(
uint8_t * apdu,
uint8_t invoke_id)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */
apdu[1] = invoke_id; /* original invoke id from request */
apdu[2] = SERVICE_CONFIRMED_GET_ALARM_SUMMARY;
apdu_len = 3;
}
return apdu_len;
}
/** Helper function encode the data portion of a GetAlarmSummary ACK.
*
* @param apdu - buffer where to put encoding
* @param max_apdu - number of bytes available in the buffer for encoding
* @param get_alarm_data - BACNET_GET_ALARM_SUMMARY_DATA type with data
*
* @return number of bytes encoded, or BACNET_STATUS_ERROR if an error.
*/
int get_alarm_summary_ack_encode_apdu_data(
uint8_t * apdu,
size_t max_apdu,
BACNET_GET_ALARM_SUMMARY_DATA * get_alarm_data)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (!apdu) {
apdu_len = BACNET_STATUS_ERROR;
} else if (max_apdu >= 10) {
/* tag 0 - Object Identifier */
apdu_len +=
encode_application_object_id(&apdu[apdu_len],
(int) get_alarm_data->objectIdentifier.type,
get_alarm_data->objectIdentifier.instance);
/* tag 1 - Alarm State */
apdu_len +=
encode_application_enumerated(&apdu[apdu_len],
get_alarm_data->alarmState);
/* tag 2 - Acknowledged Transitions */
apdu_len +=
encode_application_bitstring(&apdu[apdu_len],
&get_alarm_data->acknowledgedTransitions);
} else {
apdu_len = BACNET_STATUS_ABORT;
}
return apdu_len;
}
/** Helper function to decode the data portion of a GetAlarmSummary ACK.
*
* @param apdu - buffer where to put encoding
* @param max_apdu - number of bytes available in the buffer for encoding
* @param get_alarm_data - BACNET_GET_ALARM_SUMMARY_DATA type for data
*
* @return number of bytes decoded, or BACNET_STATUS_ERROR if an error.
*/
int get_alarm_summary_ack_decode_apdu_data(
uint8_t * apdu,
size_t max_apdu,
BACNET_GET_ALARM_SUMMARY_DATA * get_alarm_data)
{
int apdu_len = 0; /* total length of the apdu, return value */
BACNET_APPLICATION_DATA_VALUE value;
if (!apdu) {
apdu_len = BACNET_STATUS_ERROR;
} else if (max_apdu >= 10) {
/* tag 0 - Object Identifier */
apdu_len +=
bacapp_decode_application_data(&apdu[apdu_len],
(unsigned int)(max_apdu - apdu_len), &value);
if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) {
get_alarm_data->objectIdentifier = value.type.Object_Id;
} else {
return BACNET_STATUS_ERROR;
}
/* tag 1 - Alarm State */
apdu_len +=
bacapp_decode_application_data(&apdu[apdu_len],
(unsigned int)(max_apdu - apdu_len), &value);
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
get_alarm_data->alarmState =
(BACNET_EVENT_STATE) value.type.Enumerated;
} else {
return BACNET_STATUS_ERROR;
}
/* tag 2 - Acknowledged Transitions */
apdu_len +=
bacapp_decode_application_data(&apdu[apdu_len],
(unsigned int)(max_apdu - apdu_len), &value);
if (value.tag == BACNET_APPLICATION_TAG_BIT_STRING) {
get_alarm_data->acknowledgedTransitions = value.type.Bit_String;
} else {
return BACNET_STATUS_ERROR;
}
}
return apdu_len;
}
+515
View File
@@ -0,0 +1,515 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2009 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "getevent.h"
/** @file getevent.c Encode/Decode GetEvent services */
/* encode service */
int getevent_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_OBJECT_ID * lastReceivedObjectIdentifier)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_GET_EVENT_INFORMATION;
apdu_len = 4;
/* encode optional parameter */
if (lastReceivedObjectIdentifier) {
len =
encode_context_object_id(&apdu[apdu_len], 0,
(int) lastReceivedObjectIdentifier->type,
lastReceivedObjectIdentifier->instance);
apdu_len += len;
}
}
return apdu_len;
}
/* decode the service request only */
int getevent_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_OBJECT_ID * lastReceivedObjectIdentifier)
{
unsigned len = 0;
/* check for value pointers */
if (apdu_len && lastReceivedObjectIdentifier) {
/* Tag 0: Object ID - optional */
if (!decode_is_context_tag(&apdu[len++], 0))
return -1;
len +=
decode_object_id(&apdu[len], &lastReceivedObjectIdentifier->type,
&lastReceivedObjectIdentifier->instance);
}
return (int) len;
}
int getevent_ack_encode_apdu_init(
uint8_t * apdu,
size_t max_apdu,
uint8_t invoke_id)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu && (max_apdu >= 4)) {
apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */
apdu[1] = invoke_id; /* original invoke id from request */
apdu[2] = SERVICE_CONFIRMED_GET_EVENT_INFORMATION;
apdu_len = 3;
/* service ack follows */
/* Tag 0: listOfEventSummaries */
apdu_len += encode_opening_tag(&apdu[apdu_len], 0);
}
return apdu_len;
}
int getevent_ack_encode_apdu_data(
uint8_t * apdu,
size_t max_apdu,
BACNET_GET_EVENT_INFORMATION_DATA * get_event_data)
{
int apdu_len = 0; /* total length of the apdu, return value */
BACNET_GET_EVENT_INFORMATION_DATA *event_data;
unsigned i = 0; /* counter */
/* unused parameter */
max_apdu = max_apdu;
if (apdu) {
event_data = get_event_data;
while (event_data) {
/* Tag 0: objectIdentifier */
apdu_len +=
encode_context_object_id(&apdu[apdu_len], 0,
(int) event_data->objectIdentifier.type,
event_data->objectIdentifier.instance);
/* Tag 1: eventState */
apdu_len +=
encode_context_enumerated(&apdu[apdu_len], 1,
event_data->eventState);
/* Tag 2: acknowledgedTransitions */
apdu_len +=
encode_context_bitstring(&apdu[apdu_len], 2,
&event_data->acknowledgedTransitions);
/* Tag 3: eventTimeStamps */
apdu_len += encode_opening_tag(&apdu[apdu_len], 3);
for (i = 0; i < 3; i++) {
apdu_len +=
bacapp_encode_timestamp(&apdu[apdu_len],
&event_data->eventTimeStamps[i]);
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 3);
/* Tag 4: notifyType */
apdu_len +=
encode_context_enumerated(&apdu[apdu_len], 4,
event_data->notifyType);
/* Tag 5: eventEnable */
apdu_len +=
encode_context_bitstring(&apdu[apdu_len], 5,
&event_data->eventEnable);
/* Tag 6: eventPriorities */
apdu_len += encode_opening_tag(&apdu[apdu_len], 6);
for (i = 0; i < 3; i++) {
apdu_len +=
encode_application_unsigned(&apdu[apdu_len],
event_data->eventPriorities[i]);
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 6);
event_data = event_data->next;
}
}
return apdu_len;
}
int getevent_ack_encode_apdu_end(
uint8_t * apdu,
size_t max_apdu,
bool moreEvents)
{
int apdu_len = 0; /* total length of the apdu, return value */
/* unused parameter */
max_apdu = max_apdu;
if (apdu) {
apdu_len += encode_closing_tag(&apdu[apdu_len], 0);
apdu_len += encode_context_boolean(&apdu[apdu_len], 1, moreEvents);
}
return apdu_len;
}
int getevent_ack_decode_service_request(
uint8_t * apdu,
int apdu_len, /* total length of the apdu */
BACNET_GET_EVENT_INFORMATION_DATA * get_event_data,
bool * moreEvents)
{
uint8_t tag_number = 0;
uint32_t len_value = 0;
int len = 0; /* total length of decodes */
uint32_t enum_value = 0; /* for decoding */
BACNET_GET_EVENT_INFORMATION_DATA *event_data;
unsigned i = 0; /* counter */
/* FIXME: check apdu_len against the len during decode */
event_data = get_event_data;
if (apdu && apdu_len && event_data && moreEvents) {
if (!decode_is_opening_tag_number(&apdu[len], 0)) {
return -1;
}
len++;
while (event_data) {
/* Tag 0: objectIdentifier */
if (decode_is_context_tag(&apdu[len], 0)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_object_id(&apdu[len],
&event_data->objectIdentifier.type,
&event_data->objectIdentifier.instance);
} else {
return -1;
}
/* Tag 1: eventState */
if (decode_is_context_tag(&apdu[len], 1)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_enumerated(&apdu[len], len_value, &enum_value);
event_data->eventState = enum_value;
} else {
return -1;
}
/* Tag 2: acknowledgedTransitions */
if (decode_is_context_tag(&apdu[len], 2)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_bitstring(&apdu[len], len_value,
&event_data->acknowledgedTransitions);
} else {
return -1;
}
/* Tag 3: eventTimeStamps */
if (decode_is_opening_tag_number(&apdu[len], 3)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
for (i = 0; i < 3; i++) {
len +=
bacapp_decode_timestamp(&apdu[len],
&event_data->eventTimeStamps[i]);
}
} else {
return -1;
}
if (decode_is_closing_tag_number(&apdu[len], 3)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
} else {
return -1;
}
/* Tag 4: notifyType */
if (decode_is_context_tag(&apdu[len], 4)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_enumerated(&apdu[len], len_value, &enum_value);
event_data->notifyType = enum_value;
} else {
return -1;
}
/* Tag 5: eventEnable */
if (decode_is_context_tag(&apdu[len], 5)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_bitstring(&apdu[len], len_value,
&event_data->eventEnable);
} else {
return -1;
}
/* Tag 6: eventPriorities */
if (decode_is_opening_tag_number(&apdu[len], 6)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
for (i = 0; i < 3; i++) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_unsigned(&apdu[len], len_value,
&event_data->eventPriorities[i]);
}
} else {
return -1;
}
if (decode_is_closing_tag_number(&apdu[len], 6)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
} else {
return -1;
}
if (decode_is_closing_tag_number(&apdu[len], 0)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
event_data->next = NULL;
}
event_data = event_data->next;
}
if (decode_is_context_tag(&apdu[len], 1)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
if (len_value == 1)
*moreEvents = decode_context_boolean(&apdu[len++]);
else
*moreEvents = false;
} else {
return -1;
}
}
return len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
int getevent_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
BACNET_OBJECT_ID * lastReceivedObjectIdentifier)
{
int len = 0;
unsigned offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST)
return -1;
/* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_GET_EVENT_INFORMATION)
return -1;
offset = 4;
if (apdu_len > offset) {
len =
getevent_decode_service_request(&apdu[offset], apdu_len - offset,
lastReceivedObjectIdentifier);
}
return len;
}
int getevent_ack_decode_apdu(
uint8_t * apdu,
int apdu_len, /* total length of the apdu */
uint8_t * invoke_id,
BACNET_GET_EVENT_INFORMATION_DATA * get_event_data,
bool * moreEvents)
{
int len = 0;
int offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_COMPLEX_ACK)
return -1;
*invoke_id = apdu[1];
if (apdu[2] != SERVICE_CONFIRMED_GET_EVENT_INFORMATION)
return -1;
offset = 3;
if (apdu_len > offset) {
len =
getevent_ack_decode_service_request(&apdu[offset],
apdu_len - offset, get_event_data, moreEvents);
}
return len;
}
void testGetEventInformationAck(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 1;
uint8_t test_invoke_id = 0;
BACNET_GET_EVENT_INFORMATION_DATA event_data;
BACNET_GET_EVENT_INFORMATION_DATA test_event_data;
bool moreEvents = false;
bool test_moreEvents = false;
unsigned i = 0;
event_data.objectIdentifier.type = OBJECT_BINARY_INPUT;
event_data.objectIdentifier.instance = 1;
event_data.eventState = EVENT_STATE_NORMAL;
bitstring_init(&event_data.acknowledgedTransitions);
bitstring_set_bit(&event_data.acknowledgedTransitions,
TRANSITION_TO_OFFNORMAL, false);
bitstring_set_bit(&event_data.acknowledgedTransitions, TRANSITION_TO_FAULT,
false);
bitstring_set_bit(&event_data.acknowledgedTransitions,
TRANSITION_TO_NORMAL, false);
for (i = 0; i < 3; i++) {
event_data.eventTimeStamps[i].tag = TIME_STAMP_SEQUENCE;
event_data.eventTimeStamps[i].value.sequenceNum = 0;
}
event_data.notifyType = NOTIFY_ALARM;
bitstring_init(&event_data.eventEnable);
bitstring_set_bit(&event_data.eventEnable, TRANSITION_TO_OFFNORMAL, true);
bitstring_set_bit(&event_data.eventEnable, TRANSITION_TO_FAULT, true);
bitstring_set_bit(&event_data.eventEnable, TRANSITION_TO_NORMAL, true);
for (i = 0; i < 3; i++) {
event_data.eventPriorities[i] = 1;
}
event_data.next = NULL;
len = getevent_ack_encode_apdu_init(&apdu[0], sizeof(apdu), invoke_id);
ct_test(pTest, len != 0);
ct_test(pTest, len != -1);
apdu_len = len;
len =
getevent_ack_encode_apdu_data(&apdu[apdu_len], sizeof(apdu) - apdu_len,
&event_data);
ct_test(pTest, len != 0);
ct_test(pTest, len != -1);
apdu_len += len;
len =
getevent_ack_encode_apdu_end(&apdu[apdu_len], sizeof(apdu) - apdu_len,
moreEvents);
ct_test(pTest, len != 0);
ct_test(pTest, len != -1);
apdu_len += len;
len = getevent_ack_decode_apdu(&apdu[0], apdu_len, /* total length of the apdu */
&test_invoke_id, &test_event_data, &test_moreEvents);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest,
event_data.objectIdentifier.type ==
test_event_data.objectIdentifier.type);
ct_test(pTest,
event_data.objectIdentifier.instance ==
test_event_data.objectIdentifier.instance);
ct_test(pTest, event_data.eventState == test_event_data.eventState);
}
void testGetEventInformation(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 128;
uint8_t test_invoke_id = 0;
BACNET_OBJECT_ID lastReceivedObjectIdentifier;
BACNET_OBJECT_ID test_lastReceivedObjectIdentifier;
lastReceivedObjectIdentifier.type = OBJECT_BINARY_INPUT;
lastReceivedObjectIdentifier.instance = 12345;
len =
getevent_encode_apdu(&apdu[0], invoke_id,
&lastReceivedObjectIdentifier);
ct_test(pTest, len != 0);
apdu_len = len;
len =
getevent_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_lastReceivedObjectIdentifier);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest,
test_lastReceivedObjectIdentifier.type ==
lastReceivedObjectIdentifier.type);
ct_test(pTest,
test_lastReceivedObjectIdentifier.instance ==
lastReceivedObjectIdentifier.instance);
return;
}
#ifdef TEST_GET_EVENT_INFORMATION
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet GetEventInformation", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testGetEventInformation);
assert(rc);
rc = ct_addTestFunction(pTest, testGetEventInformationAck);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif /* TEST */
Executable
+221
View File
@@ -0,0 +1,221 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdef.h"
#include "npdu.h"
#include "dcc.h"
#include "bacdcode.h"
#include "address.h"
#include "iam.h"
/** @file iam.c Encode/Decode I-Am service */
/* encode I-Am service */
int iam_encode_apdu(
uint8_t * apdu,
uint32_t device_id,
unsigned max_apdu,
int segmentation,
uint16_t vendor_id)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST;
apdu[1] = SERVICE_UNCONFIRMED_I_AM; /* service choice */
apdu_len = 2;
len =
encode_application_object_id(&apdu[apdu_len], OBJECT_DEVICE,
device_id);
apdu_len += len;
len = encode_application_unsigned(&apdu[apdu_len], max_apdu);
apdu_len += len;
len =
encode_application_enumerated(&apdu[apdu_len],
(uint32_t) segmentation);
apdu_len += len;
len = encode_application_unsigned(&apdu[apdu_len], vendor_id);
apdu_len += len;
}
return apdu_len;
}
int iam_decode_service_request(
uint8_t * apdu,
uint32_t * pDevice_id,
unsigned *pMax_apdu,
int *pSegmentation,
uint16_t * pVendor_id)
{
int len = 0;
int apdu_len = 0; /* total length of the apdu, return value */
uint16_t object_type = 0; /* should be a Device Object */
uint32_t object_instance = 0;
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t decoded_value = 0;
/* OBJECT ID - object id */
len =
decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value);
apdu_len += len;
if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID)
return -1;
len = decode_object_id(&apdu[apdu_len], &object_type, &object_instance);
apdu_len += len;
if (object_type != OBJECT_DEVICE)
return -1;
if (pDevice_id)
*pDevice_id = object_instance;
/* MAX APDU - unsigned */
len =
decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value);
apdu_len += len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
return -1;
len = decode_unsigned(&apdu[apdu_len], len_value, &decoded_value);
apdu_len += len;
if (pMax_apdu)
*pMax_apdu = (unsigned) decoded_value;
/* Segmentation - enumerated */
len =
decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value);
apdu_len += len;
if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED)
return -1;
len = decode_enumerated(&apdu[apdu_len], len_value, &decoded_value);
apdu_len += len;
if (decoded_value >= MAX_BACNET_SEGMENTATION)
return -1;
if (pSegmentation)
*pSegmentation = (int) decoded_value;
/* Vendor ID - unsigned16 */
len =
decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value);
apdu_len += len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
return -1;
len = decode_unsigned(&apdu[apdu_len], len_value, &decoded_value);
apdu_len += len;
if (decoded_value > 0xFFFF)
return -1;
if (pVendor_id)
*pVendor_id = (uint16_t) decoded_value;
return apdu_len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
int iam_decode_apdu(
uint8_t * apdu,
uint32_t * pDevice_id,
unsigned *pMax_apdu,
int *pSegmentation,
uint16_t * pVendor_id)
{
int apdu_len = 0; /* total length of the apdu, return value */
/* valid data? */
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST)
return -1;
if (apdu[1] != SERVICE_UNCONFIRMED_I_AM)
return -1;
apdu_len =
iam_decode_service_request(&apdu[2], pDevice_id, pMax_apdu,
pSegmentation, pVendor_id);
return apdu_len;
}
void testIAm(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
uint32_t device_id = 42;
unsigned max_apdu = 480;
int segmentation = SEGMENTATION_NONE;
uint16_t vendor_id = 42;
uint32_t test_device_id = 0;
unsigned test_max_apdu = 0;
int test_segmentation = 0;
uint16_t test_vendor_id = 0;
len =
iam_encode_apdu(&apdu[0], device_id, max_apdu, segmentation,
vendor_id);
ct_test(pTest, len != 0);
len =
iam_decode_apdu(&apdu[0], &test_device_id, &test_max_apdu,
&test_segmentation, &test_vendor_id);
ct_test(pTest, len != -1);
ct_test(pTest, test_device_id == device_id);
ct_test(pTest, test_vendor_id == vendor_id);
ct_test(pTest, test_max_apdu == max_apdu);
ct_test(pTest, test_segmentation == segmentation);
}
#ifdef TEST_IAM
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet I-Am", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testIAm);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_IAM */
#endif /* TEST */
+212
View File
@@ -0,0 +1,212 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2006 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "ihave.h"
/** @file ihave.c Encode/Decode I-Have service */
int ihave_encode_apdu(
uint8_t * apdu,
BACNET_I_HAVE_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu && data) {
apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST;
apdu[1] = SERVICE_UNCONFIRMED_I_HAVE;
apdu_len = 2;
/* deviceIdentifier */
len =
encode_application_object_id(&apdu[apdu_len],
(int) data->device_id.type, data->device_id.instance);
apdu_len += len;
/* objectIdentifier */
len =
encode_application_object_id(&apdu[apdu_len],
(int) data->object_id.type, data->object_id.instance);
apdu_len += len;
/* objectName */
len =
encode_application_character_string(&apdu[apdu_len],
&data->object_name);
apdu_len += len;
}
return apdu_len;
}
#if BACNET_SVC_I_HAVE_A
/* decode the service request only */
int ihave_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_I_HAVE_DATA * data)
{
int len = 0;
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint16_t decoded_type = 0; /* for decoding */
if (apdu_len && data) {
/* deviceIdentifier */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) {
len +=
decode_object_id(&apdu[len], &decoded_type,
&data->device_id.instance);
data->device_id.type = decoded_type;
} else
return -1;
/* objectIdentifier */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) {
len +=
decode_object_id(&apdu[len], &decoded_type,
&data->object_id.instance);
data->object_id.type = decoded_type;
} else
return -1;
/* objectName */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number == BACNET_APPLICATION_TAG_CHARACTER_STRING) {
len +=
decode_character_string(&apdu[len], len_value,
&data->object_name);
} else
return -1;
} else
return -1;
return len;
}
int ihave_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
BACNET_I_HAVE_DATA * data)
{
int len = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST)
return -1;
if (apdu[1] != SERVICE_UNCONFIRMED_I_HAVE)
return -1;
len = ihave_decode_service_request(&apdu[2], apdu_len - 2, data);
return len;
}
#endif
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testIHaveData(
Test * pTest,
BACNET_I_HAVE_DATA * data)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_I_HAVE_DATA test_data;
len = ihave_encode_apdu(&apdu[0], data);
ct_test(pTest, len != 0);
apdu_len = len;
len = ihave_decode_apdu(&apdu[0], apdu_len, &test_data);
ct_test(pTest, len != -1);
ct_test(pTest, test_data.device_id.type == data->device_id.type);
ct_test(pTest, test_data.device_id.instance == data->device_id.instance);
ct_test(pTest, test_data.object_id.type == data->object_id.type);
ct_test(pTest, test_data.object_id.instance == data->object_id.instance);
ct_test(pTest, characterstring_same(&test_data.object_name,
&data->object_name));
}
void testIHave(
Test * pTest)
{
BACNET_I_HAVE_DATA data;
characterstring_init_ansi(&data.object_name, "Patricia - my love!");
data.device_id.type = OBJECT_DEVICE;
for (data.device_id.instance = 1;
data.device_id.instance <= BACNET_MAX_INSTANCE;
data.device_id.instance <<= 1) {
for (data.object_id.type = OBJECT_ANALOG_INPUT;
data.object_id.type < MAX_BACNET_OBJECT_TYPE;
data.object_id.type++) {
for (data.object_id.instance = 1;
data.object_id.instance <= BACNET_MAX_INSTANCE;
data.object_id.instance <<= 1) {
testIHaveData(pTest, &data);
}
}
}
}
#ifdef TEST_I_HAVE
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet I-Have", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testIHave);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_WHOIS */
#endif /* TEST */
+267
View File
@@ -0,0 +1,267 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdbool.h>
#include <string.h>
#include "indtext.h"
/** @file indtext.c Maps text strings and indices of type INDTEXT_DATA */
#if !defined(__BORLANDC__) && !defined(_MSC_VER)
#include <ctype.h>
int stricmp(
const char *s1,
const char *s2)
{
unsigned char c1, c2;
do {
c1 = (unsigned char) *s1;
c2 = (unsigned char) *s2;
c1 = (unsigned char) tolower(c1);
c2 = (unsigned char) tolower(c2);
s1++;
s2++;
} while ((c1 == c2) && (c1 != '\0'));
return (int) c1 - c2;
}
#endif
#if defined(_MSC_VER)
#define stricmp _stricmp
#endif
bool indtext_by_string(
INDTEXT_DATA * data_list,
const char *search_name,
unsigned *found_index)
{
bool found = false;
unsigned index = 0;
if (data_list && search_name) {
while (data_list->pString) {
if (strcmp(data_list->pString, search_name) == 0) {
index = data_list->index;
found = true;
break;
}
data_list++;
}
}
if (found && found_index)
*found_index = index;
return found;
}
/* case insensitive version */
bool indtext_by_istring(
INDTEXT_DATA * data_list,
const char *search_name,
unsigned *found_index)
{
bool found = false;
unsigned index = 0;
if (data_list && search_name) {
while (data_list->pString) {
if (stricmp(data_list->pString, search_name) == 0) {
index = data_list->index;
found = true;
break;
}
data_list++;
}
}
if (found && found_index)
*found_index = index;
return found;
}
unsigned indtext_by_string_default(
INDTEXT_DATA * data_list,
const char *search_name,
unsigned default_index)
{
unsigned index = 0;
if (!indtext_by_string(data_list, search_name, &index))
index = default_index;
return index;
}
unsigned indtext_by_istring_default(
INDTEXT_DATA * data_list,
const char *search_name,
unsigned default_index)
{
unsigned index = 0;
if (!indtext_by_istring(data_list, search_name, &index))
index = default_index;
return index;
}
const char *indtext_by_index_default(
INDTEXT_DATA * data_list,
unsigned index,
const char *default_string)
{
const char *pString = NULL;
if (data_list) {
while (data_list->pString) {
if (data_list->index == index) {
pString = data_list->pString;
break;
}
data_list++;
}
}
return pString ? pString : default_string;
}
const char *indtext_by_index_split_default(
INDTEXT_DATA * data_list,
unsigned index,
unsigned split_index,
const char *before_split_default_name,
const char *default_name)
{
if (index < split_index)
return indtext_by_index_default(data_list, index,
before_split_default_name);
else
return indtext_by_index_default(data_list, index, default_name);
}
const char *indtext_by_index(
INDTEXT_DATA * data_list,
unsigned index)
{
return indtext_by_index_default(data_list, index, NULL);
}
unsigned indtext_count(
INDTEXT_DATA * data_list)
{
unsigned count = 0; /* return value */
if (data_list) {
while (data_list->pString) {
count++;
data_list++;
}
}
return count;
}
#ifdef TEST
#include <assert.h>
#include "ctest.h"
static INDTEXT_DATA data_list[] = {
{1, "Joshua"},
{2, "Mary"},
{3, "Anna"},
{4, "Christopher"},
{5, "Patricia"},
{0, NULL}
};
void testIndexText(
Test * pTest)
{
unsigned i; /*counter */
const char *pString;
unsigned index;
bool valid;
unsigned count = 0;
for (i = 0; i < 10; i++) {
pString = indtext_by_index(data_list, i);
if (pString) {
count++;
valid = indtext_by_string(data_list, pString, &index);
ct_test(pTest, valid == true);
ct_test(pTest, index == i);
ct_test(pTest, index == indtext_by_string_default(data_list,
pString, index));
}
}
ct_test(pTest, indtext_count(data_list) == count);
ct_test(pTest, indtext_by_string(data_list, "Harry", NULL) == false);
ct_test(pTest, indtext_by_string(data_list, NULL, NULL) == false);
ct_test(pTest, indtext_by_string(NULL, NULL, NULL) == false);
ct_test(pTest, indtext_by_index(data_list, 0) == NULL);
ct_test(pTest, indtext_by_index(data_list, 10) == NULL);
ct_test(pTest, indtext_by_index(NULL, 10) == NULL);
/* case insensitive versions */
ct_test(pTest, indtext_by_istring(data_list, "JOSHUA", NULL) == true);
ct_test(pTest, indtext_by_istring(data_list, "joshua", NULL) == true);
valid = indtext_by_istring(data_list, "ANNA", &index);
ct_test(pTest, index == indtext_by_istring_default(data_list, "ANNA",
index));
}
#endif
#ifdef TEST_INDEX_TEXT
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("index text", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testIndexText);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_INDEX_TEXT */
+121
View File
@@ -0,0 +1,121 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2003 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
/*#define TEST */
/*#define TEST_KEY */
#include "key.h"
/** @file key.c Tests (only) of key encoding/decoding. */
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
/* test the encode and decode macros */
void testKeys(
Test * pTest)
{
int type, id;
int decoded_type, decoded_id;
KEY key;
for (type = 0; type < KEY_TYPE_MAX; type++) {
for (id = 0; id < KEY_ID_MAX; id++) {
key = KEY_ENCODE(type, id);
decoded_type = KEY_DECODE_TYPE(key);
decoded_id = KEY_DECODE_ID(key);
ct_test(pTest, decoded_type == type);
ct_test(pTest, decoded_id == id);
}
}
return;
}
/* test the encode and decode macros */
void testKeySample(
Test * pTest)
{
int type, id;
int type_list[] = { 0, 1, KEY_TYPE_MAX / 2, KEY_TYPE_MAX - 1, -1 };
int id_list[] = { 0, 1, KEY_ID_MAX / 2, KEY_ID_MAX - 1, -1 };
int type_index = 0;
int id_index = 0;
int decoded_type, decoded_id;
KEY key;
while (type_list[type_index] != -1) {
while (id_list[id_index] != -1) {
type = type_list[type_index];
id = id_list[id_index];
key = KEY_ENCODE(type, id);
decoded_type = KEY_DECODE_TYPE(key);
decoded_id = KEY_DECODE_ID(key);
ct_test(pTest, decoded_type == type);
ct_test(pTest, decoded_id == id);
id_index++;
}
id_index = 0;
type_index++;
}
return;
}
#ifdef TEST_KEY
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("key", NULL);
/* add the individual tests */
/* rc = ct_addTestFunction(pTest, testKeys); */
/* assert(rc); */
rc = ct_addTestFunction(pTest, testKeySample);
assert(rc);
/* run all the tests */
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
/* completed testing - cleanup */
ct_destroy(pTest);
return 0;
}
#endif /* LOCAL_TEST */
#endif
+737
View File
@@ -0,0 +1,737 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2003 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
/** @file keylist.c Keyed Linked List Library */
/* */
/* This is an enhanced array of pointers to data. */
/* The list is sorted, indexed, and keyed. */
/* The array is much faster than a linked list. */
/* It stores a pointer to data, which you must */
/* malloc and free on your own, or just use */
/* static data */
#include <stdlib.h>
#include "keylist.h" /* check for valid prototypes */
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
/******************************************************************** */
/* Generic node routines */
/******************************************************************** */
/* grab memory for a node */
static struct Keylist_Node *NodeCreate(
void)
{
return calloc(1, sizeof(struct Keylist_Node));
}
/* grab memory for a list */
static struct Keylist *KeylistCreate(
void)
{
return calloc(1, sizeof(struct Keylist));
}
/* check to see if the array is big enough for an addition */
/* or is too big when we are deleting and we can shrink */
/* returns TRUE if success, FALSE if failed */
static int CheckArraySize(
OS_Keylist list)
{
int new_size = 0; /* set it up so that no size change is the default */
const int chunk = 8; /* minimum number of nodes to allocate memory for */
struct Keylist_Node **new_array; /* new array of nodes, if needed */
int i; /* counter */
if (!list)
return FALSE;
/* indicates the need for more memory allocation */
if (list->count == list->size)
new_size = list->size + chunk;
/* allow for shrinking memory */
else if ((list->size > chunk) && (list->count < (list->size - chunk)))
new_size = list->size - chunk;
if (new_size) {
/* Allocate more room for node pointer array */
new_array = calloc((size_t) new_size, sizeof(new_array));
/* See if we got the memory we wanted */
if (!new_array)
return FALSE;
/* copy the nodes from the old array to the new array */
if (list->array) {
for (i = 0; i < list->count; i++) {
new_array[i] = list->array[i];
}
free(list->array);
}
list->array = new_array;
list->size = new_size;
}
return TRUE;
}
/* find the index of the key that we are looking for */
/* since it is sorted, we can optimize the search */
/* returns TRUE if found, and FALSE not found */
/* returns the found key and the index where it was found in parameters */
/* If the key is not found, the nearest index from the bottom will be returned, */
/* allowing the ability to find where an key should go into the list. */
static int FindIndex(
OS_Keylist list,
KEY key,
int *pIndex)
{
struct Keylist_Node *node; /* holds the new node */
int left = 0; /* the left branch of tree, beginning of list */
int right = 0; /* the right branch on the tree, end of list */
int index = 0; /* our current search place in the array */
KEY current_key = 0; /* place holder for current node key */
int status = FALSE; /* return value */
if (!list || !list->array || !list->count) {
*pIndex = 0;
return (FALSE);
}
right = list->count - 1;
/* assume that the list is sorted */
do {
/* A binary search */
index = (left + right) / 2;
node = list->array[index];
if (!node)
break;
current_key = node->key;
if (key < current_key)
right = index - 1;
else
left = index + 1;
}
while ((key != current_key) && (left <= right));
if (key == current_key) {
status = TRUE;
*pIndex = index;
}
else {
/* where the index should be */
if (key > current_key)
*pIndex = index + 1;
else
*pIndex = index;
}
return (status);
}
/******************************************************************** */
/* list data functions */
/******************************************************************** */
/* inserts a node into its sorted position */
int Keylist_Data_Add(
OS_Keylist list,
KEY key,
void *data)
{
struct Keylist_Node *node; /* holds the new node */
int index = -1; /* return value */
int i; /* counts through the array */
if (list && CheckArraySize(list)) {
/* figure out where to put the new node */
if (list->count) {
(void) FindIndex(list, key, &index);
/* Add to the beginning of the list */
if (index < 0)
index = 0;
/* Add to the end of the list */
else if (index > list->count)
index = list->count;
/* Move all the items up to make room for the new one */
for (i = list->count; i > index; i--) {
list->array[i] = list->array[i - 1];
}
}
else {
index = 0;
}
/* create and add the node */
node = NodeCreate();
if (node) {
list->count++;
node->key = key;
node->data = data;
list->array[index] = node;
}
}
return index;
}
/* deletes a node specified by its index */
/* returns the data from the node */
void *Keylist_Data_Delete_By_Index(
OS_Keylist list,
int index)
{
struct Keylist_Node *node;
void *data = NULL;
if (list && list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
if (node)
data = node->data;
/* move the nodes to account for the deleted one */
if (list->count == 1) {
/* There is no node shifting to do */
}
/* We are the last one */
else if (index == (list->count - 1)) {
/* There is no node shifting to do */
}
/* Move all the nodes down one */
else {
int i; /* counter */
int count = list->count - 1;
for (i = index; i < count; i++) {
list->array[i] = list->array[i + 1];
}
}
list->count--;
if (node)
free(node);
/* potentially reduce the size of the array */
(void) CheckArraySize(list);
}
return (data);
}
/* deletes a node specified by its key */
/* returns the data from the node */
void *Keylist_Data_Delete(
OS_Keylist list,
KEY key)
{
void *data = NULL; /* return value */
int index; /* where the node is in the array */
if (list) {
if (FindIndex(list, key, &index))
data = Keylist_Data_Delete_By_Index(list, index);
}
return data;
}
/* returns the data from last node, and removes it from the list */
void *Keylist_Data_Pop(
OS_Keylist list)
{
void *data = NULL; /* return value */
int index; /* position in the array */
if (list && list->count) {
index = list->count - 1;
data = Keylist_Data_Delete_By_Index(list, index);
}
return data;
}
/* returns the data from the node specified by key */
void *Keylist_Data(
OS_Keylist list,
KEY key)
{
struct Keylist_Node *node = NULL;
int index = 0; /* used to look up the index of node */
if (list && list->array && list->count) {
if (FindIndex(list, key, &index))
node = list->array[index];
}
return node ? node->data : NULL;
}
/* returns the index from the node specified by key */
int Keylist_Index(
OS_Keylist list,
KEY key)
{
int index = -1; /* used to look up the index of node */
if (list && list->array && list->count) {
if (!FindIndex(list, key, &index)) {
index = -1;
}
}
return index;
}
/* returns the data specified by index */
void *Keylist_Data_Index(
OS_Keylist list,
int index)
{
struct Keylist_Node *node = NULL;
if (list && list->array && list->count && (index >= 0) &&
(index < list->count))
node = list->array[index];
return node ? node->data : NULL;
}
/* return the key at the given index */
KEY Keylist_Key(
OS_Keylist list,
int index)
{
KEY key = 0; /* return value */
struct Keylist_Node *node;
if (list && list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
if (node)
key = node->key;
}
return key;
}
/* returns the next empty key from the list */
KEY Keylist_Next_Empty_Key(
OS_Keylist list,
KEY key)
{
int index;
if (list) {
while (FindIndex(list, key, &index)) {
if (KEY_LAST(key))
break;
key++;
}
}
return key;
}
/* return the number of nodes in this list */
int Keylist_Count(
OS_Keylist list)
{
return list->count;
}
/******************************************************************** */
/* Public List functions */
/******************************************************************** */
/* returns head of the list or NULL on failure. */
OS_Keylist Keylist_Create(
void)
{
struct Keylist *list;
list = KeylistCreate();
if (list)
CheckArraySize(list);
return list;
}
/* delete specified list */
void Keylist_Delete(
OS_Keylist list)
{ /* list number to be deleted */
if (list) {
/* clean out the list */
while (list->count) {
(void) Keylist_Data_Delete_By_Index(list, 0);
}
if (list->array)
free(list->array);
free(list);
}
return;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
/* test the FIFO */
static void testKeyListFIFO(
Test * pTest)
{
OS_Keylist list;
KEY key;
int index;
char *data1 = "Joshua";
char *data2 = "Anna";
char *data3 = "Mary";
char *data;
list = Keylist_Create();
ct_test(pTest, list != NULL);
key = 0;
index = Keylist_Data_Add(list, key, data1);
ct_test(pTest, index == 0);
index = Keylist_Data_Add(list, key, data2);
ct_test(pTest, index == 0);
index = Keylist_Data_Add(list, key, data3);
ct_test(pTest, index == 0);
ct_test(pTest, Keylist_Count(list) == 3);
data = Keylist_Data_Pop(list);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
data = Keylist_Data_Pop(list);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data2) == 0);
data = Keylist_Data_Pop(list);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data3) == 0);
data = Keylist_Data_Pop(list);
ct_test(pTest, data == NULL);
data = Keylist_Data_Pop(list);
ct_test(pTest, data == NULL);
Keylist_Delete(list);
return;
}
/* test the FILO */
static void testKeyListFILO(
Test * pTest)
{
OS_Keylist list;
KEY key;
int index;
char *data1 = "Joshua";
char *data2 = "Anna";
char *data3 = "Mary";
char *data;
list = Keylist_Create();
ct_test(pTest, list != NULL);
key = 0;
index = Keylist_Data_Add(list, key, data1);
ct_test(pTest, index == 0);
index = Keylist_Data_Add(list, key, data2);
ct_test(pTest, index == 0);
index = Keylist_Data_Add(list, key, data3);
ct_test(pTest, index == 0);
ct_test(pTest, Keylist_Count(list) == 3);
data = Keylist_Data_Delete_By_Index(list, 0);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data3) == 0);
data = Keylist_Data_Delete_By_Index(list, 0);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data2) == 0);
data = Keylist_Data_Delete_By_Index(list, 0);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
data = Keylist_Data_Delete_By_Index(list, 0);
ct_test(pTest, data == NULL);
data = Keylist_Data_Delete_By_Index(list, 0);
ct_test(pTest, data == NULL);
Keylist_Delete(list);
return;
}
static void testKeyListDataKey(
Test * pTest)
{
OS_Keylist list;
KEY key;
KEY test_key;
int index;
char *data1 = "Joshua";
char *data2 = "Anna";
char *data3 = "Mary";
char *data;
list = Keylist_Create();
ct_test(pTest, list != NULL);
key = 1;
index = Keylist_Data_Add(list, key, data1);
ct_test(pTest, index == 0);
test_key = Keylist_Key(list, index);
ct_test(pTest, test_key == key);
key = 2;
index = Keylist_Data_Add(list, key, data2);
ct_test(pTest, index == 1);
test_key = Keylist_Key(list, index);
ct_test(pTest, test_key == key);
key = 3;
index = Keylist_Data_Add(list, key, data3);
ct_test(pTest, index == 2);
test_key = Keylist_Key(list, index);
ct_test(pTest, test_key == key);
ct_test(pTest, Keylist_Count(list) == 3);
/* look at the data */
key = 2;
data = Keylist_Data(list, key);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data2) == 0);
key = 1;
data = Keylist_Data(list, key);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
key = 3;
data = Keylist_Data(list, key);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data3) == 0);
/* work the data */
key = 2;
data = Keylist_Data_Delete(list, key);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data2) == 0);
data = Keylist_Data_Delete(list, key);
ct_test(pTest, data == NULL);
ct_test(pTest, Keylist_Count(list) == 2);
key = 1;
data = Keylist_Data(list, key);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
key = 3;
data = Keylist_Data(list, key);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data3) == 0);
/* cleanup */
do {
data = Keylist_Data_Pop(list);
}
while (data);
Keylist_Delete(list);
return;
}
static void testKeyListDataIndex(
Test * pTest)
{
OS_Keylist list;
KEY key;
int index;
char *data1 = "Joshua";
char *data2 = "Anna";
char *data3 = "Mary";
char *data;
list = Keylist_Create();
ct_test(pTest, list != NULL);
key = 0;
index = Keylist_Data_Add(list, key, data1);
ct_test(pTest, index == 0);
index = Keylist_Data_Add(list, key, data2);
ct_test(pTest, index == 0);
index = Keylist_Data_Add(list, key, data3);
ct_test(pTest, index == 0);
ct_test(pTest, Keylist_Count(list) == 3);
/* look at the data */
data = Keylist_Data_Index(list, 0);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data3) == 0);
data = Keylist_Data_Index(list, 1);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data2) == 0);
data = Keylist_Data_Index(list, 2);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
/* work the data */
data = Keylist_Data_Delete_By_Index(list, 1);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data2) == 0);
ct_test(pTest, Keylist_Count(list) == 2);
data = Keylist_Data_Index(list, 0);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data3) == 0);
data = Keylist_Data_Index(list, 1);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
data = Keylist_Data_Delete_By_Index(list, 1);
ct_test(pTest, data != NULL);
ct_test(pTest, strcmp(data, data1) == 0);
data = Keylist_Data_Delete_By_Index(list, 1);
ct_test(pTest, data == NULL);
/* cleanup */
do {
data = Keylist_Data_Pop(list);
}
while (data);
Keylist_Delete(list);
return;
}
/* test access of a lot of entries */
static void testKeyListLarge(
Test * pTest)
{
int data1 = 42;
int *data;
OS_Keylist list;
KEY key;
int index;
const unsigned num_keys = 1024 * 16;
list = Keylist_Create();
if (!list)
return;
for (key = 0; key < num_keys; key++) {
index = Keylist_Data_Add(list, key, &data1);
}
for (key = 0; key < num_keys; key++) {
data = Keylist_Data(list, key);
ct_test(pTest, *data == data1);
}
for (index = 0; index < num_keys; index++) {
data = Keylist_Data_Index(list, index);
ct_test(pTest, *data == data1);
}
Keylist_Delete(list);
return;
}
/* test access of a lot of entries */
void testKeyList(
Test * pTest)
{
bool rc;
/* individual tests */
rc = ct_addTestFunction(pTest, testKeyListFIFO);
assert(rc);
rc = ct_addTestFunction(pTest, testKeyListFILO);
assert(rc);
rc = ct_addTestFunction(pTest, testKeyListDataKey);
assert(rc);
rc = ct_addTestFunction(pTest, testKeyListDataIndex);
assert(rc);
rc = ct_addTestFunction(pTest, testKeyListLarge);
assert(rc);
}
#ifdef TEST_KEYLIST
int main(
void)
{
Test *pTest;
pTest = ct_create("keylist", NULL);
testKeyList(pTest);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_KEYLIST */
#endif /* TEST */
+385
View File
@@ -0,0 +1,385 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2013 Steve Karg <skarg@users.sourceforge.net>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "lighting.h"
#include "bacdcode.h"
/** @file lighting.c Manipulate BACnet lighting command values */
/**
* Encodes into bytes from the lighting-command structure
*
* @param apdu - buffer to hold the bytes
* @param value - lighting command value to encode
*
* @return number of bytes encoded, or 0 if unable to encode.
*/
int lighting_command_encode(
uint8_t * apdu,
BACNET_LIGHTING_COMMAND * data)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0; /* total length of the apdu, return value */
if (apdu) {
len = encode_context_enumerated(&apdu[apdu_len], 0,
data->operation);
apdu_len += len;
/* optional target-level */
if (data->use_target_level) {
len = encode_context_real(&apdu[apdu_len], 1,
data->target_level);
apdu_len += len;
}
/* optional ramp-rate */
if (data->use_ramp_rate) {
len = encode_context_real(&apdu[apdu_len], 2,
data->ramp_rate);
apdu_len += len;
}
/* optional step increment */
if (data->use_step_increment) {
len = encode_context_real(&apdu[apdu_len], 3,
data->step_increment);
apdu_len += len;
}
/* optional fade time */
if (data->use_fade_time) {
len = encode_context_unsigned(&apdu[apdu_len], 4,
data->fade_time);
apdu_len += len;
}
/* optional priority */
if (data->use_priority) {
len = encode_context_unsigned(&apdu[apdu_len], 5,
data->priority);
apdu_len += len;
}
}
return apdu_len;
}
/**
* Encodes into bytes from the lighting-command structure
* a context tagged chunk (opening and closing tag)
*
* @param apdu - buffer to hold the bytes
* @param tag_number - tag number to encode this chunk
* @param value - lighting command value to encode
*
* @return number of bytes encoded, or 0 if unable to encode.
*/
int lighting_command_encode_context(
uint8_t * apdu,
uint8_t tag_number,
BACNET_LIGHTING_COMMAND * value)
{
int apdu_len = 0;
apdu_len += encode_opening_tag(&apdu[apdu_len], tag_number);
apdu_len += lighting_command_encode(&apdu[apdu_len], value);
apdu_len += encode_closing_tag(&apdu[apdu_len], tag_number);
return apdu_len;
}
/**
* Decodes from bytes into the lighting-command structure
*
* @param apdu - buffer to hold the bytes
* @param apdu_max_len - number of bytes in the buffer to decode
* @param value - lighting command value to place the decoded values
*
* @return number of bytes encoded
*/
int lighting_command_decode(
uint8_t * apdu,
unsigned apdu_max_len,
BACNET_LIGHTING_COMMAND * data)
{
int len = 0;
int apdu_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint32_t unsigned_value = 0;
float real_value = 0.0;
apdu_max_len = apdu_max_len;
/* check for value pointers */
if (apdu_max_len && data) {
/* Tag 0: operation */
if (!decode_is_context_tag(&apdu[apdu_len], 0))
return BACNET_STATUS_ERROR;
len =
decode_tag_number_and_value(&apdu[apdu_len], &tag_number,
&len_value_type);
apdu_len += len;
len =
decode_enumerated(&apdu[apdu_len], len_value_type, &unsigned_value);
if (len > 0) {
data->operation = unsigned_value;
}
apdu_len += len;
/* Tag 1: target-level - OPTIONAL */
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
len =
decode_tag_number_and_value(&apdu[apdu_len], &tag_number,
&len_value_type);
apdu_len += len;
len = decode_real(&apdu[apdu_len], &real_value);
data->target_level = real_value;
apdu_len += len;
data->use_target_level = true;
} else {
data->use_target_level = false;
}
/* Tag 2: ramp-rate - OPTIONAL */
if (decode_is_context_tag(&apdu[apdu_len], 2)) {
len =
decode_tag_number_and_value(&apdu[apdu_len], &tag_number,
&len_value_type);
apdu_len += len;
len = decode_real(&apdu[apdu_len], &real_value);
data->ramp_rate = real_value;
data->use_ramp_rate = true;
} else {
data->use_ramp_rate = false;
}
/* Tag 3: step-increment - OPTIONAL */
if (decode_is_context_tag(&apdu[apdu_len], 3)) {
len =
decode_tag_number_and_value(&apdu[apdu_len], &tag_number,
&len_value_type);
apdu_len += len;
len = decode_real(&apdu[apdu_len], &real_value);
data->step_increment = real_value;
data->use_step_increment = true;
} else {
data->use_step_increment = false;
}
/* Tag 4: fade-time - OPTIONAL */
if (decode_is_context_tag(&apdu[apdu_len], 4)) {
len =
decode_tag_number_and_value(&apdu[apdu_len], &tag_number,
&len_value_type);
apdu_len += len;
len = decode_unsigned(&apdu[apdu_len], len_value_type,
&unsigned_value);
data->fade_time = unsigned_value;
data->use_fade_time = true;
} else {
data->use_fade_time = false;
}
/* Tag 5: priority - OPTIONAL */
if (decode_is_context_tag(&apdu[apdu_len], 4)) {
len =
decode_tag_number_and_value(&apdu[apdu_len], &tag_number,
&len_value_type);
apdu_len += len;
len = decode_unsigned(&apdu[apdu_len], len_value_type,
&unsigned_value);
data->priority = unsigned_value;
data->use_priority = true;
} else {
data->use_priority = false;
}
}
return len;
}
/**
* Copies one lighting-command structure to another
*
* @param dst - lighting command value target
* @param src - lighting command value source
*
* @return true if copy succeeded
*/
bool lighting_command_copy(
BACNET_LIGHTING_COMMAND * dst,
BACNET_LIGHTING_COMMAND * src)
{
bool status = false;
if (dst && src) {
dst->operation = src->operation;
dst->use_target_level = src->use_target_level;
dst->use_ramp_rate = src->use_ramp_rate;
dst->use_step_increment = src->use_step_increment;
dst->use_fade_time = src->use_fade_time;
dst->use_priority = src->use_priority;
dst->target_level = src->target_level;
dst->ramp_rate = src->ramp_rate;
dst->step_increment = src->step_increment;
dst->fade_time = src->fade_time;
dst->priority = src->priority;
status = true;
}
return status;
}
/**
* Compare one lighting-command structure to another
*
* @param dst - lighting command value target
* @param src - lighting command value source
*
* @return true if lighting-commands are the same for values in-use
*/
bool lighting_command_same(
BACNET_LIGHTING_COMMAND * dst,
BACNET_LIGHTING_COMMAND * src)
{
bool status = false;
if (dst && src) {
if ((dst->operation == src->operation) &&
(dst->use_target_level == src->use_target_level) &&
(dst->use_ramp_rate == src->use_ramp_rate) &&
(dst->use_step_increment == src->use_step_increment) &&
(dst->use_fade_time == src->use_fade_time) &&
(dst->use_priority == src->use_priority)) {
status = true;
if ((dst->use_target_level) &&
(dst->target_level != src->target_level)) {
status = false;
}
if ((dst->use_ramp_rate) &&
(dst->ramp_rate != src->ramp_rate)) {
status = false;
}
if ((dst->use_step_increment) &&
(dst->step_increment != src->step_increment)) {
status = false;
}
if ((dst->use_fade_time) &&
(dst->fade_time != src->fade_time)) {
status = false;
}
if ((dst->use_priority) &&
(dst->priority != src->priority)) {
status = false;
}
}
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testBACnetLightingCommand(
Test * pTest,
BACNET_LIGHTING_COMMAND *data)
{
bool status = false;
BACNET_LIGHTING_COMMAND test_data;
int len, apdu_len;
uint8_t apdu[MAX_APDU] = {0};
status = lighting_command_copy(&test_data, NULL);
ct_test(pTest, status == false);
status = lighting_command_copy(NULL, data);
ct_test(pTest, status == false);
status = lighting_command_copy(&test_data, data);
ct_test(pTest, status == true);
status = lighting_command_same(&test_data, data);
ct_test(pTest, status == true);
len = lighting_command_encode(apdu, data);
apdu_len = lighting_command_decode(apdu, sizeof(apdu), &test_data);
ct_test(pTest, len > 0);
ct_test(pTest, apdu_len > 0);
status = lighting_command_same(&test_data, data);
}
void testBACnetLightingCommandAll(
Test * pTest)
{
BACNET_LIGHTING_COMMAND data;
data.operation = BACNET_LIGHTS_NONE;
data.use_target_level = false;
data.use_ramp_rate = false;
data.use_step_increment = false;
data.use_fade_time = false;
data.use_priority = false;
data.target_level = 0.0;
data.ramp_rate = 100.0;
data.step_increment = 1.0;
data.fade_time = 100;
data.priority = 1;
testBACnetLightingCommand(pTest, &data);
data.operation = BACNET_LIGHTS_STOP;
data.use_target_level = true;
data.use_ramp_rate = true;
data.use_step_increment = true;
data.use_fade_time = true;
data.use_priority = true;
testBACnetLightingCommand(pTest, &data);
}
#ifdef TEST_LIGHTING_COMMAND
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Lighting Command", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBACnetLightingCommandAll);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif /* TEST */
+194
View File
@@ -0,0 +1,194 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2008 John Minack
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include "lso.h"
#include "bacdcode.h"
#include "apdu.h"
/** @file lso.c BACnet Life Safety Operation encode/decode */
int lso_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_LSO_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu && data) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION;
apdu_len = 4;
/* tag 0 - requestingProcessId */
len = encode_context_unsigned(&apdu[apdu_len], 0, data->processId);
apdu_len += len;
/* tag 1 - requestingSource */
len =
encode_context_character_string(&apdu[apdu_len], 1,
&data->requestingSrc);
apdu_len += len;
/*
Operation
*/
len = encode_context_enumerated(&apdu[apdu_len], 2, data->operation);
apdu_len += len;
/*
Object ID
*/
len =
encode_context_object_id(&apdu[apdu_len], 3,
(int) data->targetObject.type, data->targetObject.instance);
apdu_len += len;
}
return apdu_len;
}
int lso_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_LSO_DATA * data)
{
int len = 0; /* return value */
int section_length = 0; /* length returned from decoding */
uint32_t operation = 0; /* handles decoded value */
/* check for value pointers */
if (apdu_len && data) {
/* Tag 0: Object ID */
if ((section_length =
decode_context_unsigned(&apdu[len], 0,
&data->processId)) == -1) {
return -1;
}
len += section_length;
if ((section_length =
decode_context_character_string(&apdu[len], 1,
&data->requestingSrc)) == -1) {
return -1;
}
len += section_length;
if ((section_length =
decode_context_enumerated(&apdu[len], 2, &operation)) == -1) {
return -1;
}
data->operation = (BACNET_LIFE_SAFETY_OPERATION) operation;
len += section_length;
/*
** This is an optional parameter, so dont fail if it doesnt exist
*/
if (decode_is_context_tag(&apdu[len], 3)) {
if ((section_length =
decode_context_object_id(&apdu[len], 3,
&data->targetObject.type,
&data->targetObject.instance)) == -1) {
return -1;
}
len += section_length;
} else {
data->targetObject.type = 0;
data->targetObject.instance = 0;
}
return len;
}
return 0;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
#include "bacapp.h"
void testLSO(
Test * pTest)
{
uint8_t apdu[1000];
int len;
BACNET_LSO_DATA data;
BACNET_LSO_DATA rxdata;
memset(&rxdata, 0, sizeof(rxdata));
characterstring_init_ansi(&data.requestingSrc, "foobar");
data.operation = LIFE_SAFETY_OP_RESET;
data.processId = 0x1234;
data.targetObject.instance = 0x1000;
data.targetObject.type = OBJECT_BINARY_INPUT;
len = lso_encode_apdu(apdu, 100, &data);
lso_decode_service_request(&apdu[4], len, &rxdata);
ct_test(pTest, data.operation == rxdata.operation);
ct_test(pTest, data.processId == rxdata.processId);
ct_test(pTest, data.targetObject.instance == rxdata.targetObject.instance);
ct_test(pTest, data.targetObject.type == rxdata.targetObject.type);
ct_test(pTest, memcmp(data.requestingSrc.value, rxdata.requestingSrc.value,
rxdata.requestingSrc.length) == 0);
}
#ifdef TEST_LSO
int main(
int argc,
char *argv[])
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Life Safety Operation", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testLSO);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_COV */
#endif /* TEST */
+180
View File
@@ -0,0 +1,180 @@
/**
* @file
* @author Steve Karg
* @date 2008
* @section LICENSE
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to:
* The Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*
* As a special exception, if other files instantiate templates or
* use macros or inline functions from this file, or you compile
* this file and link it with other works to produce a work based
* on this file, this file does not by itself cause the resulting
* work to be covered by the GNU General Public License. However
* the source code for this file must still be made available in
* accordance with section (3) of the GNU General Public License.
*
* This exception does not invalidate any other reasons why a work
* based on this file might be covered by the GNU General Public
* License.
*
* @section DESCRIPTION
*
* Memory copy functions for deeply embedded system. The functions
* are used with a buffer, the buffer offset, the sizeof the buffer,
* and the number of bytes to copy to the buffer.
*/
#include <stddef.h>
#include "memcopy.h"
#include <string.h>
/**
* Tests to see if the number of bytes is available from an offset
* given the maximum sizeof a buffer.
*
* @param offset - offset into a buffer
* @param max - maximum sizeof a buffer
* @param len - number of bytes to add to the buffer starting at offset.
*
* @return True if there is enough space to copy len bytes.
*/
bool memcopylen(
size_t offset,
size_t max,
size_t len)
{
return ((offset + len) <= max);
}
#if defined (MEMCOPY_SIMPLE)
/**
* Copy len bytes from src to offset of dest if there is enough space
* in a buffer of max bytes.
*
* @param dest - pointer to the destination buffer
* @param src - pointer to the source buffer
* @param offset - offset into the destination buffer
* @param max - maximum sizeof the destination buffer
* @param len - number of bytes to add to the destination buffer
* starting at offset.
*
* @return returns zero if there is not enough space, or returns
* the number of bytes copied.
*/
size_t memcopy(
void *dest,
void *src,
size_t offset,
size_t len,
size_t max)
{
size_t i;
size_t copy_len = 0;
char *s1, *s2;
s1 = dest;
s2 = src;
if (memcopylen(offset, max, len)) {
for (i = 0; i < len; i++) {
s1[offset + i] = s2[i];
copy_len++;
}
}
return copy_len;
}
#else
/**
* Copy len bytes from src to offset of dest if there is enough space
* in a buffer of max bytes.
*
* @param dest - pointer to the destination buffer
* @param src - pointer to the source buffer
* @param offset - offset into the destination buffer
* @param max - maximum sizeof the destination buffer
* @param len - number of bytes to add to the destination buffer
* starting at offset.
*
* @return returns zero if there is not enough space, or returns
* the number of bytes copied.
*/
size_t memcopy(
void *dest,
void *src,
size_t offset, /* where in dest to put the data */
size_t len, /* amount of data to copy */
size_t max)
{ /* total size of destination */
if (memcopylen(offset, max, len)) {
memcpy(&((char *) dest)[offset], src, len);
return (len);
}
return 0;
}
#endif
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void test_memcopy(
Test * pTest)
{
char *data1 = "Joshua";
char *data2 = "Anna";
char buffer[480] = "";
char big_buffer[480] = "";
size_t len = 0;
len = memcopy(&buffer[0], &data1[0], 0, sizeof(data1), sizeof(buffer));
ct_test(pTest, len == sizeof(data1));
ct_test(pTest, memcmp(&buffer[0], &data1[0], len) == 0);
len = memcopy(&buffer[0], &data2[0], len, sizeof(data2), sizeof(buffer));
ct_test(pTest, len == sizeof(data2));
len =
memcopy(&buffer[0], &big_buffer[0], 1, sizeof(big_buffer),
sizeof(buffer));
ct_test(pTest, len == 0);
}
#ifdef TEST_MEM_COPY
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("Memory Copy", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, test_memcopy);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif /* TEST */
+1738
View File
File diff suppressed because it is too large Load Diff
+93
View File
@@ -0,0 +1,93 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2007 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdio.h>
#include "mstp.h"
#include "indtext.h"
#include "bacenum.h"
#include "mstptext.h"
/** @file mstptext.c Text mapping functions for BACnet MS/TP */
static INDTEXT_DATA mstp_receive_state_text[] = {
{MSTP_RECEIVE_STATE_IDLE, "IDLE"},
{MSTP_RECEIVE_STATE_PREAMBLE, "PREAMBLE"},
{MSTP_RECEIVE_STATE_HEADER, "HEADER"},
{MSTP_RECEIVE_STATE_DATA, "DATA"},
{0, NULL}
};
const char *mstptext_receive_state(
unsigned index)
{
return indtext_by_index_default(mstp_receive_state_text, index, "unknown");
}
static INDTEXT_DATA mstp_master_state_text[] = {
{MSTP_MASTER_STATE_INITIALIZE, "INITIALIZE"},
{MSTP_MASTER_STATE_IDLE, "IDLE"},
{MSTP_MASTER_STATE_USE_TOKEN, "USE_TOKEN"},
{MSTP_MASTER_STATE_WAIT_FOR_REPLY, "WAIT_FOR_REPLY"},
{MSTP_MASTER_STATE_DONE_WITH_TOKEN, "DONE_WITH_TOKEN"},
{MSTP_MASTER_STATE_PASS_TOKEN, "PASS_TOKEN"},
{MSTP_MASTER_STATE_NO_TOKEN, "NO_TOKEN"},
{MSTP_MASTER_STATE_POLL_FOR_MASTER, "POLL_FOR_MASTER"},
{MSTP_MASTER_STATE_ANSWER_DATA_REQUEST, "ANSWER_DATA_REQUEST"},
{0, NULL}
};
const char *mstptext_master_state(
unsigned index)
{
return indtext_by_index_default(mstp_master_state_text, index, "unknown");
}
static INDTEXT_DATA mstp_frame_type_text[] = {
{FRAME_TYPE_TOKEN, "TOKEN"},
{FRAME_TYPE_POLL_FOR_MASTER, "POLL_FOR_MASTER"},
{FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, "REPLY_TO_POLL_FOR_MASTER"},
{FRAME_TYPE_TEST_REQUEST, "TEST_REQUEST"},
{FRAME_TYPE_TEST_RESPONSE, "TEST_RESPONSE"},
{FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY, "BACNET_DATA_EXPECTING_REPLY"},
{FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY,
"BACNET_DATA_NOT_EXPECTING_REPLY"},
{FRAME_TYPE_REPLY_POSTPONED, "REPLY_POSTPONED"},
{0, NULL}
};
const char *mstptext_frame_type(
unsigned index)
{
return indtext_by_index_split_default(mstp_frame_type_text, index,
FRAME_TYPE_PROPRIETARY_MIN, "UNKNOWN", "PROPRIETARY");
}
+614
View File
@@ -0,0 +1,614 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdbool.h>
#include <stdint.h>
#include "bacdef.h"
#include "bacdcode.h"
#include "bacint.h"
#include "bacenum.h"
#include "bits.h"
#include "npdu.h"
#include "apdu.h"
/** @file npdu.c Encode/Decode NPDUs - Network Protocol Data Units */
/** Copy the npdu_data structure information from src to dest.
* @param dest [out] The 'to' structure
* @param src [in] The 'from' structure
*/
void npdu_copy_data(
BACNET_NPDU_DATA * dest,
BACNET_NPDU_DATA * src)
{
if (dest && src) {
dest->protocol_version = src->protocol_version;
dest->data_expecting_reply = src->data_expecting_reply;
dest->network_layer_message = src->network_layer_message;
dest->priority = src->priority;
dest->network_message_type = src->network_message_type;
dest->vendor_id = src->vendor_id;
dest->hop_count = src->hop_count;
}
return;
}
/*
The following ICI parameters are exchanged with the
various service primitives across an API:
'destination_address' (DA): the address of the device(s)
intended to receive the service primitive. Its format (device name,
network address, etc.) is a local matter. This address
may also be a multicast, local broadcast or global broadcast type.
'source_address' (SA): the address of the device from which
the service primitive was received. Its format (device name,
network address, etc.) is a local matter.
'network_priority' (NP): a four-level network priority parameter
described in 6.2.2.
'data_expecting_reply' (DER): a Boolean parameter that indicates
whether (TRUE) or not (FALSE) a reply service primitive
is expected for the service being issued.
Table 5-1. Applicability of ICI parameters for abstract service primitives
Service Primitive DA SA NP DER
CONF_SERV.request Yes No Yes Yes
CONF_SERV.indication Yes Yes Yes Yes
CONF_SERV.response Yes No Yes Yes
CONF_SERV.confirm Yes Yes Yes No
UNCONF_SERV.request Yes No Yes No
UNCONF_SERV.indication Yes Yes Yes No
REJECT.request Yes No Yes No
REJECT.indication Yes Yes Yes No
SEGMENT_ACK.request Yes No Yes No
SEGMENT_ACK.indication Yes Yes Yes No
ABORT.request Yes No Yes No
ABORT.indication Yes Yes Yes No
*/
/** Encode the NPDU portion of a message to be sent, based on the npdu_data
* and associated data.
* If this is to be a Network Layer Control Message, there are probably
* more bytes which will need to be encoded following the ones encoded here.
* The Network Layer Protocol Control Information byte is described
* in section 6.2.2 of the BACnet standard.
* @param npdu [out] Buffer which will hold the encoded NPDU header bytes.
* The size isn't given, but it must be at least 2 bytes
* for the simplest case, and should always be at least 24
* bytes to accommodate the maximal case (all fields loaded).
* @param dest [in] The routing destination information if the message must
* be routed to reach its destination.
* If dest->net and dest->len are 0, there is no
* routing destination information.
* @param src [in] The routing source information if the message was routed
* from another BACnet network.
* If src->net and src->len are 0, there is no
* routing source information.
* This src describes the original source of the message when
* it had to be routed to reach this BACnet Device.
* @param npdu_data [in] The structure which describes how the NCPI and other
* NPDU bytes should be encoded.
* @return On success, returns the number of bytes which were encoded into the
* NPDU section.
* If 0 or negative, there were problems with the data or encoding.
*/
int npdu_encode_pdu(
uint8_t * npdu,
BACNET_ADDRESS * dest,
BACNET_ADDRESS * src,
BACNET_NPDU_DATA * npdu_data)
{
int len = 0; /* return value - number of octets loaded in this function */
uint8_t i = 0; /* counter */
if (npdu && npdu_data) {
/* protocol version */
npdu[0] = npdu_data->protocol_version;
/* initialize the control octet */
npdu[1] = 0;
/* Bit 7: 1 indicates that the NSDU conveys a network layer message. */
/* Message Type field is present. */
/* 0 indicates that the NSDU contains a BACnet APDU. */
/* Message Type field is absent. */
if (npdu_data->network_layer_message)
npdu[1] |= BIT7;
/*Bit 6: Reserved. Shall be zero. */
/*Bit 5: Destination specifier where: */
/* 0 = DNET, DLEN, DADR, and Hop Count absent */
/* 1 = DNET, DLEN, and Hop Count present */
/* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */
/* DLEN > 0 specifies length of DADR field */
if (dest && dest->net)
npdu[1] |= BIT5;
/* Bit 4: Reserved. Shall be zero. */
/* Bit 3: Source specifier where: */
/* 0 = SNET, SLEN, and SADR absent */
/* 1 = SNET, SLEN, and SADR present */
/* SLEN = 0 Invalid */
/* SLEN > 0 specifies length of SADR field */
if (src && src->net && src->len)
npdu[1] |= BIT3;
/* Bit 2: The value of this bit corresponds to the */
/* data_expecting_reply parameter in the N-UNITDATA primitives. */
/* 1 indicates that a BACnet-Confirmed-Request-PDU, */
/* a segment of a BACnet-ComplexACK-PDU, */
/* or a network layer message expecting a reply is present. */
/* 0 indicates that other than a BACnet-Confirmed-Request-PDU, */
/* a segment of a BACnet-ComplexACK-PDU, */
/* or a network layer message expecting a reply is present. */
if (npdu_data->data_expecting_reply)
npdu[1] |= BIT2;
/* Bits 1,0: Network priority where: */
/* B'11' = Life Safety message */
/* B'10' = Critical Equipment message */
/* B'01' = Urgent message */
/* B'00' = Normal message */
npdu[1] |= (npdu_data->priority & 0x03);
len = 2;
if (dest && dest->net) {
len += encode_unsigned16(&npdu[len], dest->net);
npdu[len++] = dest->len;
/* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */
/* DLEN > 0 specifies length of DADR field */
if (dest->len) {
for (i = 0; i < dest->len; i++) {
npdu[len++] = dest->adr[i];
}
}
}
if (src && src->net && src->len) { /* Only insert if valid */
len += encode_unsigned16(&npdu[len], src->net);
npdu[len++] = src->len;
/* SLEN = 0 denotes broadcast MAC SADR and SADR field is absent */
/* SLEN > 0 specifies length of SADR field */
if (src->len) {
for (i = 0; i < src->len; i++) {
npdu[len++] = src->adr[i];
}
}
}
/* The Hop Count field shall be present only if the message is */
/* destined for a remote network, i.e., if DNET is present. */
/* This is a one-octet field that is initialized to a value of 0xff. */
if (dest && dest->net) {
npdu[len] = npdu_data->hop_count;
len++;
}
if (npdu_data->network_layer_message) {
npdu[len] = npdu_data->network_message_type;
len++;
/* Message Type field contains a value in the range 0x80 - 0xFF, */
/* then a Vendor ID field shall be present */
if (npdu_data->network_message_type >= 0x80)
len += encode_unsigned16(&npdu[len], npdu_data->vendor_id);
}
}
return len;
}
/* Configure the NPDU portion of the packet for an APDU */
/* This function does not handle the network messages, just APDUs. */
/* From BACnet 5.1:
Applicability of ICI parameters for abstract service primitives
Service Primitive DA SA NP DER
----------------- --- --- --- ---
CONF_SERV.request Yes No Yes Yes
CONF_SERV.indication Yes Yes Yes Yes
CONF_SERV.response Yes No Yes Yes
CONF_SERV.confirm Yes Yes Yes No
UNCONF_SERV.request Yes No Yes No
UNCONF_SERV.indication Yes Yes Yes No
REJECT.request Yes No Yes No
REJECT.indication Yes Yes Yes No
SEGMENT_ACK.request Yes No Yes No
SEGMENT_ACK.indication Yes Yes Yes No
ABORT.request Yes No Yes No
ABORT.indication Yes Yes Yes No
Where:
'destination_address' (DA): the address of the device(s) intended
to receive the service primitive. Its format (device name,
network address, etc.) is a local matter. This address may
also be a multicast, local broadcast or global broadcast type.
'source_address' (SA): the address of the device from which
the service primitive was received. Its format (device name,
network address, etc.) is a local matter.
'network_priority' (NP): a four-level network priority parameter
described in 6.2.2.
'data_expecting_reply' (DER): a Boolean parameter that indicates
whether (TRUE) or not (FALSE) a reply service primitive
is expected for the service being issued.
*/
/** Initialize an npdu_data structure to good defaults.
* The name is a misnomer, as it doesn't do any actual encoding here.
* @see npdu_encode_npdu_network if you need to set a network layer msg.
*
* @param npdu_data [out] Returns a filled-out structure with information
* provided by the other arguments and good defaults.
* @param data_expecting_reply [in] True if message should have a reply.
* @param priority [in] One of the 4 priorities defined in section 6.2.2,
* like B'11' = Life Safety message
*/
void npdu_encode_npdu_data(
BACNET_NPDU_DATA * npdu_data,
bool data_expecting_reply,
BACNET_MESSAGE_PRIORITY priority)
{
if (npdu_data) {
npdu_data->data_expecting_reply = data_expecting_reply;
npdu_data->protocol_version = BACNET_PROTOCOL_VERSION;
npdu_data->network_layer_message = false; /* false if APDU */
npdu_data->network_message_type = NETWORK_MESSAGE_INVALID; /* optional */
npdu_data->vendor_id = 0; /* optional, if net message type is > 0x80 */
npdu_data->priority = priority;
npdu_data->hop_count = HOP_COUNT_DEFAULT;
}
}
/** Decode the NPDU portion of a received message, particularly the NCPI byte.
* The Network Layer Protocol Control Information byte is described
* in section 6.2.2 of the BACnet standard.
* @param npdu [in] Buffer holding the received NPDU header bytes (must be at least 2)
* @param dest [out] Returned with routing destination information if the NPDU
* has any and if this points to non-null storage for it.
* If dest->net and dest->len are 0 on return, there is no
* routing destination information.
* @param src [out] Returned with routing source information if the NPDU
* has any and if this points to non-null storage for it.
* If src->net and src->len are 0 on return, there is no
* routing source information.
* This src describes the original source of the message when
* it had to be routed to reach this BACnet Device.
* @param npdu_data [out] Returns a filled-out structure with information
* decoded from the NCPI and other NPDU bytes.
* @return On success, returns the number of bytes which were decoded from the
* NPDU section; if this is a network layer message, there may be more
* bytes left in the NPDU; if not a network msg, the APDU follows.
* If 0 or negative, there were problems with the data or arguments.
*/
int npdu_decode(
uint8_t * npdu,
BACNET_ADDRESS * dest,
BACNET_ADDRESS * src,
BACNET_NPDU_DATA * npdu_data)
{
int len = 0; /* return value - number of octets loaded in this function */
uint8_t i = 0; /* counter */
uint16_t src_net = 0;
uint16_t dest_net = 0;
uint8_t slen = 0;
uint8_t dlen = 0;
uint8_t mac_octet = 0;
if (npdu && npdu_data) {
/* Protocol Version */
npdu_data->protocol_version = npdu[0];
/* control octet */
/* Bit 7: 1 indicates that the NSDU conveys a network layer message. */
/* Message Type field is present. */
/* 0 indicates that the NSDU contains a BACnet APDU. */
/* Message Type field is absent. */
npdu_data->network_layer_message = (npdu[1] & BIT7) ? true : false;
/*Bit 6: Reserved. Shall be zero. */
/* Bit 4: Reserved. Shall be zero. */
/* Bit 2: The value of this bit corresponds to data expecting reply */
/* parameter in the N-UNITDATA primitives. */
/* 1 indicates that a BACnet-Confirmed-Request-PDU, */
/* a segment of a BACnet-ComplexACK-PDU, */
/* or a network layer message expecting a reply is present. */
/* 0 indicates that other than a BACnet-Confirmed-Request-PDU, */
/* a segment of a BACnet-ComplexACK-PDU, */
/* or a network layer message expecting a reply is present. */
npdu_data->data_expecting_reply = (npdu[1] & BIT2) ? true : false;
/* Bits 1,0: Network priority where: */
/* B'11' = Life Safety message */
/* B'10' = Critical Equipment message */
/* B'01' = Urgent message */
/* B'00' = Normal message */
npdu_data->priority = (BACNET_MESSAGE_PRIORITY) (npdu[1] & 0x03);
/* set the offset to where the optional stuff starts */
len = 2;
/*Bit 5: Destination specifier where: */
/* 0 = DNET, DLEN, DADR, and Hop Count absent */
/* 1 = DNET, DLEN, and Hop Count present */
/* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */
/* DLEN > 0 specifies length of DADR field */
if (npdu[1] & BIT5) {
len += decode_unsigned16(&npdu[len], &dest_net);
/* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */
/* DLEN > 0 specifies length of DADR field */
dlen = npdu[len++];
if (dest) {
dest->net = dest_net;
dest->len = dlen;
}
if (dlen) {
if (dlen > MAX_MAC_LEN) {
/* address is too large could be a malformed message */
return -1;
}
for (i = 0; i < dlen; i++) {
mac_octet = npdu[len++];
if (dest)
dest->adr[i] = mac_octet;
}
}
}
/* zero out the destination address */
else if (dest) {
dest->net = 0;
dest->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
/* Bit 3: Source specifier where: */
/* 0 = SNET, SLEN, and SADR absent */
/* 1 = SNET, SLEN, and SADR present */
if (npdu[1] & BIT3) {
len += decode_unsigned16(&npdu[len], &src_net);
/* SLEN = 0 denotes broadcast MAC SADR and SADR field is absent */
/* SLEN > 0 specifies length of SADR field */
slen = npdu[len++];
if (src) {
src->net = src_net;
src->len = slen;
}
if (slen) {
if (slen > MAX_MAC_LEN) {
/* address is too large could be a malformed message */
return -1;
}
for (i = 0; i < slen; i++) {
mac_octet = npdu[len++];
if (src)
src->adr[i] = mac_octet;
}
}
} else if (src) {
/* Clear the net number, with one exception: if the receive()
* function set it to BACNET_BROADCAST_NETWORK, (eg, for
* BVLC_ORIGINAL_BROADCAST_NPDU) then don't stomp on that.
*/
if (src->net != BACNET_BROADCAST_NETWORK)
src->net = 0;
src->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->adr[i] = 0;
}
}
/* The Hop Count field shall be present only if the message is */
/* destined for a remote network, i.e., if DNET is present. */
/* This is a one-octet field that is initialized to a value of 0xff. */
if (dest_net) {
npdu_data->hop_count = npdu[len++];
} else {
npdu_data->hop_count = 0;
}
/* Indicates that the NSDU conveys a network layer message. */
/* Message Type field is present. */
if (npdu_data->network_layer_message) {
npdu_data->network_message_type =
(BACNET_NETWORK_MESSAGE_TYPE) npdu[len++];
/* Message Type field contains a value in the range 0x80 - 0xFF, */
/* then a Vendor ID field shall be present */
if (npdu_data->network_message_type >= 0x80)
len += decode_unsigned16(&npdu[len], &npdu_data->vendor_id);
} else {
/* Since npdu_data->network_layer_message is false,
* it doesn't much matter what we set here; this is safe: */
npdu_data->network_message_type = NETWORK_MESSAGE_INVALID;
}
}
return len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testNPDU2(
Test * pTest)
{
uint8_t pdu[480] = { 0 };
BACNET_ADDRESS dest = { 0 };
BACNET_ADDRESS src = { 0 };
BACNET_ADDRESS npdu_dest = { 0 };
BACNET_ADDRESS npdu_src = { 0 };
int len = 0;
bool data_expecting_reply = true;
BACNET_MESSAGE_PRIORITY priority = MESSAGE_PRIORITY_NORMAL;
BACNET_NPDU_DATA npdu_data = { 0 };
int i = 0; /* counter */
int npdu_len = 0;
bool network_layer_message = false; /* false if APDU */
BACNET_NETWORK_MESSAGE_TYPE network_message_type = 0; /* optional */
uint16_t vendor_id = 0; /* optional, if net message type is > 0x80 */
dest.mac_len = 6;
for (i = 0; i < dest.mac_len; i++) {
dest.mac[i] = i;
}
/* DNET,DLEN,DADR */
dest.net = 1;
dest.len = 6;
for (i = 0; i < dest.len; i++) {
dest.adr[i] = i * 10;
}
src.mac_len = 1;
for (i = 0; i < src.mac_len; i++) {
src.mac[i] = 0x80;
}
/* SNET,SLEN,SADR */
src.net = 2;
src.len = 1;
for (i = 0; i < src.len; i++) {
src.adr[i] = 0x40;
}
npdu_encode_npdu_data(&npdu_data, true, priority);
len = npdu_encode_pdu(&pdu[0], &dest, &src, &npdu_data);
ct_test(pTest, len != 0);
/* can we get the info back? */
npdu_len = npdu_decode(&pdu[0], &npdu_dest, &npdu_src, &npdu_data);
ct_test(pTest, npdu_len != 0);
ct_test(pTest, npdu_data.data_expecting_reply == data_expecting_reply);
ct_test(pTest, npdu_data.network_layer_message == network_layer_message);
if (npdu_data.network_layer_message) {
ct_test(pTest, npdu_data.network_message_type == network_message_type);
}
ct_test(pTest, npdu_data.vendor_id == vendor_id);
ct_test(pTest, npdu_data.priority == priority);
/* DNET,DLEN,DADR */
ct_test(pTest, npdu_dest.net == dest.net);
ct_test(pTest, npdu_dest.len == dest.len);
for (i = 0; i < dest.len; i++) {
ct_test(pTest, npdu_dest.adr[i] == dest.adr[i]);
}
/* SNET,SLEN,SADR */
ct_test(pTest, npdu_src.net == src.net);
ct_test(pTest, npdu_src.len == src.len);
for (i = 0; i < src.len; i++) {
ct_test(pTest, npdu_src.adr[i] == src.adr[i]);
}
}
void testNPDU1(
Test * pTest)
{
uint8_t pdu[480] = { 0 };
BACNET_ADDRESS dest = { 0 };
BACNET_ADDRESS src = { 0 };
BACNET_ADDRESS npdu_dest = { 0 };
BACNET_ADDRESS npdu_src = { 0 };
int len = 0;
bool data_expecting_reply = false;
BACNET_MESSAGE_PRIORITY priority = MESSAGE_PRIORITY_NORMAL;
BACNET_NPDU_DATA npdu_data = { 0 };
int i = 0; /* counter */
int npdu_len = 0;
bool network_layer_message = false; /* false if APDU */
BACNET_NETWORK_MESSAGE_TYPE network_message_type = 0; /* optional */
uint16_t vendor_id = 0; /* optional, if net message type is > 0x80 */
/* mac_len = 0 if global address */
dest.mac_len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest.mac[i] = 0;
}
/* DNET,DLEN,DADR */
dest.net = 0;
dest.len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest.adr[i] = 0;
}
src.mac_len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
src.mac[i] = 0;
}
/* SNET,SLEN,SADR */
src.net = 0;
src.len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
src.adr[i] = 0;
}
npdu_encode_npdu_data(&npdu_data, false, priority);
len = npdu_encode_pdu(&pdu[0], &dest, &src, &npdu_data);
ct_test(pTest, len != 0);
/* can we get the info back? */
npdu_len = npdu_decode(&pdu[0], &npdu_dest, &npdu_src, &npdu_data);
ct_test(pTest, npdu_len != 0);
ct_test(pTest, npdu_data.data_expecting_reply == data_expecting_reply);
ct_test(pTest, npdu_data.network_layer_message == network_layer_message);
if (npdu_data.network_layer_message) {
ct_test(pTest, npdu_data.network_message_type == network_message_type);
}
ct_test(pTest, npdu_data.vendor_id == vendor_id);
ct_test(pTest, npdu_data.priority == priority);
ct_test(pTest, npdu_dest.mac_len == src.mac_len);
ct_test(pTest, npdu_src.mac_len == dest.mac_len);
}
#ifdef TEST_NPDU
/* dummy stub for testing */
void tsm_free_invoke_id(
uint8_t invokeID)
{
(void) invokeID;
}
void iam_handler(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
(void) service_request;
(void) service_len;
(void) src;
}
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet NPDU", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testNPDU1);
assert(rc);
rc = ct_addTestFunction(pTest, testNPDU2);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_NPDU */
#endif /* TEST */
+1416
View File
File diff suppressed because it is too large Load Diff
+696
View File
@@ -0,0 +1,696 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2009 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "ptransfer.h"
/** @file ptransfer.c Encode/Decode Private Transfer data */
/* encode service */
static int pt_encode_apdu(
uint8_t * apdu,
uint16_t max_apdu,
BACNET_PRIVATE_TRANSFER_DATA * private_data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
/*
Unconfirmed/ConfirmedPrivateTransfer-Request ::= SEQUENCE {
vendorID [0] Unsigned,
serviceNumber [1] Unsigned,
serviceParameters [2] ABSTRACT-SYNTAX.&Type OPTIONAL
}
*/
/* unused parameter */
max_apdu = max_apdu;
if (apdu) {
len =
encode_context_unsigned(&apdu[apdu_len], 0,
private_data->vendorID);
apdu_len += len;
len =
encode_context_unsigned(&apdu[apdu_len], 1,
private_data->serviceNumber);
apdu_len += len;
len = encode_opening_tag(&apdu[apdu_len], 2);
apdu_len += len;
for (len = 0; len < private_data->serviceParametersLen; len++) {
apdu[apdu_len] = private_data->serviceParameters[len];
apdu_len++;
}
len = encode_closing_tag(&apdu[apdu_len], 2);
apdu_len += len;
}
return apdu_len;
}
int ptransfer_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_PRIVATE_TRANSFER_DATA * private_data)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0;
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_PRIVATE_TRANSFER;
apdu_len = 4;
len =
pt_encode_apdu(&apdu[apdu_len], (uint16_t) (MAX_APDU - apdu_len),
private_data);
apdu_len += len;
}
return apdu_len;
}
int uptransfer_encode_apdu(
uint8_t * apdu,
BACNET_PRIVATE_TRANSFER_DATA * private_data)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0;
if (apdu) {
apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST;
apdu[1] = SERVICE_UNCONFIRMED_PRIVATE_TRANSFER;
apdu_len = 2;
len =
pt_encode_apdu(&apdu[apdu_len], (uint16_t) (MAX_APDU - apdu_len),
private_data);
apdu_len += len;
}
return apdu_len;
}
/* decode the service request only */
int ptransfer_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_PRIVATE_TRANSFER_DATA * private_data)
{
int len = 0; /* return value */
int decode_len = 0; /* return value */
uint32_t unsigned_value = 0;
/* check for value pointers */
if (apdu_len && private_data) {
/* Tag 0: vendorID */
decode_len = decode_context_unsigned(&apdu[len], 0, &unsigned_value);
if (decode_len < 0) {
return -1;
}
len = decode_len;
private_data->vendorID = (uint16_t) unsigned_value;
/* Tag 1: serviceNumber */
decode_len = decode_context_unsigned(&apdu[len], 1, &unsigned_value);
if (decode_len < 0) {
return -1;
}
len += decode_len;
private_data->serviceNumber = unsigned_value;
/* Tag 2: serviceParameters */
if (decode_is_opening_tag_number(&apdu[len], 2)) {
/* a tag number of 2 is not extended so only one octet */
len++;
/* don't decode the serviceParameters here */
private_data->serviceParameters = &apdu[len];
private_data->serviceParametersLen =
(int) apdu_len - len - 1 /*closing tag */ ;
/* len includes the data and the closing tag */
len = (int) apdu_len;
} else {
return -1;
}
}
return len;
}
int ptransfer_error_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code,
BACNET_PRIVATE_TRANSFER_DATA * private_data)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0; /* length of the part of the encoding */
if (apdu) {
apdu[0] = PDU_TYPE_ERROR;
apdu[1] = invoke_id;
apdu[2] = SERVICE_CONFIRMED_PRIVATE_TRANSFER;
apdu_len = 3;
/* service parameters */
/*
ConfirmedPrivateTransfer-Error ::= SEQUENCE {
errorType [0] Error,
vendorID [1] Unsigned,
serviceNumber [2] Unsigned,
errorParameters [3] ABSTRACT-SYNTAX.&Type OPTIONAL
}
*/
len = encode_opening_tag(&apdu[apdu_len], 0);
apdu_len += len;
len = encode_application_enumerated(&apdu[apdu_len], error_class);
apdu_len += len;
len = encode_application_enumerated(&apdu[apdu_len], error_code);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], 0);
apdu_len += len;
len =
encode_context_unsigned(&apdu[apdu_len], 1,
private_data->vendorID);
apdu_len += len;
len =
encode_context_unsigned(&apdu[apdu_len], 2,
private_data->serviceNumber);
apdu_len += len;
len = encode_opening_tag(&apdu[apdu_len], 3);
apdu_len += len;
for (len = 0; len < private_data->serviceParametersLen; len++) {
apdu[apdu_len] = private_data->serviceParameters[len];
apdu_len++;
}
len = encode_closing_tag(&apdu[apdu_len], 3);
apdu_len += len;
}
return apdu_len;
}
/* decode the service request only */
int ptransfer_error_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_ERROR_CLASS * error_class,
BACNET_ERROR_CODE * error_code,
BACNET_PRIVATE_TRANSFER_DATA * private_data)
{
int len = 0; /* return value */
int decode_len = 0; /* return value */
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint32_t unsigned_value = 0;
/* check for value pointers */
if (apdu_len && private_data) {
/* Tag 0: Error */
if (decode_is_opening_tag_number(&apdu[len], 0)) {
/* a tag number of 0 is not extended so only one octet */
len++;
/* error class */
decode_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += decode_len;
if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) {
return 0;
}
decode_len =
decode_enumerated(&apdu[len], len_value_type, &unsigned_value);
len += decode_len;
if (error_class) {
*error_class = (BACNET_ERROR_CLASS) unsigned_value;
}
/* error code */
decode_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += decode_len;
if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) {
return 0;
}
decode_len =
decode_enumerated(&apdu[len], len_value_type, &unsigned_value);
len += decode_len;
if (error_code) {
*error_code = (BACNET_ERROR_CODE) unsigned_value;
}
if (decode_is_closing_tag_number(&apdu[len], 0)) {
/* a tag number of 0 is not extended so only one octet */
len++;
} else {
return 0;
}
}
/* Tag 1: vendorID */
decode_len = decode_context_unsigned(&apdu[len], 1, &unsigned_value);
if (decode_len < 0) {
return -1;
}
len += decode_len;
private_data->vendorID = (uint16_t) unsigned_value;
/* Tag 2: serviceNumber */
decode_len = decode_context_unsigned(&apdu[len], 2, &unsigned_value);
if (decode_len < 0) {
return -1;
}
len += decode_len;
private_data->serviceNumber = unsigned_value;
/* Tag 3: serviceParameters */
if (decode_is_opening_tag_number(&apdu[len], 3)) {
/* a tag number of 2 is not extended so only one octet */
len++;
/* don't decode the serviceParameters here */
private_data->serviceParameters = &apdu[len];
private_data->serviceParametersLen =
(int) apdu_len - len - 1 /*closing tag */ ;
} else {
return -1;
}
/* we could check for a closing tag of 3 */
}
return len;
}
int ptransfer_ack_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_PRIVATE_TRANSFER_DATA * private_data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */
apdu[1] = invoke_id; /* original invoke id from request */
apdu[2] = SERVICE_CONFIRMED_PRIVATE_TRANSFER; /* service choice */
apdu_len = 3;
/* service ack follows */
/*
ConfirmedPrivateTransfer-ACK ::= SEQUENCE {
vendorID [0] Unsigned,
serviceNumber [1] Unsigned,
resultBlock [2] ABSTRACT-SYNTAX.&Type OPTIONAL
}
*/
len =
encode_context_unsigned(&apdu[apdu_len], 0,
private_data->vendorID);
apdu_len += len;
len =
encode_context_unsigned(&apdu[apdu_len], 1,
private_data->serviceNumber);
apdu_len += len;
len = encode_opening_tag(&apdu[apdu_len], 2);
apdu_len += len;
for (len = 0; len < private_data->serviceParametersLen; len++) {
apdu[apdu_len] = private_data->serviceParameters[len];
apdu_len++;
}
len = encode_closing_tag(&apdu[apdu_len], 2);
apdu_len += len;
}
return apdu_len;
}
/* ptransfer_ack_decode_service_request() is the same as
ptransfer_decode_service_request */
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
#include "bacapp.h"
int ptransfer_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
BACNET_PRIVATE_TRANSFER_DATA * private_data)
{
int len = 0;
unsigned offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST)
return -1;
/* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */
/* invoke id - filled in by net layer */
*invoke_id = apdu[2];
if (apdu[3] != SERVICE_CONFIRMED_PRIVATE_TRANSFER)
return -1;
offset = 4;
if (apdu_len > offset) {
len =
ptransfer_decode_service_request(&apdu[offset], apdu_len - offset,
private_data);
}
return len;
}
int uptransfer_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
BACNET_PRIVATE_TRANSFER_DATA * private_data)
{
int len = 0;
unsigned offset = 0;
if (!apdu) {
return -1;
}
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) {
return -1;
}
if (apdu[1] != SERVICE_UNCONFIRMED_PRIVATE_TRANSFER) {
return -1;
}
offset = 2;
if (apdu_len > offset) {
len =
ptransfer_decode_service_request(&apdu[offset], apdu_len - offset,
private_data);
}
return len;
}
int ptransfer_ack_decode_apdu(
uint8_t * apdu,
int apdu_len, /* total length of the apdu */
uint8_t * invoke_id,
BACNET_PRIVATE_TRANSFER_DATA * private_data)
{
int len = 0;
int offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_COMPLEX_ACK)
return -1;
*invoke_id = apdu[1];
if (apdu[2] != SERVICE_CONFIRMED_PRIVATE_TRANSFER)
return -1;
offset = 3;
if (apdu_len > offset) {
len =
ptransfer_decode_service_request(&apdu[offset], apdu_len - offset,
private_data);
}
return len;
}
int ptransfer_error_decode_apdu(
uint8_t * apdu,
int apdu_len, /* total length of the apdu */
uint8_t * invoke_id,
BACNET_ERROR_CLASS * error_class,
BACNET_ERROR_CODE * error_code,
BACNET_PRIVATE_TRANSFER_DATA * private_data)
{
int len = 0;
int offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_ERROR)
return -1;
*invoke_id = apdu[1];
if (apdu[2] != SERVICE_CONFIRMED_PRIVATE_TRANSFER)
return -1;
offset = 3;
if (apdu_len > offset) {
len =
ptransfer_error_decode_service_request(&apdu[offset],
apdu_len - offset, error_class, error_code, private_data);
}
return len;
}
void test_Private_Transfer_Ack(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 128;
uint8_t test_invoke_id = 0;
BACNET_PRIVATE_TRANSFER_DATA private_data;
BACNET_PRIVATE_TRANSFER_DATA test_data;
uint8_t test_value[480] = { 0 };
int private_data_len = 0;
char private_data_chunk[33] = { "00112233445566778899AABBCCDDEEFF" };
BACNET_APPLICATION_DATA_VALUE data_value;
BACNET_APPLICATION_DATA_VALUE test_data_value;
bool status = false;
private_data.vendorID = BACNET_VENDOR_ID;
private_data.serviceNumber = 1;
status =
bacapp_parse_application_data(BACNET_APPLICATION_TAG_OCTET_STRING,
&private_data_chunk[0], &data_value);
ct_test(pTest, status == true);
private_data_len =
bacapp_encode_application_data(&test_value[0], &data_value);
private_data.serviceParameters = &test_value[0];
private_data.serviceParametersLen = private_data_len;
len = ptransfer_ack_encode_apdu(&apdu[0], invoke_id, &private_data);
ct_test(pTest, len != 0);
ct_test(pTest, len != -1);
apdu_len = len;
len =
ptransfer_ack_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_data);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, test_data.vendorID == private_data.vendorID);
ct_test(pTest, test_data.serviceNumber == private_data.serviceNumber);
ct_test(pTest,
test_data.serviceParametersLen == private_data.serviceParametersLen);
len =
bacapp_decode_application_data(test_data.serviceParameters,
test_data.serviceParametersLen, &test_data_value);
ct_test(pTest, bacapp_same_value(&data_value, &test_data_value) == true);
}
void test_Private_Transfer_Error(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 128;
uint8_t test_invoke_id = 0;
BACNET_ERROR_CLASS error_class = ERROR_CLASS_RESOURCES;
BACNET_ERROR_CODE error_code = ERROR_CODE_OPERATIONAL_PROBLEM;
BACNET_ERROR_CLASS test_error_class = 0;
BACNET_ERROR_CODE test_error_code = 0;
BACNET_PRIVATE_TRANSFER_DATA private_data;
BACNET_PRIVATE_TRANSFER_DATA test_data;
uint8_t test_value[480] = { 0 };
int private_data_len = 0;
char private_data_chunk[33] = { "00112233445566778899AABBCCDDEEFF" };
BACNET_APPLICATION_DATA_VALUE data_value;
BACNET_APPLICATION_DATA_VALUE test_data_value;
bool status = false;
private_data.vendorID = BACNET_VENDOR_ID;
private_data.serviceNumber = 1;
status =
bacapp_parse_application_data(BACNET_APPLICATION_TAG_OCTET_STRING,
&private_data_chunk[0], &data_value);
ct_test(pTest, status == true);
private_data_len =
bacapp_encode_application_data(&test_value[0], &data_value);
private_data.serviceParameters = &test_value[0];
private_data.serviceParametersLen = private_data_len;
len =
ptransfer_error_encode_apdu(&apdu[0], invoke_id, error_class,
error_code, &private_data);
ct_test(pTest, len != 0);
ct_test(pTest, len != -1);
apdu_len = len;
len =
ptransfer_error_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_error_class, &test_error_code, &test_data);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, test_data.vendorID == private_data.vendorID);
ct_test(pTest, test_data.serviceNumber == private_data.serviceNumber);
ct_test(pTest, test_error_class == error_class);
ct_test(pTest, test_error_code == error_code);
ct_test(pTest,
test_data.serviceParametersLen == private_data.serviceParametersLen);
len =
bacapp_decode_application_data(test_data.serviceParameters,
test_data.serviceParametersLen, &test_data_value);
ct_test(pTest, bacapp_same_value(&data_value, &test_data_value) == true);
}
void test_Private_Transfer_Request(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
uint8_t test_value[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 128;
uint8_t test_invoke_id = 0;
int private_data_len = 0;
char private_data_chunk[33] = { "00112233445566778899AABBCCDDEEFF" };
BACNET_APPLICATION_DATA_VALUE data_value = { 0 };
BACNET_APPLICATION_DATA_VALUE test_data_value = { 0 };
BACNET_PRIVATE_TRANSFER_DATA private_data = { 0 };
BACNET_PRIVATE_TRANSFER_DATA test_data = { 0 };
bool status = false;
private_data.vendorID = BACNET_VENDOR_ID;
private_data.serviceNumber = 1;
status =
bacapp_parse_application_data(BACNET_APPLICATION_TAG_OCTET_STRING,
&private_data_chunk[0], &data_value);
ct_test(pTest, status == true);
private_data_len =
bacapp_encode_application_data(&test_value[0], &data_value);
private_data.serviceParameters = &test_value[0];
private_data.serviceParametersLen = private_data_len;
len = ptransfer_encode_apdu(&apdu[0], invoke_id, &private_data);
ct_test(pTest, len != 0);
apdu_len = len;
len =
ptransfer_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data);
ct_test(pTest, len != -1);
ct_test(pTest, test_data.vendorID == private_data.vendorID);
ct_test(pTest, test_data.serviceNumber == private_data.serviceNumber);
ct_test(pTest,
test_data.serviceParametersLen == private_data.serviceParametersLen);
len =
bacapp_decode_application_data(test_data.serviceParameters,
test_data.serviceParametersLen, &test_data_value);
ct_test(pTest, bacapp_same_value(&data_value, &test_data_value) == true);
return;
}
void test_Unconfirmed_Private_Transfer_Request(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
uint8_t test_value[480] = { 0 };
int len = 0;
int apdu_len = 0;
int private_data_len = 0;
char private_data_chunk[32] = { "I Love You, Patricia!" };
BACNET_APPLICATION_DATA_VALUE data_value;
BACNET_APPLICATION_DATA_VALUE test_data_value;
BACNET_PRIVATE_TRANSFER_DATA private_data;
BACNET_PRIVATE_TRANSFER_DATA test_data;
bool status = false;
private_data.vendorID = BACNET_VENDOR_ID;
private_data.serviceNumber = 1;
status =
bacapp_parse_application_data(BACNET_APPLICATION_TAG_CHARACTER_STRING,
&private_data_chunk[0], &data_value);
ct_test(pTest, status == true);
private_data_len =
bacapp_encode_application_data(&test_value[0], &data_value);
private_data.serviceParameters = &test_value[0];
private_data.serviceParametersLen = private_data_len;
len = uptransfer_encode_apdu(&apdu[0], &private_data);
ct_test(pTest, len != 0);
apdu_len = len;
len = uptransfer_decode_apdu(&apdu[0], apdu_len, &test_data);
ct_test(pTest, len != -1);
ct_test(pTest, test_data.vendorID == private_data.vendorID);
ct_test(pTest, test_data.serviceNumber == private_data.serviceNumber);
ct_test(pTest,
test_data.serviceParametersLen == private_data.serviceParametersLen);
len =
bacapp_decode_application_data(test_data.serviceParameters,
test_data.serviceParametersLen, &test_data_value);
ct_test(pTest, bacapp_same_value(&data_value, &test_data_value) == true);
return;
}
#ifdef TEST_PRIVATE_TRANSFER
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet PrivateTransfer", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, test_Private_Transfer_Request);
assert(rc);
rc = ct_addTestFunction(pTest, test_Private_Transfer_Ack);
assert(rc);
rc = ct_addTestFunction(pTest, test_Private_Transfer_Error);
assert(rc);
rc = ct_addTestFunction(pTest, test_Unconfirmed_Private_Transfer_Request);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_READ_PROPERTY */
#endif /* TEST */
+196
View File
@@ -0,0 +1,196 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "rd.h"
/** @file rd.c Encode/Decode Reinitialize Device APDUs */
#if BACNET_SVC_RD_A
/* encode service */
int rd_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_REINITIALIZED_STATE state,
BACNET_CHARACTER_STRING * password)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_REINITIALIZE_DEVICE;
apdu_len = 4;
len = encode_context_enumerated(&apdu[apdu_len], 0, state);
apdu_len += len;
/* optional password */
if (password) {
/* FIXME: must be at least 1 character, limited to 20 characters */
len =
encode_context_character_string(&apdu[apdu_len], 1, password);
apdu_len += len;
}
}
return apdu_len;
}
#endif
/* decode the service request only */
int rd_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_REINITIALIZED_STATE * state,
BACNET_CHARACTER_STRING * password)
{
unsigned len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint32_t value = 0;
/* check for value pointers */
if (apdu_len) {
/* Tag 0: reinitializedStateOfDevice */
if (!decode_is_context_tag(&apdu[len], 0))
return -1;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += decode_enumerated(&apdu[len], len_value_type, &value);
if (state)
*state = (BACNET_REINITIALIZED_STATE) value;
/* Tag 1: password - optional */
if (len < apdu_len) {
if (!decode_is_context_tag(&apdu[len], 1))
return -1;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len +=
decode_character_string(&apdu[len], len_value_type, password);
}
}
return (int) len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
int rd_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
BACNET_REINITIALIZED_STATE * state,
BACNET_CHARACTER_STRING * password)
{
int len = 0;
unsigned offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST)
return -1;
/* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_REINITIALIZE_DEVICE)
return -1;
offset = 4;
if (apdu_len > offset) {
len =
rd_decode_service_request(&apdu[offset], apdu_len - offset, state,
password);
}
return len;
}
void test_ReinitializeDevice(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 128;
uint8_t test_invoke_id = 0;
BACNET_REINITIALIZED_STATE state;
BACNET_REINITIALIZED_STATE test_state;
BACNET_CHARACTER_STRING password;
BACNET_CHARACTER_STRING test_password;
state = BACNET_REINIT_WARMSTART;
characterstring_init_ansi(&password, "John 3:16");
len = rd_encode_apdu(&apdu[0], invoke_id, state, &password);
ct_test(pTest, len != 0);
apdu_len = len;
len =
rd_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_state,
&test_password);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, test_state == state);
ct_test(pTest, characterstring_same(&test_password, &password));
return;
}
#ifdef TEST_REINITIALIZE_DEVICE
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet ReinitializeDevice", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, test_ReinitializeDevice);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_REINITIALIZE_DEVICE */
#endif /* TEST */
+455
View File
@@ -0,0 +1,455 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2009 Peter Mc Shane
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "readrange.h"
/** @file readrange.c Encode/Decode ReadRange requests */
/*
* ReadRange-Request ::= SEQUENCE {
* objectIdentifier [0] BACnetObjectIdentifier,
* propertyIdentifier [1] BACnetPropertyIdentifier,
* propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype
* range CHOICE {
* byPosition [3] SEQUENCE {
* referenceIndex Unsigned,
* count INTEGER
* },
* -- context tag 4 is deprecated
* -- context tag 5 is deprecated
* bySequenceNumber [6] SEQUENCE {
* referenceIndex Unsigned,
* count INTEGER
* },
* byTime [7] SEQUENCE {
* referenceTime BACnetDateTime,
* count INTEGER
* }
* } OPTIONAL
* }
*/
/*****************************************************************************
* Build a ReadRange request packet. *
*****************************************************************************/
int rr_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_READ_RANGE_DATA * rrdata)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_READ_RANGE; /* service choice */
apdu_len = 4;
apdu_len +=
encode_context_object_id(&apdu[apdu_len], 0, rrdata->object_type,
rrdata->object_instance);
apdu_len +=
encode_context_enumerated(&apdu[apdu_len], 1,
rrdata->object_property);
/* optional array index */
if (rrdata->array_index != BACNET_ARRAY_ALL) {
apdu_len +=
encode_context_unsigned(&apdu[apdu_len], 2,
rrdata->array_index);
}
/* Build the appropriate (optional) range parameter based on the request type */
switch (rrdata->RequestType) {
case RR_BY_POSITION:
apdu_len += encode_opening_tag(&apdu[apdu_len], 3);
apdu_len +=
encode_application_unsigned(&apdu[apdu_len],
rrdata->Range.RefIndex);
apdu_len +=
encode_application_signed(&apdu[apdu_len], rrdata->Count);
apdu_len += encode_closing_tag(&apdu[apdu_len], 3);
break;
case RR_BY_SEQUENCE:
apdu_len += encode_opening_tag(&apdu[apdu_len], 6);
apdu_len +=
encode_application_unsigned(&apdu[apdu_len],
rrdata->Range.RefSeqNum);
apdu_len +=
encode_application_signed(&apdu[apdu_len], rrdata->Count);
apdu_len += encode_closing_tag(&apdu[apdu_len], 6);
break;
case RR_BY_TIME:
apdu_len += encode_opening_tag(&apdu[apdu_len], 7);
apdu_len +=
encode_application_date(&apdu[apdu_len],
&rrdata->Range.RefTime.date);
apdu_len +=
encode_application_time(&apdu[apdu_len],
&rrdata->Range.RefTime.time);
apdu_len +=
encode_application_signed(&apdu[apdu_len], rrdata->Count);
apdu_len += encode_closing_tag(&apdu[apdu_len], 7);
break;
case RR_READ_ALL: /* to attempt a read of the whole array or list, omit the range parameter */
break;
default:
break;
}
}
return apdu_len;
}
/*****************************************************************************
* Decode the received ReadRange request *
*****************************************************************************/
int rr_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_READ_RANGE_DATA * rrdata)
{
unsigned len = 0;
unsigned TagLen = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint16_t type = 0; /* for decoding */
uint32_t UnsignedTemp;
/* check for value pointers */
if (apdu_len && rrdata) {
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[len++], 0))
return -1;
len += decode_object_id(&apdu[len], &type, &rrdata->object_instance);
rrdata->object_type = (BACNET_OBJECT_TYPE) type;
/* Tag 1: Property ID */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number != 1)
return -1;
len += decode_enumerated(&apdu[len], len_value_type, &UnsignedTemp);
rrdata->object_property = (BACNET_PROPERTY_ID) UnsignedTemp;
rrdata->Overhead = RR_OVERHEAD; /* Start with the fixed overhead */
/* Tag 2: Optional Array Index - set to ALL if not present */
rrdata->array_index = BACNET_ARRAY_ALL; /* Assuming this is the most common outcome... */
if (len < apdu_len) {
TagLen =
(unsigned) decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number == 2) {
len += TagLen;
len +=
decode_unsigned(&apdu[len], len_value_type, &UnsignedTemp);
rrdata->array_index = UnsignedTemp;
rrdata->Overhead += RR_INDEX_OVERHEAD; /* Allow for this in the response */
}
}
/* And/or optional range selection- Tags 3, 6 and 7 */
rrdata->RequestType = RR_READ_ALL; /* Assume the worst to cut out explicit checking later */
if (len < apdu_len) {
/*
* Note: We pick up the opening tag and then decode the parameter types we recognise.
* We deal with the count and the closing tag in each case statement even though it
* might appear that we could do them after the switch statement as common elements.
* This is so that if we receive a tag we don't recognise, we don't try to decode it
* blindly and make a mess of it.
*/
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
switch (tag_number) {
case 3: /* ReadRange by position */
rrdata->RequestType = RR_BY_POSITION;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len +=
decode_unsigned(&apdu[len], len_value_type,
&rrdata->Range.RefIndex);
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len +=
decode_signed(&apdu[len], len_value_type,
&rrdata->Count);
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
break;
case 6: /* ReadRange by sequence number */
rrdata->RequestType = RR_BY_SEQUENCE;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len +=
decode_unsigned(&apdu[len], len_value_type,
&rrdata->Range.RefSeqNum);
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len +=
decode_signed(&apdu[len], len_value_type,
&rrdata->Count);
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
rrdata->Overhead += RR_1ST_SEQ_OVERHEAD; /* Allow for this in the response */
break;
case 7: /* ReadRange by time stamp */
rrdata->RequestType = RR_BY_TIME;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len +=
decode_date(&apdu[len], &rrdata->Range.RefTime.date);
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len +=
decode_bacnet_time(&apdu[len],
&rrdata->Range.RefTime.time);
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len +=
decode_signed(&apdu[len], len_value_type,
&rrdata->Count);
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
rrdata->Overhead += RR_1ST_SEQ_OVERHEAD; /* Allow for this in the response */
break;
default: /* If we don't recognise the tag then we do nothing here and try to return
* all elements of the array */
break;
}
}
}
return (int) len;
}
/*
* ReadRange-ACK ::= SEQUENCE {
* objectIdentifier [0] BACnetObjectIdentifier,
* propertyIdentifier [1] BACnetPropertyIdentifier,
* propertyArrayIndex [2] Unsigned OPTIONAL , -- used only with array datatype
* resultFlags [3] BACnetResultFlags,
* itemCount [4] Unsigned,
* itemData [5] SEQUENCE OF ABSTRACT-SYNTAX.&TYPE,
* firstSequenceNumber [6] Unsigned32 OPTIONAL -- used only if 'Item Count' > 0 and the request was either of
* -- type 'By Sequence Number' or 'By Time'
* }
*/
/*****************************************************************************
* Build a ReadRange response packet *
*****************************************************************************/
int rr_ack_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_READ_RANGE_DATA * rrdata)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */
apdu[1] = invoke_id; /* original invoke id from request */
apdu[2] = SERVICE_CONFIRMED_READ_RANGE; /* service choice */
apdu_len = 3;
/* service ack follows */
apdu_len +=
encode_context_object_id(&apdu[apdu_len], 0, rrdata->object_type,
rrdata->object_instance);
apdu_len +=
encode_context_enumerated(&apdu[apdu_len], 1,
rrdata->object_property);
/* context 2 array index is optional */
if (rrdata->array_index != BACNET_ARRAY_ALL) {
apdu_len +=
encode_context_unsigned(&apdu[apdu_len], 2,
rrdata->array_index);
}
/* Context 3 BACnet Result Flags */
apdu_len +=
encode_context_bitstring(&apdu[apdu_len], 3, &rrdata->ResultFlags);
/* Context 4 Item Count */
apdu_len +=
encode_context_unsigned(&apdu[apdu_len], 4, rrdata->ItemCount);
/* Context 5 Property list - reading the standard it looks like an empty list still
* requires an opening and closing tag as the tagged parameter is not optional
*/
apdu_len += encode_opening_tag(&apdu[apdu_len], 5);
if (rrdata->ItemCount != 0) {
for (len = 0; len < rrdata->application_data_len; len++) {
apdu[apdu_len++] = rrdata->application_data[len];
}
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 5);
if ((rrdata->ItemCount != 0) && (rrdata->RequestType != RR_BY_POSITION)
&& (rrdata->RequestType != RR_READ_ALL)) {
/* Context 6 Sequence number of first item */
apdu_len +=
encode_context_unsigned(&apdu[apdu_len], 6,
rrdata->FirstSequence);
}
}
return apdu_len;
}
/*****************************************************************************
* Decode the received ReadRange response *
*****************************************************************************/
int rr_ack_decode_service_request(
uint8_t * apdu,
int apdu_len, /* total length of the apdu */
BACNET_READ_RANGE_DATA * rrdata)
{
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
int tag_len = 0; /* length of tag decode */
int len = 0; /* total length of decodes */
int start_len;
uint16_t object = 0; /* object type */
uint32_t property = 0; /* for decoding */
uint32_t array_value = 0; /* for decoding */
/* FIXME: check apdu_len against the len during decode */
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[0], 0))
return -1;
len = 1;
len += decode_object_id(&apdu[len], &object, &rrdata->object_instance);
rrdata->object_type = (BACNET_OBJECT_TYPE) object;
/* Tag 1: Property ID */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number != 1)
return -1;
len += decode_enumerated(&apdu[len], len_value_type, &property);
rrdata->object_property = (BACNET_PROPERTY_ID) property;
/* Tag 2: Optional Array Index */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number == 2) {
len += tag_len;
len += decode_unsigned(&apdu[len], len_value_type, &array_value);
rrdata->array_index = array_value;
} else
rrdata->array_index = BACNET_ARRAY_ALL;
/* Tag 3: Result Flags */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number != 3)
return -1;
len += decode_bitstring(&apdu[len], len_value_type, &rrdata->ResultFlags);
/* Tag 4: Item count */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number != 4)
return -1;
len += decode_unsigned(&apdu[len], len_value_type, &rrdata->ItemCount);
if (decode_is_opening_tag_number(&apdu[len], 5)) {
len++; /* a tag number of 5 is not extended so only one octet */
/* Setup the start position and length of the data returned from the request
* don't decode the application tag number or its data here */
rrdata->application_data = &apdu[len];
start_len = len;
while (len < apdu_len) {
if (IS_CONTEXT_SPECIFIC(apdu[len]) &&
(decode_is_closing_tag_number(&apdu[len], 5))) {
rrdata->application_data_len = len - start_len;
len++; /* Step over single byte closing tag */
break;
} else {
/* Don't care about tag number, just skipping over anyway */
len +=
decode_tag_number_and_value(&apdu[len], NULL,
&len_value_type);
len += len_value_type; /* Skip over data value as well */
if (len >= apdu_len) /* APDU is exhausted so we have failed to find closing tag */
return (-1);
}
}
} else {
return -1;
}
if (len < apdu_len) { /* Still something left to look at? */
/* Tag 6: Item count */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number != 6)
return -1;
len +=
decode_unsigned(&apdu[len], len_value_type,
&rrdata->FirstSequence);
}
len = apdu_len; /* There should be nothing left to see here */
return len;
}
/* FIXME: Currently does not have test framework */
+240
View File
@@ -0,0 +1,240 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "reject.h"
/** @file reject.c Encode/Decode Reject APDUs */
/* Helper function to avoid needing additional entries in service data structures
* when passing back reject status.
* Convert from error code to reject code.
* Anything not defined gets converted to REJECT_REASON_OTHER.
* Will need reworking if it is required to return proprietary reject codes.
*/
BACNET_REJECT_REASON reject_convert_error_code(
BACNET_ERROR_CODE error_code)
{
BACNET_REJECT_REASON reject_code = REJECT_REASON_OTHER;
switch (error_code) {
case ERROR_CODE_REJECT_BUFFER_OVERFLOW:
reject_code = REJECT_REASON_BUFFER_OVERFLOW;
break;
case ERROR_CODE_REJECT_INCONSISTENT_PARAMETERS:
reject_code = REJECT_REASON_INCONSISTENT_PARAMETERS;
break;
case ERROR_CODE_REJECT_INVALID_PARAMETER_DATA_TYPE:
reject_code = REJECT_REASON_INVALID_PARAMETER_DATA_TYPE;
break;
case ERROR_CODE_REJECT_INVALID_TAG:
reject_code = REJECT_REASON_INVALID_TAG;
break;
case ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER:
reject_code = REJECT_REASON_MISSING_REQUIRED_PARAMETER;
break;
case ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE:
reject_code = REJECT_REASON_PARAMETER_OUT_OF_RANGE;
break;
case ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS:
reject_code = REJECT_REASON_TOO_MANY_ARGUMENTS;
break;
case ERROR_CODE_REJECT_UNDEFINED_ENUMERATION:
reject_code = REJECT_REASON_UNDEFINED_ENUMERATION;
break;
case ERROR_CODE_REJECT_UNRECOGNIZED_SERVICE:
reject_code = REJECT_REASON_UNRECOGNIZED_SERVICE;
break;
case ERROR_CODE_REJECT_PROPRIETARY:
reject_code = REJECT_REASON_PROPRIETARY_FIRST;
break;
case ERROR_CODE_REJECT_OTHER:
default:
reject_code = REJECT_REASON_OTHER;
break;
}
return (reject_code);
}
/* encode service */
int reject_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
uint8_t reject_reason)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_REJECT;
apdu[1] = invoke_id;
apdu[2] = reject_reason;
apdu_len = 3;
}
return apdu_len;
}
#if !BACNET_SVC_SERVER
/* decode the service request only */
int reject_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
uint8_t * reject_reason)
{
int len = 0;
if (apdu_len) {
if (invoke_id)
*invoke_id = apdu[0];
if (reject_reason)
*reject_reason = apdu[1];
}
return len;
}
#endif
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
/* decode the whole APDU - mainly used for unit testing */
int reject_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
uint8_t * reject_reason)
{
int len = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu_len) {
if (apdu[0] != PDU_TYPE_REJECT)
return -1;
if (apdu_len > 1) {
len =
reject_decode_service_request(&apdu[1], apdu_len - 1,
invoke_id, reject_reason);
}
}
return len;
}
void testReject(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 0;
uint8_t reject_reason = 0;
uint8_t test_invoke_id = 0;
uint8_t test_reject_reason = 0;
len = reject_encode_apdu(&apdu[0], invoke_id, reject_reason);
ct_test(pTest, len != 0);
apdu_len = len;
len =
reject_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_reject_reason);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, test_reject_reason == reject_reason);
/* change type to get negative response */
apdu[0] = PDU_TYPE_ABORT;
len =
reject_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_reject_reason);
ct_test(pTest, len == -1);
/* test NULL APDU */
len =
reject_decode_apdu(NULL, apdu_len, &test_invoke_id,
&test_reject_reason);
ct_test(pTest, len == -1);
/* force a zero length */
len =
reject_decode_apdu(&apdu[0], 0, &test_invoke_id, &test_reject_reason);
ct_test(pTest, len == 0);
/* check them all... */
for (invoke_id = 0; invoke_id < 255; invoke_id++) {
for (reject_reason = 0; reject_reason < 255; reject_reason++) {
len = reject_encode_apdu(&apdu[0], invoke_id, reject_reason);
apdu_len = len;
ct_test(pTest, len != 0);
len =
reject_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&test_reject_reason);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, test_reject_reason == reject_reason);
}
}
}
#ifdef TEST_REJECT
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Reject", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testReject);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_REJECT */
#endif /* TEST */
+832
View File
@@ -0,0 +1,832 @@
/**
* @file
* @author Steve Karg
* @date 2004
* @brief Generic ring buffer library for deeply embedded system.
*
* @section LICENSE
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to:
* The Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*
* As a special exception, if other files instantiate templates or
* use macros or inline functions from this file, or you compile
* this file and link it with other works to produce a work based
* on this file, this file does not by itself cause the resulting
* work to be covered by the GNU General Public License. However
* the source code for this file must still be made available in
* accordance with section (3) of the GNU General Public License.
*
* This exception does not invalidate any other reasons why a work
* based on this file might be covered by the GNU General Public
* License.
*
* @section DESCRIPTION
*
* Generic ring buffer library for deeply embedded system.
* It uses a data store whose size is a power of 2 (8, 16, 32, 64, ...)
* and doesn't waste any data bytes. It has very low overhead, and
* utilizes modulo for indexing the data in the data store.
* It uses separate variables for consumer and producer so it can
* be used in multithreaded environment.
*
* See the unit tests for usage examples.
*
*/
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "ringbuf.h"
/**
* Returns the number of elements in the ring buffer
*
* @param b - pointer to RING_BUFFER structure
* @return Number of elements in the ring buffer
*/
unsigned Ringbuf_Count(RING_BUFFER const *b)
{
unsigned head, tail; /* used to avoid volatile decision */
if (b) {
head = b->head;
tail = b->tail;
return head - tail;
}
return 0;
}
/**
* Returns the empty/full status of the ring buffer
*
* @param b - pointer to RING_BUFFER structure
* @return true if the ring buffer is full, false if it is not.
*/
bool Ringbuf_Full(RING_BUFFER const *b)
{
return (b ? (Ringbuf_Count(b) == b->element_count) : true);
}
/**
* Returns the empty/full status of the ring buffer
*
* @param b - pointer to RING_BUFFER structure
* @return true if the ring buffer is empty, false if it is not.
*/
bool Ringbuf_Empty(RING_BUFFER const *b)
{
return (b ? (Ringbuf_Count(b) == 0) : true);
}
/**
* Updates the depth tracking in the ring buffer
*
* @param b - pointer to RING_BUFFER structure
*/
static void Ringbuf_Depth_Update(RING_BUFFER *b)
{
unsigned count;
if (b) {
count = Ringbuf_Count(b);
if (count > b->depth) {
b->depth = count;
}
}
}
/**
* Updates the depth tracking in the ring buffer
*
* @param b - pointer to RING_BUFFER structure
* @return largest number of items that have been in the ring buffer
*/
unsigned Ringbuf_Depth(RING_BUFFER const *b)
{
unsigned depth = 0;
if (b) {
depth = b->depth;
}
return depth;
}
/**
* Resets the depth tracking in the ring buffer
*
* @param b - pointer to RING_BUFFER structure
* @return largest number of items that have been in the ring buffer
*/
unsigned Ringbuf_Depth_Reset(RING_BUFFER *b)
{
unsigned depth = 0;
if (b) {
depth = b->depth;
b->depth = 0;
}
return depth;
}
/**
* Gets the capacity of the ring buffer (number of possible elements)
*
* @param b - pointer to RING_BUFFER structure
* @return largest number of items that have been in the ring buffer
*/
unsigned Ringbuf_Size(RING_BUFFER const *b)
{
unsigned count = 0;
if (b) {
count = b->element_count;
}
return count;
}
/**
* Looks at the data from the head of the list without removing it
*
* @param b - pointer to RING_BUFFER structure
* @return pointer to the data, or NULL if nothing in the list
*/
volatile uint8_t *Ringbuf_Peek(RING_BUFFER const *b)
{
volatile uint8_t *data_element = NULL; /* return value */
if (!Ringbuf_Empty(b)) {
data_element = b->buffer;
data_element += ((b->tail % b->element_count) * b->element_size);
}
return data_element;
}
/**
* Looks at the data from the next element of the list without removing it
*
* @param b - pointer to RING_BUFFER structure
* @param data_element - find the next element from this one
* @return pointer to the data, or NULL if nothing in the list
*/
volatile uint8_t *Ringbuf_Peek_Next(RING_BUFFER const *b,
uint8_t * data_element)
{
unsigned index; /* list index */
volatile uint8_t *this_element;
volatile uint8_t *next_element = NULL; /* return value */
if (!Ringbuf_Empty(b) && data_element != NULL) {
/* Use (b->head-1) here to avoid walking off end of ring */
for (index = b->tail; index < b->head-1; index++) {
/* Find the specified data_element */
this_element = b->buffer +
((index % b->element_count) * b->element_size);
if (data_element == this_element) {
/* Found the current element, get the next one on the list */
next_element = b->buffer +
(((index+1) % b->element_count) * b->element_size);
break;
}
}
}
return next_element;
}
/**
* Copy the data from the front of the list, and removes it
*
* @param b - pointer to RING_BUFFER structure
* @param data_element - element of data that is loaded with data from ring
* @return true if data was copied, false if list is empty
*/
bool Ringbuf_Pop(RING_BUFFER * b,
uint8_t * data_element)
{
bool status = false; /* return value */
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
unsigned i; /* loop counter */
if (!Ringbuf_Empty(b)) {
ring_data = b->buffer;
ring_data += ((b->tail % b->element_count) * b->element_size);
if (data_element) {
for (i = 0; i < b->element_size; i++) {
data_element[i] = ring_data[i];
}
}
b->tail++;
status = true;
}
return status;
}
/**
* Copy the data from the specified element, and removes it and moves other
* elements up the list
*
* @param b - pointer to RING_BUFFER structure
* @param this_element - element to find
* @param data_element - element of data that is loaded with data from ring
* @return true if data was copied, false if list is empty
*/
bool Ringbuf_Pop_Element(RING_BUFFER * b,
uint8_t * this_element,
uint8_t * data_element)
{
bool status = false; /* return value */
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
volatile uint8_t *prev_data;
unsigned index; /* list index */
unsigned this_index = b->head; /* index of element to remove */
unsigned i; /* loop counter */
if (!Ringbuf_Empty(b) && this_element != NULL) {
for (index = b->tail; index < b->head; index++) {
/* Find the specified data_element */
ring_data = b->buffer +
((index % b->element_count) * b->element_size);
if (this_element == ring_data) {
/* Found the specified element, copy the data if required */
this_index = index;
if (data_element) {
for (i = 0; i < b->element_size; i++) {
data_element[i] = ring_data[i];
}
}
break;
}
}
if (this_index < b->head) {
/* Found a match, move elements up the list to fill the gap */
for ( index = this_index; index > b->tail; index--) {
/* Get pointers to current and previous data_elements */
ring_data = b->buffer +
((index % b->element_count) * b->element_size);
prev_data = b->buffer +
(((index-1) % b->element_count) * b->element_size);
for (i = 0; i < b->element_size; i++) {
ring_data[i] = prev_data[i];
}
}
}
b->tail++;
status = true;
}
return status;
}
/**
* Adds an element of data to the ring buffer
*
* @param b - pointer to RING_BUFFER structure
* @param data_element - one element that is copied to the ring buffer
* @return true on succesful add, false if not added
*/
bool Ringbuf_Put(RING_BUFFER * b,
uint8_t * data_element)
{
bool status = false; /* return value */
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
unsigned i; /* loop counter */
if (b && data_element) {
/* limit the amount of elements that we accept */
if (!Ringbuf_Full(b)) {
ring_data = b->buffer;
ring_data += ((b->head % b->element_count) * b->element_size);
for (i = 0; i < b->element_size; i++) {
ring_data[i] = data_element[i];
}
b->head++;
Ringbuf_Depth_Update(b);
status = true;
}
}
return status;
}
/**
* Adds an element of data to the front of the ring buffer
*
* Note that this function moves the tail on add instead of head,
* so this function cannot be used if you are keeping producer and
* consumer as separate processes (i.e. interrupt handlers)
*
* @param b - pointer to RING_BUFFER structure
* @param data_element - one element to copy to the front of the ring
* @return true on succesful add, false if not added
*/
bool Ringbuf_Put_Front(RING_BUFFER * b,
uint8_t * data_element)
{
bool status = false; /* return value */
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
unsigned i = 0; /* loop counter */
if (b && data_element) {
/* limit the amount of elements that we accept */
if (!Ringbuf_Full(b)) {
b->tail--;
ring_data = b->buffer;
ring_data += ((b->tail % b->element_count) * b->element_size);
/* copy the data to the ring data element */
for (i = 0; i < b->element_size; i++) {
ring_data[i] = data_element[i];
}
Ringbuf_Depth_Update(b);
status = true;
}
}
return status;
}
/**
* Gets a pointer to the next free data element of the buffer
* without adding it to the ring.
*
* @param b - pointer to RING_BUFFER structure
* @return pointer to the next data element, or NULL if the list is full
*/
volatile uint8_t *Ringbuf_Data_Peek(RING_BUFFER * b)
{
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
if (b) {
/* limit the amount of elements that we accept */
if (!Ringbuf_Full(b)) {
ring_data = b->buffer;
ring_data += ((b->head % b->element_count) * b->element_size);
}
}
return ring_data;
}
/**
* Adds the previously peeked element of data to the end of the
* ring buffer.
*
* @param b - pointer to RING_BUFFER structure
* @param data_element - pointer to the peeked data element
* @return true if the buffer has space and the data element points to the
* same memory previously peeked.
*/
bool Ringbuf_Data_Put(RING_BUFFER * b, volatile uint8_t *data_element)
{
bool status = false;
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
if (b) {
/* limit the amount of elements that we accept */
if (!Ringbuf_Full(b)) {
ring_data = b->buffer;
ring_data += ((b->head % b->element_count) * b->element_size);
if (ring_data == data_element) {
/* same chunk of memory - okay to signal the head */
b->head++;
Ringbuf_Depth_Update(b);
status = true;
}
}
}
return status;
}
/**
* Test that the parameter is a power of two.
*
* @param x unsigned integer value to be tested
*
* @return true if the parameter is a power of 2
*/
static bool isPowerOfTwo (unsigned int x)
{
/* First x in the below expression is for the case when x is 0 */
return x && (!(x&(x-1)));
}
/**
* Configures the ring buffer data buffer. Note that the element_count
* parameter must be a power of two.
*
* @param b - pointer to RING_BUFFER structure
* @param buffer - pointer to a data buffer that is used to store the ring data
* @param element_size - size of one element in the data block
* @param element_count - number elements in the data block
*
* @return true if ring buffer was initialized
*/
bool Ringbuf_Init(RING_BUFFER * b,
volatile uint8_t * buffer,
unsigned element_size,
unsigned element_count)
{
bool status = false;
if (b && isPowerOfTwo(element_count)) {
b->head = 0;
b->tail = 0;
b->buffer = buffer;
b->element_size = element_size;
b->element_count = element_count;
/* tuning diagnostics */
b->depth = 0;
status = true;
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include <limits.h>
#include "ctest.h"
/**
* Unit Test for the ring buffer
*
* @param pTest - test tracking pointer
* @param test_buffer - pointer to RING_BUFFER structure
* @param data_element - one data element
* @param element_size - size of one data element
* @param element_count - number of data elements in the store
*/
static void testRingAroundBuffer(Test * pTest,
RING_BUFFER * test_buffer,
uint8_t * data_element,
unsigned element_size,
unsigned element_count)
{
volatile uint8_t *test_data;
unsigned index;
unsigned data_index;
unsigned count;
uint8_t value;
bool status;
ct_test(pTest, Ringbuf_Empty(test_buffer));
/* test the ring around the buffer */
for (index = 0; index < element_count; index++) {
for (count = 1; count < 4; count++) {
value = (index * count)%255;
for (data_index = 0; data_index < element_size; data_index++) {
data_element[data_index] = value;
}
status = Ringbuf_Put(test_buffer, data_element);
ct_test(pTest, status == true);
ct_test(pTest, Ringbuf_Count(test_buffer) == count);
}
for (count = 1; count < 4; count++) {
value = (index * count)%255;
test_data = Ringbuf_Peek(test_buffer);
ct_test(pTest, test_data);
if (test_data) {
for (data_index = 0; data_index < element_size; data_index++) {
ct_test(pTest, test_data[data_index] == value);
}
}
status = Ringbuf_Pop(test_buffer, NULL);
ct_test(pTest, status == true);
}
}
ct_test(pTest, Ringbuf_Empty(test_buffer));
}
/**
* Unit Test for the ring buffer
*
* @param pTest - test tracking pointer
* @param data_store - buffer to store elements
* @param data_element - one data element
* @param element_size - size of one data element
* @param element_count - number of data elements in the store
*/
static bool testRingBuf(Test * pTest,
uint8_t * data_store,
uint8_t * data_element,
unsigned element_size,
unsigned element_count)
{
RING_BUFFER test_buffer;
volatile uint8_t *test_data;
unsigned index;
unsigned data_index;
bool status;
status = Ringbuf_Init(&test_buffer, data_store,
element_size, element_count);
if (!status) {
return false;
}
ct_test(pTest, Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 0);
for (data_index = 0; data_index < element_size; data_index++) {
data_element[data_index] = data_index;
}
status = Ringbuf_Put(&test_buffer, data_element);
ct_test(pTest, status == true);
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 1);
test_data = Ringbuf_Peek(&test_buffer);
for (data_index = 0; data_index < element_size; data_index++) {
ct_test(pTest, test_data[data_index] == data_element[data_index]);
}
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
(void) Ringbuf_Pop(&test_buffer, NULL);
ct_test(pTest, Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 1);
/* fill to max */
for (index = 0; index < element_count; index++) {
for (data_index = 0; data_index < element_size; data_index++) {
data_element[data_index] = index;
}
status = Ringbuf_Put(&test_buffer, data_element);
ct_test(pTest, status == true);
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == (index+1));
}
ct_test(pTest, Ringbuf_Depth(&test_buffer) == element_count);
/* verify actions on full buffer */
for (index = 0; index < element_count; index++) {
for (data_index = 0; data_index < element_size; data_index++) {
data_element[data_index] = index;
}
status = Ringbuf_Put(&test_buffer, data_element);
ct_test(pTest, status == false);
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == element_count);
}
/* check buffer full */
for (index = 0; index < element_count; index++) {
test_data = Ringbuf_Peek(&test_buffer);
ct_test(pTest, test_data);
if (test_data) {
for (data_index = 0; data_index < element_size; data_index++) {
ct_test(pTest, test_data[data_index] == index);
}
}
(void) Ringbuf_Pop(&test_buffer, NULL);
}
ct_test(pTest, Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == element_count);
Ringbuf_Depth_Reset(&test_buffer);
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 0);
testRingAroundBuffer(pTest, &test_buffer, data_element, element_size,
element_count);
/* adjust the internal index of Ringbuf to test unsigned wrapping */
test_buffer.head = UINT_MAX - 1;
test_buffer.tail = UINT_MAX - 1;
testRingAroundBuffer(pTest, &test_buffer, data_element, element_size,
element_count);
return true;
}
/**
* Unit Test for the ring buffer with 16 data elements
*
* @param pTest - test tracking pointer
*/
void testRingBufSizeSmall(Test * pTest)
{
bool status;
uint8_t data_element[5];
uint8_t data_store[sizeof(data_element) * NEXT_POWER_OF_2(16)];
status = testRingBuf(pTest, data_store, data_element,
sizeof(data_element),
sizeof(data_store) / sizeof(data_element));
ct_test(pTest, status);
}
/**
* Unit Test for the ring buffer with 32 data elements
*
* @param pTest - test tracking pointer
*/
void testRingBufSizeLarge(Test * pTest)
{
bool status;
uint8_t data_element[16];
uint8_t data_store[sizeof(data_element) * NEXT_POWER_OF_2(99)];
status = testRingBuf(pTest, data_store, data_element,
sizeof(data_element),
sizeof(data_store) / sizeof(data_element));
ct_test(pTest, status);
}
/**
* Unit Test for the ring buffer with 32 data elements
*
* @param pTest - test tracking pointer
*/
void testRingBufSizeInvalid(Test * pTest)
{
bool status;
uint8_t data_element[16];
uint8_t data_store[sizeof(data_element) * 99];
status = testRingBuf(pTest, data_store, data_element,
sizeof(data_element),
sizeof(data_store) / sizeof(data_element));
ct_test(pTest, status==false);
}
void testRingBufPowerOfTwo(Test * pTest)
{
ct_test(pTest, NEXT_POWER_OF_2(3)==4);
ct_test(pTest, NEXT_POWER_OF_2(100)==128);
ct_test(pTest, NEXT_POWER_OF_2(127)==128);
ct_test(pTest, NEXT_POWER_OF_2(128)==128);
ct_test(pTest, NEXT_POWER_OF_2(129)==256);
ct_test(pTest, NEXT_POWER_OF_2(300)==512);
ct_test(pTest, NEXT_POWER_OF_2(500)==512);
}
/**
* Unit Test for the ring buffer peek/pop next element
*
* @param pTest - test tracking pointer
* @param data_store - buffer to store elements
* @param data_element - one data element
* @param element_size - size of one data element
* @param element_count - number of data elements in the store
*/
static bool testRingBufNextElement(Test * pTest,
uint8_t * data_store,
uint8_t * data_element,
unsigned element_size,
unsigned element_count)
{
RING_BUFFER test_buffer;
volatile uint8_t *test_data;
unsigned index;
unsigned data_index;
bool status;
status = Ringbuf_Init(&test_buffer, data_store,
element_size, element_count);
if (!status) {
return false;
}
ct_test(pTest, Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 0);
for (data_index = 0; data_index < element_size; data_index++) {
data_element[data_index] = data_index;
}
status = Ringbuf_Put(&test_buffer, data_element);
ct_test(pTest, status == true);
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 1);
test_data = Ringbuf_Peek(&test_buffer);
for (data_index = 0; data_index < element_size; data_index++) {
ct_test(pTest, test_data[data_index] == data_element[data_index]);
}
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
(void) Ringbuf_Pop(&test_buffer, NULL);
ct_test(pTest, Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 1);
/* fill to max */
for (index = 0; index < element_count; index++) {
for (data_index = 0; data_index < element_size; data_index++) {
data_element[data_index] = index;
}
status = Ringbuf_Put(&test_buffer, data_element);
ct_test(pTest, status == true);
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
ct_test(pTest, Ringbuf_Depth(&test_buffer) == (index+1));
}
ct_test(pTest, Ringbuf_Depth(&test_buffer) == element_count);
ct_test(pTest, Ringbuf_Count(&test_buffer) == element_count);
/* Walk through ring buffer */
test_data = Ringbuf_Peek(&test_buffer);
ct_test(pTest, test_data);
for (index = 1; index < element_count; index++) {
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
ct_test(pTest, test_data);
if (test_data) {
for (data_index = 0; data_index < element_size; data_index++) {
ct_test(pTest, test_data[data_index] == index);
}
}
}
ct_test(pTest, Ringbuf_Count(&test_buffer) == element_count);
/* Try to walk off end of buffer - should return NULL */
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
ct_test(pTest, (test_data == NULL));
/* Walk through ring buffer and pop alternate elements */
test_data = Ringbuf_Peek(&test_buffer);
ct_test(pTest, test_data);
for (index = 1; index < element_count/2; index++) {
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
ct_test(pTest, test_data);
(void) Ringbuf_Pop_Element(&test_buffer, (uint8_t *)test_data, NULL);
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
}
ct_test(pTest, Ringbuf_Count(&test_buffer) == element_count/2+1);
/* Walk through ring buffer and check data */
test_data = Ringbuf_Peek(&test_buffer);
ct_test(pTest, test_data);
for (index = 0; index < element_count/2; index++) {
if (test_data) {
for (data_index = 0; data_index < element_size; data_index++) {
ct_test(pTest, test_data[data_index] == index*2);
}
}
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
ct_test(pTest, test_data);
}
ct_test(pTest, Ringbuf_Count(&test_buffer) == element_count/2+1);
return true;
}
/**
* Unit Test for the ring buffer with 16 data elements
*
* @param pTest - test tracking pointer
*/
void testRingBufNextElementSizeSmall(Test * pTest)
{
bool status;
uint8_t data_element[5];
uint8_t data_store[sizeof(data_element) * NEXT_POWER_OF_2(16)];
status = testRingBufNextElement(pTest, data_store, data_element,
sizeof(data_element),
sizeof(data_store) / sizeof(data_element));
ct_test(pTest, status);
}
#ifdef TEST_RING_BUFFER
/**
* Main program entry for Unit Test
*
* @return returns 0 on success, and non-zero on fail.
*/
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("Ring Buffer", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testRingBufPowerOfTwo);
assert(rc);
rc = ct_addTestFunction(pTest, testRingBufSizeSmall);
assert(rc);
rc = ct_addTestFunction(pTest, testRingBufSizeLarge);
assert(rc);
rc = ct_addTestFunction(pTest, testRingBufSizeInvalid);
assert(rc);
rc = ct_addTestFunction(pTest, testRingBufNextElementSizeSmall);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif
+455
View File
@@ -0,0 +1,455 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "rp.h"
/** @file rp.c Encode/Decode Read Property and RP ACKs */
#if BACNET_SVC_RP_A
/* encode service */
int rp_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_READ_PROPERTY_DATA * rpdata)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_READ_PROPERTY; /* service choice */
apdu_len = 4;
if (rpdata->object_type <= BACNET_MAX_OBJECT) {
/* check bounds so that we could create malformed
messages for testing */
len =
encode_context_object_id(&apdu[apdu_len], 0,
rpdata->object_type, rpdata->object_instance);
apdu_len += len;
}
if (rpdata->object_property <= MAX_BACNET_PROPERTY_ID) {
/* check bounds so that we could create malformed
messages for testing */
len =
encode_context_enumerated(&apdu[apdu_len], 1,
rpdata->object_property);
apdu_len += len;
}
/* optional array index */
if (rpdata->array_index != BACNET_ARRAY_ALL) {
len =
encode_context_unsigned(&apdu[apdu_len], 2,
rpdata->array_index);
apdu_len += len;
}
}
return apdu_len;
}
#endif
/* decode the service request only */
int rp_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_READ_PROPERTY_DATA * rpdata)
{
unsigned len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint16_t type = 0; /* for decoding */
uint32_t property = 0; /* for decoding */
uint32_t array_value = 0; /* for decoding */
/* check for value pointers */
if (rpdata) {
/* Must have at least 2 tags, an object id and a property identifier
* of at least 1 byte in length to have any chance of parsing */
if (apdu_len < 7) {
rpdata->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
return BACNET_STATUS_REJECT;
}
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[len++], 0)) {
rpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
len += decode_object_id(&apdu[len], &type, &rpdata->object_instance);
rpdata->object_type = (BACNET_OBJECT_TYPE) type;
/* Tag 1: Property ID */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number != 1) {
rpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
len += decode_enumerated(&apdu[len], len_value_type, &property);
rpdata->object_property = (BACNET_PROPERTY_ID) property;
/* Tag 2: Optional Array Index */
if (len < apdu_len) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if ((tag_number == 2) && (len < apdu_len)) {
len +=
decode_unsigned(&apdu[len], len_value_type, &array_value);
rpdata->array_index = array_value;
} else {
rpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
} else
rpdata->array_index = BACNET_ARRAY_ALL;
}
if (len < apdu_len) {
/* If something left over now, we have an invalid request */
if (rpdata) {
rpdata->error_code = ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS;
}
return BACNET_STATUS_REJECT;
}
return (int) len;
}
/* alternate method to encode the ack without extra buffer */
int rp_ack_encode_apdu_init(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_READ_PROPERTY_DATA * rpdata)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */
apdu[1] = invoke_id; /* original invoke id from request */
apdu[2] = SERVICE_CONFIRMED_READ_PROPERTY; /* service choice */
apdu_len = 3;
/* service ack follows */
len =
encode_context_object_id(&apdu[apdu_len], 0, rpdata->object_type,
rpdata->object_instance);
apdu_len += len;
len =
encode_context_enumerated(&apdu[apdu_len], 1,
rpdata->object_property);
apdu_len += len;
/* context 2 array index is optional */
if (rpdata->array_index != BACNET_ARRAY_ALL) {
len =
encode_context_unsigned(&apdu[apdu_len], 2,
rpdata->array_index);
apdu_len += len;
}
len = encode_opening_tag(&apdu[apdu_len], 3);
apdu_len += len;
}
return apdu_len;
}
/* note: encode the application tagged data yourself */
int rp_ack_encode_apdu_object_property_end(
uint8_t * apdu)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu_len = encode_closing_tag(&apdu[0], 3);
}
return apdu_len;
}
int rp_ack_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_READ_PROPERTY_DATA * rpdata)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
/* Do the initial encoding */
apdu_len = rp_ack_encode_apdu_init(apdu, invoke_id, rpdata);
/* propertyValue */
for (len = 0; len < rpdata->application_data_len; len++) {
apdu[apdu_len++] = rpdata->application_data[len];
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 3);
}
return apdu_len;
}
#if BACNET_SVC_RP_A
/** Decode the ReadProperty reply and store the result for one Property in a
* BACNET_READ_PROPERTY_DATA structure.
* This leaves the value(s) in the application_data buffer to be decoded later;
* the application_data field points into the apdu buffer (is not allocated).
*
* @param apdu [in] The apdu portion of the ACK reply.
* @param apdu_len [in] The total length of the apdu.
* @param rpdata [out] The structure holding the partially decoded result.
* @return Number of decoded bytes (could be less than apdu_len),
* or -1 on decoding error.
*/
int rp_ack_decode_service_request(
uint8_t * apdu,
int apdu_len, /* total length of the apdu */
BACNET_READ_PROPERTY_DATA * rpdata)
{
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
int tag_len = 0; /* length of tag decode */
int len = 0; /* total length of decodes */
uint16_t object = 0; /* object type */
uint32_t property = 0; /* for decoding */
uint32_t array_value = 0; /* for decoding */
/* FIXME: check apdu_len against the len during decode */
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[0], 0))
return -1;
len = 1;
len += decode_object_id(&apdu[len], &object, &rpdata->object_instance);
rpdata->object_type = (BACNET_OBJECT_TYPE) object;
/* Tag 1: Property ID */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number != 1)
return -1;
len += decode_enumerated(&apdu[len], len_value_type, &property);
rpdata->object_property = (BACNET_PROPERTY_ID) property;
/* Tag 2: Optional Array Index */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number == 2) {
len += tag_len;
len += decode_unsigned(&apdu[len], len_value_type, &array_value);
rpdata->array_index = array_value;
} else
rpdata->array_index = BACNET_ARRAY_ALL;
/* Tag 3: opening context tag */
if (decode_is_opening_tag_number(&apdu[len], 3)) {
/* a tag number of 3 is not extended so only one octet */
len++;
/* don't decode the application tag number or its data here */
rpdata->application_data = &apdu[len];
rpdata->application_data_len = apdu_len - len - 1 /*closing tag */ ;
/* len includes the data and the closing tag */
len = apdu_len;
} else {
return -1;
}
return len;
}
#endif
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
int rp_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
BACNET_READ_PROPERTY_DATA * rpdata)
{
int len = 0;
unsigned offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST)
return -1;
/* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_READ_PROPERTY)
return -1;
offset = 4;
if (apdu_len > offset) {
len =
rp_decode_service_request(&apdu[offset], apdu_len - offset,
rpdata);
}
return len;
}
int rp_ack_decode_apdu(
uint8_t * apdu,
int apdu_len, /* total length of the apdu */
uint8_t * invoke_id,
BACNET_READ_PROPERTY_DATA * rpdata)
{
int len = 0;
int offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_COMPLEX_ACK)
return -1;
*invoke_id = apdu[1];
if (apdu[2] != SERVICE_CONFIRMED_READ_PROPERTY)
return -1;
offset = 3;
if (apdu_len > offset) {
len =
rp_ack_decode_service_request(&apdu[offset], apdu_len - offset,
rpdata);
}
return len;
}
void testReadPropertyAck(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
uint8_t apdu2[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 1;
uint8_t test_invoke_id = 0;
BACNET_READ_PROPERTY_DATA rpdata;
BACNET_READ_PROPERTY_DATA test_data;
BACNET_OBJECT_TYPE object_type = OBJECT_DEVICE;
uint32_t object_instance = 0;
uint16_t object = 0;
rpdata.object_type = OBJECT_DEVICE;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
rpdata.application_data_len =
encode_bacnet_object_id(&apdu2[0], rpdata.object_type,
rpdata.object_instance);
rpdata.application_data = &apdu2[0];
len = rp_ack_encode_apdu(&apdu[0], invoke_id, &rpdata);
ct_test(pTest, len != 0);
ct_test(pTest, len != -1);
apdu_len = len;
len = rp_ack_decode_apdu(&apdu[0], apdu_len, /* total length of the apdu */
&test_invoke_id, &test_data);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, test_data.object_type == rpdata.object_type);
ct_test(pTest, test_data.object_instance == rpdata.object_instance);
ct_test(pTest, test_data.object_property == rpdata.object_property);
ct_test(pTest, test_data.array_index == rpdata.array_index);
ct_test(pTest,
test_data.application_data_len == rpdata.application_data_len);
/* since object property == object_id, decode the application data using
the appropriate decode function */
len =
decode_object_id(test_data.application_data, &object,
&object_instance);
object_type = object;
ct_test(pTest, object_type == rpdata.object_type);
ct_test(pTest, object_instance == rpdata.object_instance);
}
void testReadProperty(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 128;
uint8_t test_invoke_id = 0;
BACNET_READ_PROPERTY_DATA rpdata;
BACNET_READ_PROPERTY_DATA test_data;
rpdata.object_type = OBJECT_DEVICE;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = rp_encode_apdu(&apdu[0], invoke_id, &rpdata);
ct_test(pTest, len != 0);
apdu_len = len;
len = rp_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data);
ct_test(pTest, len != -1);
ct_test(pTest, test_data.object_type == rpdata.object_type);
ct_test(pTest, test_data.object_instance == rpdata.object_instance);
ct_test(pTest, test_data.object_property == rpdata.object_property);
ct_test(pTest, test_data.array_index == rpdata.array_index);
return;
}
#ifdef TEST_READ_PROPERTY
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet ReadProperty", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testReadProperty);
assert(rc);
rc = ct_addTestFunction(pTest, testReadPropertyAck);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_READ_PROPERTY */
#endif /* TEST */
+968
View File
@@ -0,0 +1,968 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacerror.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "bacapp.h"
#include "memcopy.h"
#include "rpm.h"
/** @file rpm.c Encode/Decode Read Property Multiple and RPM ACKs */
#if BACNET_SVC_RPM_A
/* encode the initial portion of the service */
int rpm_encode_apdu_init(
uint8_t * apdu,
uint8_t invoke_id)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_READ_PROP_MULTIPLE; /* service choice */
apdu_len = 4;
}
return apdu_len;
}
int rpm_encode_apdu_object_begin(
uint8_t * apdu,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu_len =
encode_context_object_id(&apdu[0], 0, object_type,
object_instance);
/* Tag 1: sequence of ReadAccessSpecification */
apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
}
return apdu_len;
}
int rpm_encode_apdu_object_property(
uint8_t * apdu,
BACNET_PROPERTY_ID object_property,
uint32_t array_index)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu_len = encode_context_enumerated(&apdu[0], 0, object_property);
/* optional array index */
if (array_index != BACNET_ARRAY_ALL)
apdu_len +=
encode_context_unsigned(&apdu[apdu_len], 1, array_index);
}
return apdu_len;
}
int rpm_encode_apdu_object_end(
uint8_t * apdu)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu_len = encode_closing_tag(&apdu[0], 1);
}
return apdu_len;
}
/** Encode an RPM request, to be sent.
*
* @param apdu [in,out] Buffer to hold encoded bytes.
* @param max_apdu [in] Length of apdu buffer.
* @param invoke_id [in] The Invoke ID to use for this message.
* @param read_access_data [in] The RPM data to be requested.
* @return Length of encoded bytes, or 0 on failure.
*/
int rpm_encode_apdu(
uint8_t * apdu,
size_t max_apdu,
uint8_t invoke_id,
BACNET_READ_ACCESS_DATA * read_access_data)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0; /* length of the data */
BACNET_READ_ACCESS_DATA *rpm_object; /* current object */
uint8_t apdu_temp[16]; /* temp for data before copy */
BACNET_PROPERTY_REFERENCE *rpm_property; /* current property */
len = rpm_encode_apdu_init(&apdu_temp[0], invoke_id);
len =
(int) memcopy(&apdu[0], &apdu_temp[0], (size_t) apdu_len, (size_t) len,
(size_t) max_apdu);
if (len == 0) {
return 0;
}
apdu_len += len;
rpm_object = read_access_data;
while (rpm_object) {
len =
encode_context_object_id(&apdu_temp[0], 0, rpm_object->object_type,
rpm_object->object_instance);
len =
(int) memcopy(&apdu[0], &apdu_temp[0], (size_t) apdu_len,
(size_t) len, (size_t) max_apdu);
if (len == 0) {
return 0;
}
apdu_len += len;
/* Tag 1: sequence of ReadAccessSpecification */
len = encode_opening_tag(&apdu_temp[0], 1);
len =
(int) memcopy(&apdu[0], &apdu_temp[0], (size_t) apdu_len,
(size_t) len, (size_t) max_apdu);
if (len == 0) {
return 0;
}
apdu_len += len;
rpm_property = rpm_object->listOfProperties;
while (rpm_property) {
/* stuff as many properties into it as APDU length will allow */
len =
encode_context_enumerated(&apdu_temp[0], 0,
rpm_property->propertyIdentifier);
len =
(int) memcopy(&apdu[0], &apdu_temp[0], (size_t) apdu_len,
(size_t) len, (size_t) max_apdu);
if (len == 0) {
return 0;
}
apdu_len += len;
/* optional array index */
if (rpm_property->propertyArrayIndex != BACNET_ARRAY_ALL) {
len =
encode_context_unsigned(&apdu_temp[0], 1,
rpm_property->propertyArrayIndex);
len =
(int) memcopy(&apdu[0], &apdu_temp[0], (size_t) apdu_len,
(size_t) len, (size_t) max_apdu);
if (len == 0) {
return 0;
}
apdu_len += len;
}
rpm_property = rpm_property->next;
}
len = encode_closing_tag(&apdu_temp[0], 1);
len =
(int) memcopy(&apdu[0], &apdu_temp[0], (size_t) apdu_len,
(size_t) len, (size_t) max_apdu);
if (len == 0) {
return 0;
}
apdu_len += len;
rpm_object = rpm_object->next;
}
return apdu_len;
}
#endif
/* decode the object portion of the service request only. Bails out if
* tags are wrong or missing/incomplete
*/
int rpm_decode_object_id(
uint8_t * apdu,
unsigned apdu_len,
BACNET_RPM_DATA * rpmdata)
{
unsigned len = 0;
uint16_t type = 0; /* for decoding */
/* check for value pointers */
if (apdu && apdu_len && rpmdata) {
if (apdu_len < 5) { /* Must be at least 2 tags and an object id */
rpmdata->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
return BACNET_STATUS_REJECT;
}
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[len++], 0)) {
rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
len += decode_object_id(&apdu[len], &type, &rpmdata->object_instance);
rpmdata->object_type = (BACNET_OBJECT_TYPE) type;
/* Tag 1: sequence of ReadAccessSpecification */
if (!decode_is_opening_tag_number(&apdu[len], 1)) {
rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
len++; /* opening tag is only one octet */
}
return (int) len;
}
int rpm_decode_object_end(
uint8_t * apdu,
unsigned apdu_len)
{
int len = 0; /* total length of the apdu, return value */
if (apdu && apdu_len) {
if (decode_is_closing_tag_number(apdu, 1) == true)
len = 1;
}
return len;
}
/* decode the object property portion of the service request only */
/* BACnetPropertyReference ::= SEQUENCE {
propertyIdentifier [0] BACnetPropertyIdentifier,
propertyArrayIndex [1] Unsigned OPTIONAL
--used only with array datatype
-- if omitted with an array the entire array is referenced
}
*/
int rpm_decode_object_property(
uint8_t * apdu,
unsigned apdu_len,
BACNET_RPM_DATA * rpmdata)
{
unsigned len = 0;
unsigned option_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint32_t property = 0; /* for decoding */
uint32_t array_value = 0; /* for decoding */
/* check for valid pointers */
if (apdu && apdu_len && rpmdata) {
/* Tag 0: propertyIdentifier */
if (!IS_CONTEXT_SPECIFIC(apdu[len])) {
rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number != 0) {
rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
/* Should be at least the unsigned value + 1 tag left */
if ((len + len_value_type) >= apdu_len) {
rpmdata->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
return BACNET_STATUS_REJECT;
}
len += decode_enumerated(&apdu[len], len_value_type, &property);
rpmdata->object_property = (BACNET_PROPERTY_ID) property;
/* Assume most probable outcome */
rpmdata->array_index = BACNET_ARRAY_ALL;
/* Tag 1: Optional propertyArrayIndex */
if (IS_CONTEXT_SPECIFIC(apdu[len]) && !IS_CLOSING_TAG(apdu[len])) {
option_len =
(unsigned) decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number == 1) {
len += option_len;
/* Should be at least the unsigned array index + 1 tag left */
if ((len + len_value_type) >= apdu_len) {
rpmdata->error_code =
ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
return BACNET_STATUS_REJECT;
}
len +=
decode_unsigned(&apdu[len], len_value_type, &array_value);
rpmdata->array_index = array_value;
}
}
}
return (int) len;
}
int rpm_ack_encode_apdu_init(
uint8_t * apdu,
uint8_t invoke_id)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */
apdu[1] = invoke_id; /* original invoke id from request */
apdu[2] = SERVICE_CONFIRMED_READ_PROP_MULTIPLE; /* service choice */
apdu_len = 3;
}
return apdu_len;
}
int rpm_ack_encode_apdu_object_begin(
uint8_t * apdu,
BACNET_RPM_DATA * rpmdata)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
/* Tag 0: objectIdentifier */
apdu_len =
encode_context_object_id(&apdu[0], 0, rpmdata->object_type,
rpmdata->object_instance);
/* Tag 1: listOfResults */
apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
}
return apdu_len;
}
int rpm_ack_encode_apdu_object_property(
uint8_t * apdu,
BACNET_PROPERTY_ID object_property,
uint32_t array_index)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
/* Tag 2: propertyIdentifier */
apdu_len = encode_context_enumerated(&apdu[0], 2, object_property);
/* Tag 3: optional propertyArrayIndex */
if (array_index != BACNET_ARRAY_ALL)
apdu_len +=
encode_context_unsigned(&apdu[apdu_len], 3, array_index);
}
return apdu_len;
}
int rpm_ack_encode_apdu_object_property_value(
uint8_t * apdu,
uint8_t * application_data,
unsigned application_data_len)
{
int apdu_len = 0; /* total length of the apdu, return value */
unsigned len = 0;
if (apdu) {
/* Tag 4: propertyValue */
apdu_len += encode_opening_tag(&apdu[apdu_len], 4);
if (application_data == &apdu[apdu_len]) { /* Is Data already in place? */
apdu_len += application_data_len; /* Yes, step over data */
} else { /* No, copy data in */
for (len = 0; len < application_data_len; len++) {
apdu[apdu_len++] = application_data[len];
}
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 4);
}
return apdu_len;
}
int rpm_ack_encode_apdu_object_property_error(
uint8_t * apdu,
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
/* Tag 5: propertyAccessError */
apdu_len += encode_opening_tag(&apdu[apdu_len], 5);
apdu_len +=
encode_application_enumerated(&apdu[apdu_len], error_class);
apdu_len += encode_application_enumerated(&apdu[apdu_len], error_code);
apdu_len += encode_closing_tag(&apdu[apdu_len], 5);
}
return apdu_len;
}
int rpm_ack_encode_apdu_object_end(
uint8_t * apdu)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu_len = encode_closing_tag(&apdu[0], 1);
}
return apdu_len;
}
#if BACNET_SVC_RPM_A
/* decode the object portion of the service request only */
int rpm_ack_decode_object_id(
uint8_t * apdu,
unsigned apdu_len,
BACNET_OBJECT_TYPE * object_type,
uint32_t * object_instance)
{
unsigned len = 0;
uint16_t type = 0; /* for decoding */
/* check for value pointers */
if (apdu && apdu_len && object_type && object_instance) {
/* Tag 0: objectIdentifier */
if (!decode_is_context_tag(&apdu[len++], 0))
return -1;
len += decode_object_id(&apdu[len], &type, object_instance);
if (object_type)
*object_type = (BACNET_OBJECT_TYPE) type;
/* Tag 1: listOfResults */
if (!decode_is_opening_tag_number(&apdu[len], 1))
return -1;
len++; /* opening tag is only one octet */
}
return (int) len;
}
/* is this the end of the list of this objects properties values? */
int rpm_ack_decode_object_end(
uint8_t * apdu,
unsigned apdu_len)
{
int len = 0; /* total length of the apdu, return value */
if (apdu && apdu_len) {
if (decode_is_closing_tag_number(apdu, 1))
len = 1;
}
return len;
}
int rpm_ack_decode_object_property(
uint8_t * apdu,
unsigned apdu_len,
BACNET_PROPERTY_ID * object_property,
uint32_t * array_index)
{
unsigned len = 0;
unsigned tag_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint32_t property = 0; /* for decoding */
uint32_t array_value = 0; /* for decoding */
/* check for valid pointers */
if (apdu && apdu_len && object_property && array_index) {
/* Tag 2: propertyIdentifier */
if (!IS_CONTEXT_SPECIFIC(apdu[len]))
return -1;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number != 2)
return -1;
len += decode_enumerated(&apdu[len], len_value_type, &property);
if (object_property)
*object_property = (BACNET_PROPERTY_ID) property;
/* Tag 3: Optional propertyArrayIndex */
if ((len < apdu_len) && IS_CONTEXT_SPECIFIC(apdu[len]) &&
(!IS_CLOSING_TAG(apdu[len]))) {
tag_len =
(unsigned) decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number == 3) {
len += tag_len;
len +=
decode_unsigned(&apdu[len], len_value_type, &array_value);
*array_index = array_value;
} else {
*array_index = BACNET_ARRAY_ALL;
}
} else {
*array_index = BACNET_ARRAY_ALL;
}
}
return (int) len;
}
#endif
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
int rpm_ack_decode_apdu(
uint8_t * apdu,
int apdu_len, /* total length of the apdu */
uint8_t * invoke_id,
uint8_t ** service_request,
unsigned *service_request_len)
{
int offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_COMPLEX_ACK)
return -1;
*invoke_id = apdu[1];
if (apdu[2] != SERVICE_CONFIRMED_READ_PROP_MULTIPLE)
return -1;
offset = 3;
if (apdu_len > offset) {
if (service_request)
*service_request = &apdu[offset];
if (service_request_len)
*service_request_len = apdu_len - offset;
}
return offset;
}
int rpm_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
uint8_t ** service_request,
unsigned *service_request_len)
{
unsigned offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST)
return -1;
/* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_READ_PROP_MULTIPLE)
return -1;
offset = 4;
if (apdu_len > offset) {
if (service_request)
*service_request = &apdu[offset];
if (service_request_len)
*service_request_len = apdu_len - offset;
}
return offset;
}
void testReadPropertyMultiple(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int test_len = 0;
int apdu_len = 0;
uint8_t invoke_id = 12;
uint8_t test_invoke_id = 0;
uint8_t *service_request = NULL;
unsigned service_request_len = 0;
BACNET_RPM_DATA rpmdata;
rpmdata.object_type = OBJECT_DEVICE;
rpmdata.object_instance = 0;
rpmdata.object_property = PROP_OBJECT_IDENTIFIER;
rpmdata.array_index = 0;
/* build the RPM - try to make it easy for the Application Layer development */
/* IDEA: similar construction, but pass apdu, apdu_len pointer, size of apdu to
let the called function handle the out of space problem that these get into
by returning a boolean of success/failure.
It almost needs to use the keylist library or something similar.
Also check case of storing a backoff point (i.e. save enough room for object_end) */
apdu_len = rpm_encode_apdu_init(&apdu[0], invoke_id);
/* each object has a beginning and an end */
apdu_len +=
rpm_encode_apdu_object_begin(&apdu[apdu_len], OBJECT_DEVICE, 123);
/* then stuff as many properties into it as APDU length will allow */
apdu_len +=
rpm_encode_apdu_object_property(&apdu[apdu_len],
PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL);
apdu_len +=
rpm_encode_apdu_object_property(&apdu[apdu_len], PROP_OBJECT_NAME,
BACNET_ARRAY_ALL);
apdu_len += rpm_encode_apdu_object_end(&apdu[apdu_len]);
/* each object has a beginning and an end */
apdu_len +=
rpm_encode_apdu_object_begin(&apdu[apdu_len], OBJECT_ANALOG_INPUT, 33);
apdu_len +=
rpm_encode_apdu_object_property(&apdu[apdu_len],
PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL);
apdu_len +=
rpm_encode_apdu_object_property(&apdu[apdu_len], PROP_ALL,
BACNET_ARRAY_ALL);
apdu_len += rpm_encode_apdu_object_end(&apdu[apdu_len]);
ct_test(pTest, apdu_len != 0);
test_len = rpm_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &service_request, /* will point to the service request in the apdu */
&service_request_len);
ct_test(pTest, test_len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, service_request != NULL);
ct_test(pTest, service_request_len > 0);
test_len =
rpm_decode_object_id(service_request, service_request_len, &rpmdata);
ct_test(pTest, test_len > 0);
ct_test(pTest, rpmdata.object_type == OBJECT_DEVICE);
ct_test(pTest, rpmdata.object_instance == 123);
len = test_len;
/* decode the object property portion of the service request */
test_len =
rpm_decode_object_property(&service_request[len],
service_request_len - len, &rpmdata);
ct_test(pTest, test_len > 0);
ct_test(pTest, rpmdata.object_property == PROP_OBJECT_IDENTIFIER);
ct_test(pTest, rpmdata.array_index == BACNET_ARRAY_ALL);
len += test_len;
test_len =
rpm_decode_object_property(&service_request[len],
service_request_len - len, &rpmdata);
ct_test(pTest, test_len > 0);
ct_test(pTest, rpmdata.object_property == PROP_OBJECT_NAME);
ct_test(pTest, rpmdata.array_index == BACNET_ARRAY_ALL);
len += test_len;
/* try again - we should fail */
test_len =
rpm_decode_object_property(&service_request[len],
service_request_len - len, &rpmdata);
ct_test(pTest, test_len < 0);
/* is it the end of this object? */
test_len =
rpm_decode_object_end(&service_request[len],
service_request_len - len);
ct_test(pTest, test_len == 1);
len += test_len;
/* try to decode an object id */
test_len =
rpm_decode_object_id(&service_request[len], service_request_len - len,
&rpmdata);
ct_test(pTest, test_len > 0);
ct_test(pTest, rpmdata.object_type == OBJECT_ANALOG_INPUT);
ct_test(pTest, rpmdata.object_instance == 33);
len += test_len;
/* decode the object property portion of the service request only */
test_len =
rpm_decode_object_property(&service_request[len],
service_request_len - len, &rpmdata);
ct_test(pTest, test_len > 0);
ct_test(pTest, rpmdata.object_property == PROP_OBJECT_IDENTIFIER);
ct_test(pTest, rpmdata.array_index == BACNET_ARRAY_ALL);
len += test_len;
test_len =
rpm_decode_object_property(&service_request[len],
service_request_len - len, &rpmdata);
ct_test(pTest, test_len > 0);
ct_test(pTest, rpmdata.object_property == PROP_ALL);
ct_test(pTest, rpmdata.array_index == BACNET_ARRAY_ALL);
len += test_len;
test_len =
rpm_decode_object_property(&service_request[len],
service_request_len - len, &rpmdata);
ct_test(pTest, test_len < 0);
/* got an error -1, is it the end of this object? */
test_len =
rpm_decode_object_end(&service_request[len],
service_request_len - len);
ct_test(pTest, test_len == 1);
len += test_len;
ct_test(pTest, len == service_request_len);
}
void testReadPropertyMultipleAck(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int test_len = 0;
int apdu_len = 0;
uint8_t invoke_id = 12;
uint8_t test_invoke_id = 0;
uint8_t *service_request = NULL;
unsigned service_request_len = 0;
BACNET_OBJECT_TYPE object_type = OBJECT_DEVICE;
uint32_t object_instance = 0;
BACNET_PROPERTY_ID object_property = PROP_OBJECT_IDENTIFIER;
uint32_t array_index = 0;
BACNET_APPLICATION_DATA_VALUE application_data[4] = { {0} };
BACNET_APPLICATION_DATA_VALUE test_application_data = { 0 };
uint8_t application_data_buffer[MAX_APDU] = { 0 };
int application_data_buffer_len = 0;
BACNET_ERROR_CLASS error_class;
BACNET_ERROR_CODE error_code;
BACNET_RPM_DATA rpmdata;
/* build the RPM - try to make it easy for the
Application Layer development */
/* IDEA: similar construction, but pass apdu, apdu_len pointer,
size of apdu to let the called function handle the out of
space problem that these get into by returning a boolean
of success/failure.
It almost needs to use the keylist library or something similar.
Also check case of storing a backoff point
(i.e. save enough room for object_end) */
apdu_len = rpm_ack_encode_apdu_init(&apdu[0], invoke_id);
/* object beginning */
rpmdata.object_type = OBJECT_DEVICE;
rpmdata.object_instance = 123;
apdu_len += rpm_ack_encode_apdu_object_begin(&apdu[apdu_len], &rpmdata);
/* reply property */
apdu_len +=
rpm_ack_encode_apdu_object_property(&apdu[apdu_len],
PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL);
/* reply value */
application_data[0].tag = BACNET_APPLICATION_TAG_OBJECT_ID;
application_data[0].type.Object_Id.type = OBJECT_DEVICE;
application_data[0].type.Object_Id.instance = 123;
application_data_buffer_len =
bacapp_encode_application_data(&application_data_buffer[0],
&application_data[0]);
apdu_len +=
rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len],
&application_data_buffer[0], application_data_buffer_len);
/* reply property */
apdu_len +=
rpm_ack_encode_apdu_object_property(&apdu[apdu_len], PROP_OBJECT_TYPE,
BACNET_ARRAY_ALL);
/* reply value */
application_data[1].tag = BACNET_APPLICATION_TAG_ENUMERATED;
application_data[1].type.Enumerated = OBJECT_DEVICE;
application_data_buffer_len =
bacapp_encode_application_data(&application_data_buffer[0],
&application_data[1]);
apdu_len +=
rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len],
&application_data_buffer[0], application_data_buffer_len);
/* object end */
apdu_len += rpm_ack_encode_apdu_object_end(&apdu[apdu_len]);
/* object beginning */
rpmdata.object_type = OBJECT_ANALOG_INPUT;
rpmdata.object_instance = 33;
apdu_len += rpm_ack_encode_apdu_object_begin(&apdu[apdu_len], &rpmdata);
/* reply property */
apdu_len +=
rpm_ack_encode_apdu_object_property(&apdu[apdu_len],
PROP_PRESENT_VALUE, BACNET_ARRAY_ALL);
/* reply value */
application_data[2].tag = BACNET_APPLICATION_TAG_REAL;
application_data[2].type.Real = 0.0;
application_data_buffer_len =
bacapp_encode_application_data(&application_data_buffer[0],
&application_data[2]);
apdu_len +=
rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len],
&application_data_buffer[0], application_data_buffer_len);
/* reply property */
apdu_len +=
rpm_ack_encode_apdu_object_property(&apdu[apdu_len], PROP_DEADBAND,
BACNET_ARRAY_ALL);
/* reply error */
apdu_len +=
rpm_ack_encode_apdu_object_property_error(&apdu[apdu_len],
ERROR_CLASS_PROPERTY, ERROR_CODE_UNKNOWN_PROPERTY);
/* object end */
apdu_len += rpm_ack_encode_apdu_object_end(&apdu[apdu_len]);
ct_test(pTest, apdu_len != 0);
/****** decode the packet ******/
test_len = rpm_ack_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &service_request, /* will point to the service request in the apdu */
&service_request_len);
ct_test(pTest, test_len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, service_request != NULL);
ct_test(pTest, service_request_len > 0);
/* the first part should be the first object id */
test_len =
rpm_ack_decode_object_id(service_request, service_request_len,
&object_type, &object_instance);
ct_test(pTest, test_len != -1);
ct_test(pTest, object_type == OBJECT_DEVICE);
ct_test(pTest, object_instance == 123);
len = test_len;
/* extract the property */
test_len =
rpm_ack_decode_object_property(&service_request[len],
service_request_len - len, &object_property, &array_index);
ct_test(pTest, object_property == PROP_OBJECT_IDENTIFIER);
ct_test(pTest, array_index == BACNET_ARRAY_ALL);
len += test_len;
/* what is the result? An error or a value? */
ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 4));
len++;
/* decode the object property portion of the service request */
/* note: if this was an array, there could have been
more than one element to decode */
test_len =
bacapp_decode_application_data(&service_request[len],
service_request_len - len, &test_application_data);
ct_test(pTest, test_len > 0);
ct_test(pTest, bacapp_same_value(&application_data[0],
&test_application_data));
len += test_len;
ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 4));
len++;
/* see if there is another property */
test_len =
rpm_ack_decode_object_property(&service_request[len],
service_request_len - len, &object_property, &array_index);
ct_test(pTest, test_len != -1);
ct_test(pTest, object_property == PROP_OBJECT_TYPE);
ct_test(pTest, array_index == BACNET_ARRAY_ALL);
len += test_len;
/* what is the result value? */
ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 4));
len++;
/* decode the object property portion of the service request */
test_len =
bacapp_decode_application_data(&service_request[len],
service_request_len - len, &test_application_data);
ct_test(pTest, test_len > 0);
ct_test(pTest, bacapp_same_value(&application_data[1],
&test_application_data));
len += test_len;
ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 4));
len++;
/* see if there is another property */
/* this time we should fail */
test_len =
rpm_ack_decode_object_property(&service_request[len],
service_request_len - len, &object_property, &array_index);
ct_test(pTest, test_len == -1);
/* see if it is the end of this object */
test_len =
rpm_ack_decode_object_end(&service_request[len],
service_request_len - len);
ct_test(pTest, test_len == 1);
len += test_len;
/* try to decode another object id */
test_len =
rpm_ack_decode_object_id(&service_request[len],
service_request_len - len, &object_type, &object_instance);
ct_test(pTest, test_len != -1);
ct_test(pTest, object_type == OBJECT_ANALOG_INPUT);
ct_test(pTest, object_instance == 33);
len += test_len;
/* decode the object property portion of the service request only */
test_len =
rpm_ack_decode_object_property(&service_request[len],
service_request_len - len, &object_property, &array_index);
ct_test(pTest, test_len != -1);
ct_test(pTest, object_property == PROP_PRESENT_VALUE);
ct_test(pTest, array_index == BACNET_ARRAY_ALL);
len += test_len;
/* what is the result value? */
ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 4));
len++;
/* decode the object property portion of the service request */
test_len =
bacapp_decode_application_data(&service_request[len],
service_request_len - len, &test_application_data);
ct_test(pTest, test_len > 0);
ct_test(pTest, bacapp_same_value(&application_data[2],
&test_application_data));
len += test_len;
ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 4));
len++;
/* see if there is another property */
test_len =
rpm_ack_decode_object_property(&service_request[len],
service_request_len - len, &object_property, &array_index);
ct_test(pTest, test_len != -1);
ct_test(pTest, object_property == PROP_DEADBAND);
ct_test(pTest, array_index == BACNET_ARRAY_ALL);
len += test_len;
/* what is the result value? */
ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 5));
len++;
/* it was an error reply */
test_len =
bacerror_decode_error_class_and_code(&service_request[len],
service_request_len - len, &error_class, &error_code);
ct_test(pTest, test_len != 0);
ct_test(pTest, error_class == ERROR_CLASS_PROPERTY);
ct_test(pTest, error_code == ERROR_CODE_UNKNOWN_PROPERTY);
len += test_len;
ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 5));
len++;
/* is there another property? */
test_len =
rpm_ack_decode_object_property(&service_request[len],
service_request_len - len, &object_property, &array_index);
ct_test(pTest, test_len == -1);
/* got an error -1, is it the end of this object? */
test_len =
rpm_ack_decode_object_end(&service_request[len],
service_request_len - len);
ct_test(pTest, test_len == 1);
len += test_len;
/* check for another object */
test_len =
rpm_ack_decode_object_id(&service_request[len],
service_request_len - len, &object_type, &object_instance);
ct_test(pTest, test_len == 0);
ct_test(pTest, len == service_request_len);
}
#ifdef TEST_READ_PROPERTY_MULTIPLE
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet ReadPropertyMultiple", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testReadPropertyMultiple);
assert(rc);
rc = ct_addTestFunction(pTest, testReadPropertyMultipleAck);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_READ_PROPERTY_MULTIPLE */
#endif /* TEST */
+222
View File
@@ -0,0 +1,222 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 by Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307
USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
/** @file sbuf.c Static buffer library for deeply embedded system. */
/* Functional Description: Static buffer library for deeply
embedded system. See the unit tests for usage examples. */
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "sbuf.h"
void sbuf_init(
STATIC_BUFFER * b, /* static buffer structure */
char *data, /* data block */
unsigned size)
{ /* actual size, in bytes, of the data block or array of data */
if (b) {
b->data = data;
b->size = size;
b->count = 0;
}
return;
}
/* returns true if count==0, false if count > 0 */
bool sbuf_empty(
STATIC_BUFFER const *b)
{
return (b ? (b->count == 0) : false);
}
char *sbuf_data(
STATIC_BUFFER const *b)
{
return (b ? b->data : NULL);
}
unsigned sbuf_size(
STATIC_BUFFER * b)
{
return (b ? b->size : 0);
}
unsigned sbuf_count(
STATIC_BUFFER * b)
{
return (b ? b->count : 0);
}
/* returns true if successful, false if not enough room to append data */
bool sbuf_put(
STATIC_BUFFER * b, /* static buffer structure */
unsigned offset, /* where to start */
char *data, /* data to place in buffer */
unsigned data_size)
{ /* how many bytes to add */
bool status = false; /* return value */
if (b && b->data) {
if (((offset + data_size) < b->size)) {
b->count = offset + data_size;
while (data_size) {
b->data[offset] = *data;
offset++;
data++;
data_size--;
}
status = true;
}
}
return status;
}
/* returns true if successful, false if not enough room to append data */
bool sbuf_append(
STATIC_BUFFER * b, /* static buffer structure */
char *data, /* data to place in buffer */
unsigned data_size)
{ /* how many bytes to add */
unsigned count = 0;
if (b) {
count = b->count;
}
return sbuf_put(b, count, data, data_size);
}
/* returns true if successful, false if not enough room to append data */
bool sbuf_truncate(
STATIC_BUFFER * b, /* static buffer structure */
unsigned count)
{ /* total number of bytes in to remove */
bool status = false; /* return value */
if (b) {
if (count < b->size) {
b->count = count;
status = true;
}
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testStaticBuffer(
Test * pTest)
{
STATIC_BUFFER sbuffer;
char *data1 = "Joshua";
char *data2 = "Anna";
char *data3 = "Christopher";
char *data4 = "Mary";
char data_buffer[480] = "";
char test_data_buffer[480] = "";
char *data;
unsigned count;
sbuf_init(&sbuffer, NULL, 0);
ct_test(pTest, sbuf_empty(&sbuffer) == true);
ct_test(pTest, sbuf_data(&sbuffer) == NULL);
ct_test(pTest, sbuf_size(&sbuffer) == 0);
ct_test(pTest, sbuf_count(&sbuffer) == 0);
ct_test(pTest, sbuf_append(&sbuffer, data1, strlen(data1)) == false);
sbuf_init(&sbuffer, data_buffer, sizeof(data_buffer));
ct_test(pTest, sbuf_empty(&sbuffer) == true);
ct_test(pTest, sbuf_data(&sbuffer) == data_buffer);
ct_test(pTest, sbuf_size(&sbuffer) == sizeof(data_buffer));
ct_test(pTest, sbuf_count(&sbuffer) == 0);
ct_test(pTest, sbuf_append(&sbuffer, data1, strlen(data1)) == true);
ct_test(pTest, sbuf_append(&sbuffer, data2, strlen(data2)) == true);
ct_test(pTest, sbuf_append(&sbuffer, data3, strlen(data3)) == true);
ct_test(pTest, sbuf_append(&sbuffer, data4, strlen(data4)) == true);
strcat(test_data_buffer, data1);
strcat(test_data_buffer, data2);
strcat(test_data_buffer, data3);
strcat(test_data_buffer, data4);
ct_test(pTest, sbuf_count(&sbuffer) == strlen(test_data_buffer));
data = sbuf_data(&sbuffer);
count = sbuf_count(&sbuffer);
ct_test(pTest, memcmp(data, test_data_buffer, count) == 0);
ct_test(pTest, count == strlen(test_data_buffer));
ct_test(pTest, sbuf_truncate(&sbuffer, 0) == true);
ct_test(pTest, sbuf_count(&sbuffer) == 0);
ct_test(pTest, sbuf_size(&sbuffer) == sizeof(data_buffer));
ct_test(pTest, sbuf_append(&sbuffer, data4, strlen(data4)) == true);
data = sbuf_data(&sbuffer);
count = sbuf_count(&sbuffer);
ct_test(pTest, memcmp(data, data4, count) == 0);
ct_test(pTest, count == strlen(data4));
return;
}
#ifdef TEST_STATIC_BUFFER
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("static buffer", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testStaticBuffer);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_STATIC_BUFFER */
#endif /* TEST */
+380
View File
@@ -0,0 +1,380 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2008 John Minack
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include "assert.h"
#include "timestamp.h"
/** @file timestamp.c Encode/Decode BACnet Timestamps */
void bacapp_timestamp_sequence_set(
BACNET_TIMESTAMP * dest,
uint16_t sequenceNum)
{
if (dest) {
dest->tag = TIME_STAMP_SEQUENCE;
dest->value.sequenceNum = sequenceNum;
}
}
void bacapp_timestamp_time_set(
BACNET_TIMESTAMP * dest,
BACNET_TIME *btime)
{
if (dest && btime) {
dest->tag = TIME_STAMP_TIME;
datetime_copy_time(&dest->value.time, btime);
}
}
void bacapp_timestamp_datetime_set(
BACNET_TIMESTAMP * dest,
BACNET_DATE_TIME * bdateTime)
{
if (dest && bdateTime) {
dest->tag = TIME_STAMP_DATETIME;
datetime_copy(&dest->value.dateTime, bdateTime);
}
}
void bacapp_timestamp_copy(
BACNET_TIMESTAMP * dest,
BACNET_TIMESTAMP * src)
{
if (dest && src) {
dest->tag = src->tag;
switch (src->tag) {
case TIME_STAMP_TIME:
datetime_copy_time(&dest->value.time, &src->value.time);
break;
case TIME_STAMP_SEQUENCE:
dest->value.sequenceNum = src->value.sequenceNum;
break;
case TIME_STAMP_DATETIME:
datetime_copy(&dest->value.dateTime, &src->value.dateTime);
break;
default:
break;
}
}
}
int bacapp_encode_timestamp(
uint8_t * apdu,
BACNET_TIMESTAMP * value)
{
int len = 0; /* length of each encoding */
if (value && apdu) {
switch (value->tag) {
case TIME_STAMP_TIME:
len = encode_context_time(&apdu[0], 0, &value->value.time);
break;
case TIME_STAMP_SEQUENCE:
len =
encode_context_unsigned(&apdu[0], 1,
value->value.sequenceNum);
break;
case TIME_STAMP_DATETIME:
len =
bacapp_encode_context_datetime(&apdu[0], 2,
&value->value.dateTime);
break;
default:
len = 0;
assert(0);
break;
}
}
return len;
}
int bacapp_encode_context_timestamp(
uint8_t * apdu,
uint8_t tag_number,
BACNET_TIMESTAMP * value)
{
int len = 0; /* length of each encoding */
int apdu_len = 0;
if (value && apdu) {
len = encode_opening_tag(&apdu[apdu_len], tag_number);
apdu_len += len;
len = bacapp_encode_timestamp(&apdu[apdu_len], value);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], tag_number);
apdu_len += len;
}
return apdu_len;
}
int bacapp_decode_timestamp(
uint8_t * apdu,
BACNET_TIMESTAMP * value)
{
int len = 0;
int section_len;
uint32_t len_value_type;
uint32_t sequenceNum;
if (apdu) {
section_len =
decode_tag_number_and_value(&apdu[len], &value->tag,
&len_value_type);
if (-1 == section_len) {
return -1;
}
switch (value->tag) {
case TIME_STAMP_TIME:
if ((section_len =
decode_context_bacnet_time(&apdu[len], TIME_STAMP_TIME,
&value->value.time)) == -1) {
return -1;
} else {
len += section_len;
}
break;
case TIME_STAMP_SEQUENCE:
if ((section_len =
decode_context_unsigned(&apdu[len],
TIME_STAMP_SEQUENCE, &sequenceNum)) == -1) {
return -1;
} else {
if (sequenceNum <= 0xffff) {
len += section_len;
value->value.sequenceNum = (uint16_t) sequenceNum;
} else {
return -1;
}
}
break;
case TIME_STAMP_DATETIME:
if ((section_len =
bacapp_decode_context_datetime(&apdu[len],
TIME_STAMP_DATETIME,
&value->value.dateTime)) == -1) {
return -1;
} else {
len += section_len;
}
break;
default:
return -1;
}
}
return len;
}
int bacapp_decode_context_timestamp(
uint8_t * apdu,
uint8_t tag_number,
BACNET_TIMESTAMP * value)
{
int len = 0;
int section_len;
if (decode_is_opening_tag_number(&apdu[len], tag_number)) {
len++;
section_len = bacapp_decode_timestamp(&apdu[len], value);
if (section_len > 0) {
len += section_len;
if (decode_is_closing_tag_number(&apdu[len], tag_number)) {
len++;
} else {
return -1;
}
}
}
return len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testTimestampSequence(
Test * pTest)
{
BACNET_TIMESTAMP testTimestampIn;
BACNET_TIMESTAMP testTimestampOut;
uint8_t buffer[MAX_APDU];
int inLen;
int outLen;
testTimestampIn.tag = TIME_STAMP_SEQUENCE;
testTimestampIn.value.sequenceNum = 0x1234;
memset(&testTimestampOut, 0, sizeof(testTimestampOut));
inLen = bacapp_encode_context_timestamp(buffer, 2, &testTimestampIn);
outLen = bacapp_decode_context_timestamp(buffer, 2, &testTimestampOut);
ct_test(pTest, inLen == outLen);
ct_test(pTest, testTimestampIn.tag == testTimestampOut.tag);
ct_test(pTest,
testTimestampIn.value.sequenceNum ==
testTimestampOut.value.sequenceNum);
}
void testTimestampTime(
Test * pTest)
{
BACNET_TIMESTAMP testTimestampIn;
BACNET_TIMESTAMP testTimestampOut;
uint8_t buffer[MAX_APDU];
int inLen;
int outLen;
testTimestampIn.tag = TIME_STAMP_TIME;
testTimestampIn.value.time.hour = 1;
testTimestampIn.value.time.min = 2;
testTimestampIn.value.time.sec = 3;
testTimestampIn.value.time.hundredths = 4;
memset(&testTimestampOut, 0, sizeof(testTimestampOut));
inLen = bacapp_encode_context_timestamp(buffer, 2, &testTimestampIn);
outLen = bacapp_decode_context_timestamp(buffer, 2, &testTimestampOut);
ct_test(pTest, inLen == outLen);
ct_test(pTest, testTimestampIn.tag == testTimestampOut.tag);
ct_test(pTest,
testTimestampIn.value.time.hour == testTimestampOut.value.time.hour);
ct_test(pTest,
testTimestampIn.value.time.min == testTimestampOut.value.time.min);
ct_test(pTest,
testTimestampIn.value.time.sec == testTimestampOut.value.time.sec);
ct_test(pTest,
testTimestampIn.value.time.hundredths ==
testTimestampOut.value.time.hundredths);
}
void testTimestampTimeDate(
Test * pTest)
{
BACNET_TIMESTAMP testTimestampIn;
BACNET_TIMESTAMP testTimestampOut;
uint8_t buffer[MAX_APDU];
int inLen;
int outLen;
testTimestampIn.tag = TIME_STAMP_DATETIME;
testTimestampIn.value.dateTime.time.hour = 1;
testTimestampIn.value.dateTime.time.min = 2;
testTimestampIn.value.dateTime.time.sec = 3;
testTimestampIn.value.dateTime.time.hundredths = 4;
testTimestampIn.value.dateTime.date.year = 1901;
testTimestampIn.value.dateTime.date.month = 1;
testTimestampIn.value.dateTime.date.wday = 2;
testTimestampIn.value.dateTime.date.day = 3;
memset(&testTimestampOut, 0, sizeof(testTimestampOut));
inLen = bacapp_encode_context_timestamp(buffer, 2, &testTimestampIn);
outLen = bacapp_decode_context_timestamp(buffer, 2, &testTimestampOut);
ct_test(pTest, inLen == outLen);
ct_test(pTest, testTimestampIn.tag == testTimestampOut.tag);
ct_test(pTest,
testTimestampIn.value.dateTime.time.hour ==
testTimestampOut.value.dateTime.time.hour);
ct_test(pTest,
testTimestampIn.value.dateTime.time.min ==
testTimestampOut.value.dateTime.time.min);
ct_test(pTest,
testTimestampIn.value.dateTime.time.sec ==
testTimestampOut.value.dateTime.time.sec);
ct_test(pTest,
testTimestampIn.value.dateTime.time.hundredths ==
testTimestampOut.value.dateTime.time.hundredths);
ct_test(pTest,
testTimestampIn.value.dateTime.date.year ==
testTimestampOut.value.dateTime.date.year);
ct_test(pTest,
testTimestampIn.value.dateTime.date.month ==
testTimestampOut.value.dateTime.date.month);
ct_test(pTest,
testTimestampIn.value.dateTime.date.wday ==
testTimestampOut.value.dateTime.date.wday);
ct_test(pTest,
testTimestampIn.value.dateTime.date.day ==
testTimestampOut.value.dateTime.date.day);
}
#ifdef TEST_TIME_STAMP
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Time Stamp", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testTimestampSequence);
assert(rc);
rc = ct_addTestFunction(pTest, testTimestampTime);
assert(rc);
rc = ct_addTestFunction(pTest, testTimestampTimeDate);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_TIME_STAMP */
#endif /* TEST */
+529
View File
@@ -0,0 +1,529 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2006 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "bacapp.h"
#include "timesync.h"
/** @file timesync.c Encode/Decode TimeSync APDUs */
#if BACNET_SVC_TS_A
/* encode service */
int timesync_encode_apdu_service(
uint8_t * apdu,
BACNET_UNCONFIRMED_SERVICE service,
BACNET_DATE * my_date,
BACNET_TIME * my_time)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu && my_date && my_time) {
apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST;
apdu[1] = service;
apdu_len = 2;
len = encode_application_date(&apdu[apdu_len], my_date);
apdu_len += len;
len = encode_application_time(&apdu[apdu_len], my_time);
apdu_len += len;
}
return apdu_len;
}
int timesync_utc_encode_apdu(
uint8_t * apdu,
BACNET_DATE * my_date,
BACNET_TIME * my_time)
{
return timesync_encode_apdu_service(apdu,
SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, my_date, my_time);
}
int timesync_encode_apdu(
uint8_t * apdu,
BACNET_DATE * my_date,
BACNET_TIME * my_time)
{
return timesync_encode_apdu_service(apdu,
SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, my_date, my_time);
}
#endif
/* decode the service request only */
int timesync_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_DATE * my_date,
BACNET_TIME * my_time)
{
int len = 0;
uint8_t tag_number = 0;
uint32_t len_value = 0;
if (apdu_len && my_date && my_time) {
/* date */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number == BACNET_APPLICATION_TAG_DATE) {
len += decode_date(&apdu[len], my_date);
} else
return -1;
/* time */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number == BACNET_APPLICATION_TAG_TIME) {
len += decode_bacnet_time(&apdu[len], my_time);
} else
return -1;
}
return len;
}
/** Handle a request to encode the list of timesync recipients.
*
* Invoked by a request to read the Device object's
* PROP_TIME_SYNCHRONIZATION_RECIPIENTS.
* Loops through the list of timesync recipients, and, for each one,
* adds its data to the APDU.
*
* BACnetRecipient ::= CHOICE {
* device [0] BACnetObjectIdentifier,
* address [1] BACnetAddress
* }
*
* BACnetAddress ::= SEQUENCE {
* network-number Unsigned16, -- A value of 0 indicates the local network
* mac-address OCTET STRING -- A string of length 0 indicates a broadcast
* }
*
* @param apdu [out] Buffer in which the APDU contents are built.
* @param max_apdu [in] Max length of the APDU buffer.
* @param recipient [in] BACNET_RECIPIENT_LIST type linked list of recipients.
*
* @return How many bytes were encoded in the buffer, or
* BACNET_STATUS_ABORT if the response would not fit within the buffer.
*/
int timesync_encode_timesync_recipients(
uint8_t * apdu,
unsigned max_apdu,
BACNET_RECIPIENT_LIST * recipient)
{
int len = 0;
int apdu_len = 0;
BACNET_OCTET_STRING octet_string;
BACNET_RECIPIENT_LIST *pRecipient;
pRecipient = recipient;
while (pRecipient != NULL) {
if (pRecipient->tag == 0) {
if (max_apdu >= (1 + 4)) {
/* CHOICE - device [0] BACnetObjectIdentifier */
len =
encode_context_object_id(&apdu[apdu_len], 0,
(int)pRecipient->type.device.type,
pRecipient->type.device.instance);
apdu_len += len;
} else {
return BACNET_STATUS_ABORT;
}
} else if (pRecipient->tag == 1) {
if (pRecipient->type.address.net) {
len = (int)(1 + 3 + 2 + pRecipient->type.address.len + 1);
} else {
len =
(int)(1 + 3 + 2 + pRecipient->type.address.mac_len + 1);
}
if (max_apdu >= (unsigned)len) {
/* CHOICE - address [1] BACnetAddress - opening */
len = encode_opening_tag(&apdu[apdu_len], 1);
apdu_len += len;
/* network-number Unsigned16, */
/* -- A value of 0 indicates the local network */
len =
encode_application_unsigned(&apdu[apdu_len],
pRecipient->type.address.net);
apdu_len += len;
/* mac-address OCTET STRING */
/* -- A string of length 0 indicates a broadcast */
if (pRecipient->type.address.net == BACNET_BROADCAST_NETWORK) {
octetstring_init(&octet_string, NULL, 0);
} else if (pRecipient->type.address.net) {
octetstring_init(&octet_string,
&pRecipient->type.address.adr[0],
pRecipient->type.address.len);
} else {
octetstring_init(&octet_string,
&pRecipient->type.address.mac[0],
pRecipient->type.address.mac_len);
}
len =
encode_application_octet_string(&apdu[apdu_len],
&octet_string);
apdu_len += len;
/* CHOICE - address [1] BACnetAddress - closing */
len = encode_closing_tag(&apdu[apdu_len], 1);
apdu_len += len;
} else {
/* not a valid tag - don't encode this one */
}
}
pRecipient = pRecipient->next;
}
return apdu_len;
}
/** Handle a request to decode a list of timesync recipients.
*
* Invoked by a request to write the Device object's
* PROP_TIME_SYNCHRONIZATION_RECIPIENTS.
* Loops through the list of timesync recipients, and, for each one,
* adds its data from the APDU.
*
* BACnetRecipient ::= CHOICE {
* device [0] BACnetObjectIdentifier,
* address [1] BACnetAddress
* }
*
* BACnetAddress ::= SEQUENCE {
* network-number Unsigned16, -- A value of 0 indicates the local network
* mac-address OCTET STRING -- A string of length 0 indicates a broadcast
* }
*
* @param apdu [in] Buffer in which the APDU contents are read
* @param max_apdu [in] length of the APDU buffer.
* @param recipient [out] BACNET_RECIPIENT_LIST type linked list of recipients.
*
* @return How many bytes were decoded from the buffer, or
* BACNET_STATUS_ABORT if there was a problem decoding the buffer
*/
int timesync_decode_timesync_recipients(
uint8_t * apdu,
unsigned max_apdu,
BACNET_RECIPIENT_LIST * recipient)
{
int len = 0;
int apdu_len = 0;
int tag_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint32_t unsigned_value = 0;
BACNET_OCTET_STRING octet_string;
BACNET_RECIPIENT_LIST *pRecipient;
pRecipient = recipient;
while (pRecipient != NULL) {
/* device [0] BACnetObjectIdentifier */
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
pRecipient->tag = 0;
len =
decode_context_object_id(&apdu[apdu_len], 0,
&pRecipient->type.device.type,
&pRecipient->type.device.instance);
if (len < 0) {
return BACNET_STATUS_ABORT;
}
apdu_len += len;
} else if (decode_is_context_tag(&apdu[apdu_len], 1)) {
apdu_len += 1;
pRecipient->tag = 1;
/* network-number Unsigned16 */
tag_len =
decode_tag_number_and_value(&apdu[apdu_len], &tag_number,
&len_value_type);
apdu_len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
return BACNET_STATUS_ABORT;
}
len =
decode_unsigned(&apdu[apdu_len], len_value_type,
&unsigned_value);
pRecipient->type.address.net = unsigned_value;
apdu_len += len;
/* mac-address OCTET STRING */
tag_len =
decode_tag_number_and_value(&apdu[apdu_len], &tag_number,
&len_value_type);
apdu_len += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) {
return BACNET_STATUS_ABORT;
}
len = decode_octet_string(&apdu[0], len_value_type, &octet_string);
apdu_len += len;
if (octetstring_length(&octet_string) == 0) {
/* -- A string of length 0 indicates a broadcast */
} else if (pRecipient->type.address.net) {
pRecipient->type.address.len =
octetstring_copy_value(&pRecipient->type.address.adr[0],
sizeof(pRecipient->type.address.adr), &octet_string);
} else {
pRecipient->type.address.mac_len =
octetstring_copy_value(&pRecipient->type.address.mac[0],
sizeof(pRecipient->type.address.mac), &octet_string);
}
} else {
return BACNET_STATUS_ABORT;
}
pRecipient = pRecipient->next;
}
return apdu_len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testTimeSyncRecipientData(
Test * pTest,
BACNET_RECIPIENT_LIST * recipient1,
BACNET_RECIPIENT_LIST * recipient2)
{
unsigned i = 0;
if (recipient1 && recipient2) {
ct_test(pTest, recipient1->tag == recipient2->tag);
if (recipient1->tag == 0) {
ct_test(pTest,
recipient1->type.device.type == recipient2->type.device.type);
ct_test(pTest,
recipient1->type.device.instance ==
recipient2->type.device.instance);
} else if (recipient1->tag == 1) {
ct_test(pTest,
recipient1->type.address.net == recipient2->type.address.net);
if (recipient1->type.address.net == BACNET_BROADCAST_NETWORK) {
ct_test(pTest,
recipient1->type.address.mac_len ==
recipient2->type.address.mac_len);
} else if (recipient1->type.address.net) {
ct_test(pTest,
recipient1->type.address.len ==
recipient2->type.address.len);
for (i = 0; i < recipient1->type.address.len; i++) {
ct_test(pTest,
recipient1->type.address.adr[i] ==
recipient2->type.address.adr[i]);
}
} else {
ct_test(pTest,
recipient1->type.address.mac_len ==
recipient2->type.address.mac_len);
for (i = 0; i < recipient1->type.address.mac_len; i++) {
ct_test(pTest,
recipient1->type.address.mac[i] ==
recipient2->type.address.mac[i]);
}
}
} else {
ct_test(pTest, recipient1->tag <= 1);
}
}
}
void testTimeSyncRecipient(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
BACNET_RECIPIENT_LIST recipient[4];
BACNET_RECIPIENT_LIST test_recipient[4];
/* link the recipient list */
recipient[0].next = &recipient[1];
recipient[1].next = &recipient[2];
recipient[2].next = &recipient[3];
recipient[3].next = NULL;
/* link the test recipient list */
test_recipient[0].next = &test_recipient[1];
test_recipient[1].next = &test_recipient[2];
test_recipient[2].next = &test_recipient[3];
test_recipient[3].next = NULL;
/* load the test data - device */
recipient[0].tag = 0;
recipient[0].type.device.type = OBJECT_DEVICE;
recipient[0].type.device.instance = 1234;
/* load the test data - address */
/* network = broadcast */
recipient[1].tag = 1;
recipient[1].type.address.net = BACNET_BROADCAST_NETWORK;
recipient[2].type.address.mac_len = 0;
/* network = non-zero */
recipient[1].tag = 1;
recipient[2].type.address.net = 4201;
recipient[2].type.address.adr[0] = 127;
recipient[2].type.address.len = 1;
/* network = zero */
recipient[2].type.address.net = 0;
recipient[2].type.address.mac[0] = 10;
recipient[2].type.address.mac[1] = 1;
recipient[2].type.address.mac[2] = 0;
recipient[2].type.address.mac[3] = 86;
recipient[2].type.address.mac[4] = 0xBA;
recipient[2].type.address.mac[5] = 0xC1;
recipient[2].type.address.mac_len = 6;
/* perform positive test */
len =
timesync_encode_timesync_recipients(&apdu[0], sizeof(apdu),
&recipient[0]);
ct_test(pTest, len != BACNET_STATUS_ABORT);
ct_test(pTest, len > 0);
len =
timesync_decode_timesync_recipients(&apdu[0], sizeof(apdu),
&test_recipient[0]);
ct_test(pTest, len != BACNET_STATUS_ABORT);
ct_test(pTest, len > 0);
testTimeSyncRecipientData(pTest, &recipient[0], &test_recipient[0]);
}
int timesync_decode_apdu_service(
uint8_t * apdu,
BACNET_UNCONFIRMED_SERVICE service,
unsigned apdu_len,
BACNET_DATE * my_date,
BACNET_TIME * my_time)
{
int len = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST)
return -1;
if (apdu[1] != service)
return -1;
/* optional limits - must be used as a pair */
if (apdu_len > 2) {
len =
timesync_decode_service_request(&apdu[2], apdu_len - 2, my_date,
my_time);
}
return len;
}
int timesync_utc_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
BACNET_DATE * my_date,
BACNET_TIME * my_time)
{
return timesync_decode_apdu_service(apdu,
SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, apdu_len, my_date,
my_time);
}
int timesync_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
BACNET_DATE * my_date,
BACNET_TIME * my_time)
{
return timesync_decode_apdu_service(apdu,
SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, apdu_len, my_date, my_time);
}
void testTimeSyncData(
Test * pTest,
BACNET_DATE * my_date,
BACNET_TIME * my_time)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_DATE test_date;
BACNET_TIME test_time;
len = timesync_encode_apdu(&apdu[0], my_date, my_time);
ct_test(pTest, len != 0);
apdu_len = len;
len = timesync_decode_apdu(&apdu[0], apdu_len, &test_date, &test_time);
ct_test(pTest, len != -1);
ct_test(pTest, datetime_compare_time(my_time, &test_time) == 0);
ct_test(pTest, datetime_compare_date(my_date, &test_date) == 0);
len = timesync_utc_encode_apdu(&apdu[0], my_date, my_time);
ct_test(pTest, len != 0);
apdu_len = len;
len = timesync_utc_decode_apdu(&apdu[0], apdu_len, &test_date, &test_time);
ct_test(pTest, len != -1);
ct_test(pTest, datetime_compare_time(my_time, &test_time) == 0);
ct_test(pTest, datetime_compare_date(my_date, &test_date) == 0);
}
void testTimeSync(
Test * pTest)
{
BACNET_DATE bdate;
BACNET_TIME btime;
bdate.year = 2006; /* AD */
bdate.month = 4; /* 1=Jan */
bdate.day = 11; /* 1..31 */
bdate.wday = 1; /* 1=Monday */
btime.hour = 7;
btime.min = 0;
btime.sec = 3;
btime.hundredths = 1;
testTimeSyncData(pTest, &bdate, &btime);
}
#ifdef TEST_TIMESYNC
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Time-Sync", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testTimeSync);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_WHOIS */
#endif /* TEST */
+410
View File
@@ -0,0 +1,410 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
Corrections by Ferran Arumi, 2007, Barcelona, Spain
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "bits.h"
#include "apdu.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "bacenum.h"
#include "tsm.h"
#include "config.h"
#include "datalink.h"
#include "handlers.h"
#include "address.h"
#include "bacaddr.h"
/** @file tsm.c BACnet Transaction State Machine operations */
#if (MAX_TSM_TRANSACTIONS)
/* Really only needed for segmented messages */
/* and a little for sending confirmed messages */
/* If we are only a server and only initiate broadcasts, */
/* then we don't need a TSM layer. */
/* FIXME: not coded for segmentation */
/* declare space for the TSM transactions, and set it up in the init. */
/* table rules: an Invoke ID = 0 is an unused spot in the table */
static BACNET_TSM_DATA TSM_List[MAX_TSM_TRANSACTIONS];
/* invoke ID for incrementing between subsequent calls. */
static uint8_t Current_Invoke_ID = 1;
static tsm_timeout_function Timeout_Function;
void tsm_set_timeout_handler(
tsm_timeout_function pFunction)
{
Timeout_Function = pFunction;
}
/* returns MAX_TSM_TRANSACTIONS if not found */
static uint8_t tsm_find_invokeID_index(
uint8_t invokeID)
{
unsigned i = 0; /* counter */
uint8_t index = MAX_TSM_TRANSACTIONS; /* return value */
for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) {
if (TSM_List[i].InvokeID == invokeID) {
index = (uint8_t) i;
break;
}
}
return index;
}
static uint8_t tsm_find_first_free_index(
void)
{
unsigned i = 0; /* counter */
uint8_t index = MAX_TSM_TRANSACTIONS; /* return value */
for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) {
if (TSM_List[i].InvokeID == 0) {
index = (uint8_t) i;
break;
}
}
return index;
}
bool tsm_transaction_available(
void)
{
bool status = false; /* return value */
unsigned i = 0; /* counter */
for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) {
if (TSM_List[i].InvokeID == 0) {
/* one is available! */
status = true;
break;
}
}
return status;
}
uint8_t tsm_transaction_idle_count(
void)
{
uint8_t count = 0; /* return value */
unsigned i = 0; /* counter */
for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) {
if ((TSM_List[i].InvokeID == 0) &&
(TSM_List[i].state == TSM_STATE_IDLE)) {
/* one is available! */
count++;
}
}
return count;
}
/* sets the invokeID */
void tsm_invokeID_set(
uint8_t invokeID)
{
if (invokeID == 0) {
invokeID = 1;
}
Current_Invoke_ID = invokeID;
}
/* gets the next free invokeID,
and reserves a spot in the table
returns 0 if none are available */
uint8_t tsm_next_free_invokeID(
void)
{
uint8_t index = 0;
uint8_t invokeID = 0;
bool found = false;
/* is there even space available? */
if (tsm_transaction_available()) {
while (!found) {
index = tsm_find_invokeID_index(Current_Invoke_ID);
if (index == MAX_TSM_TRANSACTIONS) {
/* Not found, so this invokeID is not used */
found = true;
/* set this id into the table */
index = tsm_find_first_free_index();
if (index != MAX_TSM_TRANSACTIONS) {
TSM_List[index].InvokeID = invokeID = Current_Invoke_ID;
TSM_List[index].state = TSM_STATE_IDLE;
TSM_List[index].RequestTimer = apdu_timeout();
/* update for the next call or check */
Current_Invoke_ID++;
/* skip zero - we treat that internally as invalid or no free */
if (Current_Invoke_ID == 0) {
Current_Invoke_ID = 1;
}
}
} else {
/* found! This invokeID is already used */
/* try next one */
Current_Invoke_ID++;
/* skip zero - we treat that internally as invalid or no free */
if (Current_Invoke_ID == 0) {
Current_Invoke_ID = 1;
}
}
}
}
return invokeID;
}
void tsm_set_confirmed_unsegmented_transaction(
uint8_t invokeID,
BACNET_ADDRESS * dest,
BACNET_NPDU_DATA * ndpu_data,
uint8_t * apdu,
uint16_t apdu_len)
{
uint16_t j = 0;
uint8_t index;
if (invokeID) {
index = tsm_find_invokeID_index(invokeID);
if (index < MAX_TSM_TRANSACTIONS) {
/* SendConfirmedUnsegmented */
TSM_List[index].state = TSM_STATE_AWAIT_CONFIRMATION;
TSM_List[index].RetryCount = 0;
/* start the timer */
TSM_List[index].RequestTimer = apdu_timeout();
/* copy the data */
for (j = 0; j < apdu_len; j++) {
TSM_List[index].apdu[j] = apdu[j];
}
TSM_List[index].apdu_len = apdu_len;
npdu_copy_data(&TSM_List[index].npdu_data, ndpu_data);
bacnet_address_copy(&TSM_List[index].dest, dest);
}
}
return;
}
/* used to retrieve the transaction payload */
/* if we wanted to find out what we sent (i.e. when we get an ack) */
bool tsm_get_transaction_pdu(
uint8_t invokeID,
BACNET_ADDRESS * dest,
BACNET_NPDU_DATA * ndpu_data,
uint8_t * apdu,
uint16_t * apdu_len)
{
uint16_t j = 0;
uint8_t index;
bool found = false;
if (invokeID) {
index = tsm_find_invokeID_index(invokeID);
/* how much checking is needed? state? dest match? just invokeID? */
if (index < MAX_TSM_TRANSACTIONS) {
/* FIXME: we may want to free the transaction so it doesn't timeout */
/* retrieve the transaction */
/* FIXME: bounds check the pdu_len? */
*apdu_len = (uint16_t) TSM_List[index].apdu_len;
for (j = 0; j < *apdu_len; j++) {
apdu[j] = TSM_List[index].apdu[j];
}
npdu_copy_data(ndpu_data, &TSM_List[index].npdu_data);
bacnet_address_copy(dest, &TSM_List[index].dest);
found = true;
}
}
return found;
}
/* called once a millisecond or slower */
void tsm_timer_milliseconds(
uint16_t milliseconds)
{
unsigned i = 0; /* counter */
for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) {
if (TSM_List[i].state == TSM_STATE_AWAIT_CONFIRMATION) {
if (TSM_List[i].RequestTimer > milliseconds)
TSM_List[i].RequestTimer -= milliseconds;
else
TSM_List[i].RequestTimer = 0;
/* AWAIT_CONFIRMATION */
if (TSM_List[i].RequestTimer == 0) {
if (TSM_List[i].RetryCount < apdu_retries()) {
TSM_List[i].RequestTimer = apdu_timeout();
TSM_List[i].RetryCount++;
datalink_send_pdu(&TSM_List[i].dest,
&TSM_List[i].npdu_data, &TSM_List[i].apdu[0],
TSM_List[i].apdu_len);
} else {
/* note: the invoke id has not been cleared yet
and this indicates a failed message:
IDLE and a valid invoke id */
TSM_List[i].state = TSM_STATE_IDLE;
if (TSM_List[i].InvokeID != 0) {
if (Timeout_Function) {
Timeout_Function(TSM_List[i].InvokeID);
}
}
}
}
}
}
}
/* frees the invokeID and sets its state to IDLE */
void tsm_free_invoke_id(
uint8_t invokeID)
{
uint8_t index;
index = tsm_find_invokeID_index(invokeID);
if (index < MAX_TSM_TRANSACTIONS) {
TSM_List[index].state = TSM_STATE_IDLE;
TSM_List[index].InvokeID = 0;
}
}
/** Check if the invoke ID has been made free by the Transaction State Machine.
* @param invokeID [in] The invokeID to be checked, normally of last message sent.
* @return True if it is free (done with), False if still pending in the TSM.
*/
bool tsm_invoke_id_free(
uint8_t invokeID)
{
bool status = true;
uint8_t index;
index = tsm_find_invokeID_index(invokeID);
if (index < MAX_TSM_TRANSACTIONS)
status = false;
return status;
}
/** See if we failed get a confirmation for the message associated
* with this invoke ID.
* @param invokeID [in] The invokeID to be checked, normally of last message sent.
* @return True if already failed, False if done or segmented or still waiting
* for a confirmation.
*/
bool tsm_invoke_id_failed(
uint8_t invokeID)
{
bool status = false;
uint8_t index;
index = tsm_find_invokeID_index(invokeID);
if (index < MAX_TSM_TRANSACTIONS) {
/* a valid invoke ID and the state is IDLE is a
message that failed to confirm */
if (TSM_List[index].state == TSM_STATE_IDLE)
status = true;
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
/* flag to send an I-Am */
bool I_Am_Request = true;
/* dummy function stubs */
int datalink_send_pdu(
BACNET_ADDRESS * dest,
BACNET_NPDU_DATA * npdu_data,
uint8_t * pdu,
unsigned pdu_len)
{
(void) dest;
(void) npdu_data;
(void) pdu;
(void) pdu_len;
return 0;
}
/* dummy function stubs */
void datalink_get_broadcast_address(
BACNET_ADDRESS * dest)
{
(void) dest;
}
void testTSM(
Test * pTest)
{
/* FIXME: add some unit testing... */
return;
}
#ifdef TEST_TSM
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet TSM", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testTSM);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_TSM */
#endif /* TEST */
#endif /* MAX_TSM_TRANSACTIONS */
+221
View File
@@ -0,0 +1,221 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright (C) 2008 John Crispin <blogic@openwrt.org>
*/
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <uci_config.h>
#include <uci.h>
#include "ucix.h"
/*#include "log.h" */
static struct uci_ptr ptr;
static inline int ucix_get_ptr(
struct uci_context *ctx,
const char *p,
const char *s,
const char *o,
const char *t)
{
memset(&ptr, 0, sizeof(ptr));
ptr.package = p;
ptr.section = s;
ptr.option = o;
ptr.value = t;
return uci_lookup_ptr(ctx, &ptr, NULL, true);
}
struct uci_context *ucix_init(
const char *config_file)
{
struct uci_context *ctx = uci_alloc_context();
/* uci_add_history_path(ctx, "/var/state"); */
uci_add_delta_path(ctx, "/var/state");
if (uci_load(ctx, config_file, NULL) != UCI_OK) {
fprintf(stderr, "%s/%s is missing or corrupt\n", ctx->savedir,
config_file);
return NULL;
}
return ctx;
}
struct uci_context *ucix_init_path(
const char *path,
const char *config_file)
{
struct uci_context *ctx = uci_alloc_context();
if (path)
uci_set_confdir(ctx, path);
if (uci_load(ctx, config_file, NULL) != UCI_OK) {
fprintf(stderr, "%s/%s is missing or corrupt\n", ctx->savedir,
config_file);
return NULL;
}
return ctx;
}
void ucix_cleanup(
struct uci_context *ctx)
{
uci_free_context(ctx);
}
void ucix_save(
struct uci_context *ctx)
{
uci_set_savedir(ctx, "/tmp/.uci/");
uci_save(ctx, NULL);
}
void ucix_save_state(
struct uci_context *ctx)
{
uci_set_savedir(ctx, "/var/state/");
uci_save(ctx, NULL);
}
const char *ucix_get_option(
struct uci_context *ctx,
const char *p,
const char *s,
const char *o)
{
struct uci_element *e = NULL;
const char *value = NULL;
if (ucix_get_ptr(ctx, p, s, o, NULL))
return NULL;
if (!(ptr.flags & UCI_LOOKUP_COMPLETE))
return NULL;
e = ptr.last;
switch (e->type) {
case UCI_TYPE_SECTION:
value = uci_to_section(e)->type;
break;
case UCI_TYPE_OPTION:
switch (ptr.o->type) {
case UCI_TYPE_STRING:
value = ptr.o->v.string;
break;
default:
value = NULL;
break;
}
break;
default:
return 0;
}
return value;
}
int ucix_get_option_int(
struct uci_context *ctx,
const char *p,
const char *s,
const char *o,
int def)
{
const char *tmp = ucix_get_option(ctx, p, s, o);
int ret = def;
if (tmp)
ret = atoi(tmp);
return ret;
}
void ucix_add_section(
struct uci_context *ctx,
const char *p,
const char *s,
const char *t)
{
if (ucix_get_ptr(ctx, p, s, NULL, t))
return;
uci_set(ctx, &ptr);
}
void ucix_add_option(
struct uci_context *ctx,
const char *p,
const char *s,
const char *o,
const char *t)
{
if (ucix_get_ptr(ctx, p, s, o, (t) ? (t) : ("")))
return;
uci_set(ctx, &ptr);
}
void ucix_add_option_int(
struct uci_context *ctx,
const char *p,
const char *s,
const char *o,
int t)
{
char tmp[64];
snprintf(tmp, 64, "%d", t);
ucix_add_option(ctx, p, s, o, tmp);
}
void ucix_del(
struct uci_context *ctx,
const char *p,
const char *s,
const char *o)
{
if (!ucix_get_ptr(ctx, p, s, o, NULL))
uci_delete(ctx, &ptr);
}
void ucix_revert(
struct uci_context *ctx,
const char *p,
const char *s,
const char *o)
{
if (!ucix_get_ptr(ctx, p, s, o, NULL))
uci_revert(ctx, &ptr);
}
void ucix_for_each_section_type(
struct uci_context *ctx,
const char *p,
const char *t,
void (*cb) (const char *,
void *),
void *priv)
{
struct uci_element *e;
if (ucix_get_ptr(ctx, p, NULL, NULL, NULL))
return;
uci_foreach_element(&ptr.p->sections, e)
if (!strcmp(t, uci_to_section(e)->type))
cb(e->name, priv);
}
int ucix_commit(
struct uci_context *ctx,
const char *p)
{
if (ucix_get_ptr(ctx, p, NULL, NULL, NULL))
return 1;
return uci_commit(ctx, &ptr.p, false);
}
+38
View File
@@ -0,0 +1,38 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2007 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include "version.h"
/** @file version.c Sets the BACnet Version. */
char *BACnet_Version = BACNET_VERSION_TEXT;
+326
View File
@@ -0,0 +1,326 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2015 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "config.h"
#include "bacdef.h"
#include "keylist.h"
/* me! */
#include "vmac.h"
/** @file
Handle VMAC address binding */
/* This module is used to handle the virtual MAC address binding that */
/* occurs in BACnet for ZigBee or IPv6. */
/* Key List for storing the object data sorted by instance number */
static OS_Keylist VMAC_List;
/**
* Returns the number of VMAC in the list
*/
unsigned int VMAC_Count(void)
{
return (unsigned int)Keylist_Count(VMAC_List);
}
/**
* Adds a VMAC to the list
*
* @param device_id - BACnet device object instance number
* @param src - BACnet/IPv6 address
*
* @return true if the device ID and MAC are added
*/
bool VMAC_Add(uint32_t device_id, struct vmac_data *src)
{
bool status = false;
struct vmac_data *pVMAC = NULL;
int index = 0;
size_t i = 0;
pVMAC = Keylist_Data(VMAC_List, device_id);
if (!pVMAC) {
pVMAC = calloc(1, sizeof(struct vmac_data));
if (pVMAC) {
/* copy the MAC into the data store */
for (i = 0; i < sizeof(pVMAC->mac); i++) {
if (i < src->mac_len) {
pVMAC->mac[i] = src->mac[i];
} else {
break;
}
}
pVMAC->mac_len = src->mac_len;
index = Keylist_Data_Add(VMAC_List, device_id, pVMAC);
if (index >= 0) {
status = true;
printf("VMAC %u added.\n", (unsigned int)device_id);
}
}
}
return status;
}
/**
* Finds a VMAC in the list by seeking the Device ID, and deletes it.
*
* @param device_id - BACnet device object instance number
*
* @return pointer to the VMAC data from the list - be sure to free() it!
*/
bool VMAC_Delete(uint32_t device_id)
{
bool status = false;
struct vmac_data *pVMAC;
pVMAC = Keylist_Data_Delete(VMAC_List, device_id);
if (pVMAC) {
free(pVMAC);
status = true;
}
return status;
}
/**
* Finds a VMAC in the list by seeking the Device ID.
*
* @param device_id - BACnet device object instance number
*
* @return pointer to the VMAC data from the list
*/
struct vmac_data *VMAC_Find_By_Key(uint32_t device_id)
{
return Keylist_Data(VMAC_List, device_id);
}
/** Compare the VMAC address
*
* @param vmac1 - VMAC address that will be compared to vmac2
* @param vmac2 - VMAC address that will be compared to vmac1
*
* @return true if the addresses are different
*/
bool VMAC_Different(
struct vmac_data *vmac1,
struct vmac_data *vmac2)
{
bool status = false;
unsigned int i = 0;
unsigned int mac_len = VMAC_MAC_MAX;
if (vmac1 && vmac2) {
if (vmac1->mac_len != vmac2->mac_len) {
status = true;
} else {
if (vmac1->mac_len < mac_len) {
mac_len = (unsigned int)vmac1->mac_len;
}
for (i = 0; i < mac_len; i++) {
if (vmac1->mac[i] != vmac2->mac[i]) {
status = true;
}
}
}
}
return status;
}
/** Compare the VMAC address
*
* @param vmac1 - VMAC address that will be compared to vmac2
* @param vmac2 - VMAC address that will be compared to vmac1
*
* @return true if the addresses are the same
*/
bool VMAC_Match(
struct vmac_data *vmac1,
struct vmac_data *vmac2)
{
bool status = false;
unsigned int i = 0;
unsigned int mac_len = VMAC_MAC_MAX;
if (vmac1 && vmac2 && vmac1->mac_len) {
status = true;
if (vmac1->mac_len != vmac2->mac_len) {
status = false;
} else {
if (vmac1->mac_len < mac_len) {
mac_len = (unsigned int)vmac1->mac_len;
}
for (i = 0; i < mac_len; i++) {
if (vmac1->mac[i] != vmac2->mac[i]) {
status = false;
}
}
}
}
return status;
}
/**
* Finds a VMAC in the list by seeking a matching VMAC address
*
* @param vmac - VMAC address that will be sought
* @param device_id - BACnet device object instance number
*
* @return true if the VMAC address was found
*/
bool VMAC_Find_By_Data(struct vmac_data *vmac, uint32_t *device_id)
{
bool status = false;
struct vmac_data *list_vmac;
int count = 0;
int index = 0;
count = Keylist_Count(VMAC_List);
while (count) {
index = count - 1;
list_vmac = Keylist_Data_Index(VMAC_List, index);
if (list_vmac) {
if (VMAC_Match(vmac, list_vmac)) {
if (device_id) {
*device_id = Keylist_Key(VMAC_List, index);
}
status = true;
break;
}
}
count--;
}
return status;
}
/**
* Cleans up the memory used by the VMAC list data
*/
void VMAC_Cleanup(void)
{
struct vmac_data *pVMAC;
if (VMAC_List) {
do {
pVMAC = Keylist_Data_Pop(VMAC_List);
if (pVMAC) {
free(pVMAC);
}
} while (pVMAC);
Keylist_Delete(VMAC_List);
VMAC_List = NULL;
}
}
/**
* Initializes the VMAC list data
*/
void VMAC_Init(void)
{
VMAC_List = Keylist_Create();
if (VMAC_List) {
atexit(VMAC_Cleanup);
printf("VMAC List initialized.\n");
}
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testVMAC(
Test * pTest)
{
uint32_t device_id = 123;
uint32_t test_device_id = 0;
struct vmac_data test_vmac_data;
struct vmac_data *pVMAC;
unsigned int i = 0;
bool status = false;
VMAC_Init();
for (i = 0; i < VMAC_MAC_MAX; i++) {
test_vmac_data.mac[i] = 1 + i;
}
test_vmac_data.mac_len = VMAC_MAC_MAX;
status = VMAC_Add(device_id, &test_vmac_data);
ct_test(pTest, status);
pVMAC = VMAC_Find_By_Key(0);
ct_test(pTest, pVMAC == NULL);
pVMAC = VMAC_Find_By_Key(device_id);
ct_test(pTest, pVMAC);
status = VMAC_Different(pVMAC, &test_vmac_data);
ct_test(pTest, !status);
status = VMAC_Match(pVMAC, &test_vmac_data);
ct_test(pTest, status);
status = VMAC_Find_By_Data(&test_vmac_data, &test_device_id);
ct_test(pTest, status);
ct_test(pTest, test_device_id == device_id);
status = VMAC_Delete(device_id);
ct_test(pTest, status);
pVMAC = VMAC_Find_By_Key(device_id);
ct_test(pTest, pVMAC == NULL);
VMAC_Cleanup();
}
#ifdef TEST_VMAC
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet VMAC", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testVMAC);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif
+258
View File
@@ -0,0 +1,258 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2006 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "whohas.h"
/** @file whohas.c Encode/Decode Who-Has requests */
/* encode service - use -1 for limit for unlimited */
int whohas_encode_apdu(
uint8_t * apdu,
BACNET_WHO_HAS_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu && data) {
apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST;
apdu[1] = SERVICE_UNCONFIRMED_WHO_HAS; /* service choice */
apdu_len = 2;
/* optional limits - must be used as a pair */
if ((data->low_limit >= 0)
&& (data->low_limit <= BACNET_MAX_INSTANCE)
&& (data->high_limit >= 0)
&& (data->high_limit <= BACNET_MAX_INSTANCE)) {
len = encode_context_unsigned(&apdu[apdu_len], 0, data->low_limit);
apdu_len += len;
len =
encode_context_unsigned(&apdu[apdu_len], 1, data->high_limit);
apdu_len += len;
}
if (data->is_object_name) {
len =
encode_context_character_string(&apdu[apdu_len], 3,
&data->object.name);
apdu_len += len;
} else {
len =
encode_context_object_id(&apdu[apdu_len], 2,
(int) data->object.identifier.type,
data->object.identifier.instance);
apdu_len += len;
}
}
return apdu_len;
}
/* decode the service request only */
int whohas_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_WHO_HAS_DATA * data)
{
int len = 0;
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t decoded_value = 0; /* for decoding */
uint16_t decoded_type = 0; /* for decoding */
if (apdu_len && data) {
/* optional limits - must be used as a pair */
if (decode_is_context_tag(&apdu[len], 0)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
if (decoded_value <= BACNET_MAX_INSTANCE)
data->low_limit = decoded_value;
if (!decode_is_context_tag(&apdu[len], 1))
return -1;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
if (decoded_value <= BACNET_MAX_INSTANCE)
data->high_limit = decoded_value;
} else {
data->low_limit = -1;
data->high_limit = -1;
}
/* object id */
if (decode_is_context_tag(&apdu[len], 2)) {
data->is_object_name = false;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_object_id(&apdu[len], &decoded_type,
&data->object.identifier.instance);
data->object.identifier.type = decoded_type;
}
/* object name */
else if (decode_is_context_tag(&apdu[len], 3)) {
data->is_object_name = true;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_character_string(&apdu[len], len_value,
&data->object.name);
}
/* missing required parameters */
else
return -1;
}
return len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
int whohas_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
BACNET_WHO_HAS_DATA * data)
{
int len = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST)
return -1;
if (apdu[1] != SERVICE_UNCONFIRMED_WHO_HAS)
return -1;
/* optional limits - must be used as a pair */
if (apdu_len > 2) {
len = whohas_decode_service_request(&apdu[2], apdu_len - 2, data);
}
return len;
}
void testWhoHasData(
Test * pTest,
BACNET_WHO_HAS_DATA * data)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_WHO_HAS_DATA test_data;
len = whohas_encode_apdu(&apdu[0], data);
ct_test(pTest, len != 0);
apdu_len = len;
len = whohas_decode_apdu(&apdu[0], apdu_len, &test_data);
ct_test(pTest, len != -1);
ct_test(pTest, test_data.low_limit == data->low_limit);
ct_test(pTest, test_data.high_limit == data->high_limit);
ct_test(pTest, test_data.is_object_name == data->is_object_name);
/* Object ID */
if (data->is_object_name == false) {
ct_test(pTest,
test_data.object.identifier.type == data->object.identifier.type);
ct_test(pTest,
test_data.object.identifier.instance ==
data->object.identifier.instance);
}
/* Object Name */
else {
ct_test(pTest, characterstring_same(&test_data.object.name,
&data->object.name));
}
}
void testWhoHas(
Test * pTest)
{
BACNET_WHO_HAS_DATA data;
data.low_limit = -1;
data.high_limit = -1;
data.is_object_name = false;
data.object.identifier.type = OBJECT_ANALOG_INPUT;
data.object.identifier.instance = 1;
testWhoHasData(pTest, &data);
for (data.low_limit = 0; data.low_limit <= BACNET_MAX_INSTANCE;
data.low_limit += (BACNET_MAX_INSTANCE / 4)) {
for (data.high_limit = 0; data.high_limit <= BACNET_MAX_INSTANCE;
data.high_limit += (BACNET_MAX_INSTANCE / 4)) {
data.is_object_name = false;
for (data.object.identifier.type = OBJECT_ANALOG_INPUT;
data.object.identifier.type < MAX_BACNET_OBJECT_TYPE;
data.object.identifier.type++) {
for (data.object.identifier.instance = 1;
data.object.identifier.instance <= BACNET_MAX_INSTANCE;
data.object.identifier.instance <<= 1) {
testWhoHasData(pTest, &data);
}
}
data.is_object_name = true;
characterstring_init_ansi(&data.object.name, "patricia");
testWhoHasData(pTest, &data);
}
}
}
#ifdef TEST_WHOHAS
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Who-Has", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testWhoHas);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_WHOIS */
#endif /* TEST */
+241
View File
@@ -0,0 +1,241 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "whois.h"
/** @file whois.c Encode/Decode Who-Is requests */
/* encode I-Am service - use -1 for limit if you want unlimited */
int whois_encode_apdu(
uint8_t * apdu,
int32_t low_limit,
int32_t high_limit)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST;
apdu[1] = SERVICE_UNCONFIRMED_WHO_IS; /* service choice */
apdu_len = 2;
/* optional limits - must be used as a pair */
if ((low_limit >= 0) && (low_limit <= BACNET_MAX_INSTANCE) &&
(high_limit >= 0) && (high_limit <= BACNET_MAX_INSTANCE)) {
len = encode_context_unsigned(&apdu[apdu_len], 0, low_limit);
apdu_len += len;
len = encode_context_unsigned(&apdu[apdu_len], 1, high_limit);
apdu_len += len;
}
}
return apdu_len;
}
/* decode the service request only */
int whois_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
int32_t * pLow_limit,
int32_t * pHigh_limit)
{
unsigned int len = 0;
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t decoded_value = 0;
/* optional limits - must be used as a pair */
if (apdu_len) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number != 0) {
return BACNET_STATUS_ERROR;
}
if (apdu_len > (unsigned)len) {
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
if (decoded_value <= BACNET_MAX_INSTANCE) {
if (pLow_limit) {
*pLow_limit = decoded_value;
}
}
if (apdu_len > (unsigned)len) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number != 1) {
return BACNET_STATUS_ERROR;
}
if (apdu_len > (unsigned)len) {
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
if (decoded_value <= BACNET_MAX_INSTANCE) {
if (pHigh_limit) {
*pHigh_limit = decoded_value;
}
}
} else {
return BACNET_STATUS_ERROR;
}
} else {
return BACNET_STATUS_ERROR;
}
} else {
return BACNET_STATUS_ERROR;
}
} else {
if (pLow_limit) {
*pLow_limit = -1;
}
if (pHigh_limit) {
*pHigh_limit = -1;
}
len = 0;
}
return (int)len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
int whois_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
int32_t * pLow_limit,
int32_t * pHigh_limit)
{
int len = 0;
if (!apdu) {
return BACNET_STATUS_ERROR;
}
/* optional limits - must be used as a pair */
if (apdu_len >= 2) {
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) {
return BACNET_STATUS_ERROR;
}
if (apdu[1] != SERVICE_UNCONFIRMED_WHO_IS) {
return BACNET_STATUS_ERROR;
}
len =
whois_decode_service_request(&apdu[2], apdu_len - 2, pLow_limit,
pHigh_limit);
}
return len;
}
void testWhoIs(
Test * pTest)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
int32_t low_limit = -1;
int32_t high_limit = -1;
int32_t test_low_limit = 0;
int32_t test_high_limit = 0;
/* normal who-is without limits */
len = whois_encode_apdu(&apdu[0], low_limit, high_limit);
ct_test(pTest, len > 0);
apdu_len = len;
len =
whois_decode_apdu(&apdu[0], apdu_len, &test_low_limit,
&test_high_limit);
ct_test(pTest, len != BACNET_STATUS_ERROR);
ct_test(pTest, test_low_limit == low_limit);
ct_test(pTest, test_high_limit == high_limit);
/* normal who-is with limits - complete range */
for (low_limit = 0; low_limit <= BACNET_MAX_INSTANCE;
low_limit += (BACNET_MAX_INSTANCE / 4)) {
for (high_limit = 0; high_limit <= BACNET_MAX_INSTANCE;
high_limit += (BACNET_MAX_INSTANCE / 4)) {
len = whois_encode_apdu(&apdu[0], low_limit, high_limit);
apdu_len = len;
ct_test(pTest, len > 0);
len =
whois_decode_apdu(&apdu[0], apdu_len, &test_low_limit,
&test_high_limit);
ct_test(pTest, len != BACNET_STATUS_ERROR);
ct_test(pTest, test_low_limit == low_limit);
ct_test(pTest, test_high_limit == high_limit);
}
}
/* abnormal case:
who-is with no limits, but with APDU containing 2 limits */
low_limit = 0;
high_limit = 0;
len = whois_encode_apdu(&apdu[0], low_limit, high_limit);
ct_test(pTest, len > 0);
apdu_len = len;
low_limit = -1;
high_limit = -1;
len = whois_encode_apdu(&apdu[0], low_limit, high_limit);
ct_test(pTest, len > 0);
apdu_len = len;
len =
whois_decode_apdu(&apdu[0], apdu_len, &test_low_limit,
&test_high_limit);
ct_test(pTest, len != BACNET_STATUS_ERROR);
ct_test(pTest, test_low_limit == low_limit);
ct_test(pTest, test_high_limit == high_limit);
}
#ifdef TEST_WHOIS
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Who-Is", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testWhoIs);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_WHOIS */
#endif /* TEST */
+388
View File
@@ -0,0 +1,388 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "wp.h"
/** @file wp.c Encode/Decode BACnet Write Property APDUs */
#if BACNET_SVC_WP_A
/* encode service */
int wp_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_WRITE_PROPERTY_DATA * wpdata)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_WRITE_PROPERTY; /* service choice */
apdu_len = 4;
len =
encode_context_object_id(&apdu[apdu_len], 0, wpdata->object_type,
wpdata->object_instance);
apdu_len += len;
len =
encode_context_enumerated(&apdu[apdu_len], 1,
wpdata->object_property);
apdu_len += len;
/* optional array index; ALL is -1 which is assumed when missing */
if (wpdata->array_index != BACNET_ARRAY_ALL) {
len =
encode_context_unsigned(&apdu[apdu_len], 2,
wpdata->array_index);
apdu_len += len;
}
/* propertyValue */
len = encode_opening_tag(&apdu[apdu_len], 3);
apdu_len += len;
for (len = 0; len < wpdata->application_data_len; len++) {
apdu[apdu_len + len] = wpdata->application_data[len];
}
apdu_len += wpdata->application_data_len;
len = encode_closing_tag(&apdu[apdu_len], 3);
apdu_len += len;
/* optional priority - 0 if not set, 1..16 if set */
if (wpdata->priority != BACNET_NO_PRIORITY) {
len =
encode_context_unsigned(&apdu[apdu_len], 4, wpdata->priority);
apdu_len += len;
}
}
return apdu_len;
}
#endif
/* decode the service request only */
/* FIXME: there could be various error messages returned
using unique values less than zero */
int wp_decode_service_request(
uint8_t * apdu,
unsigned apdu_len,
BACNET_WRITE_PROPERTY_DATA * wpdata)
{
int len = 0;
int tag_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint16_t type = 0; /* for decoding */
uint32_t property = 0; /* for decoding */
uint32_t unsigned_value = 0;
int i = 0; /* loop counter */
/* check for value pointers */
if (apdu_len && wpdata) {
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[len++], 0))
return -1;
len += decode_object_id(&apdu[len], &type, &wpdata->object_instance);
wpdata->object_type = (BACNET_OBJECT_TYPE) type;
/* Tag 1: Property ID */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number != 1)
return -1;
len += decode_enumerated(&apdu[len], len_value_type, &property);
wpdata->object_property = (BACNET_PROPERTY_ID) property;
/* Tag 2: Optional Array Index */
/* note: decode without incrementing len so we can check for opening tag */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number == 2) {
len += tag_len;
len +=
decode_unsigned(&apdu[len], len_value_type, &unsigned_value);
wpdata->array_index = unsigned_value;
} else
wpdata->array_index = BACNET_ARRAY_ALL;
/* Tag 3: opening context tag */
if (!decode_is_opening_tag_number(&apdu[len], 3))
return -1;
/* determine the length of the data blob */
wpdata->application_data_len =
bacapp_data_len(&apdu[len], apdu_len - len,
(BACNET_PROPERTY_ID) property);
/* a tag number of 3 is not extended so only one octet */
len++;
/* copy the data from the APDU */
for (i = 0; i < wpdata->application_data_len; i++) {
wpdata->application_data[i] = apdu[len + i];
}
/* add on the data length */
len += wpdata->application_data_len;
if (!decode_is_closing_tag_number(&apdu[len], 3))
return -2;
/* a tag number of 3 is not extended so only one octet */
len++;
/* Tag 4: optional Priority - assumed MAX if not explicitly set */
wpdata->priority = BACNET_MAX_PRIORITY;
if ((unsigned) len < apdu_len) {
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number == 4) {
len += tag_len;
len =
decode_unsigned(&apdu[len], len_value_type,
&unsigned_value);
if ((unsigned_value >= BACNET_MIN_PRIORITY)
&& (unsigned_value <= BACNET_MAX_PRIORITY)) {
wpdata->priority = (uint8_t) unsigned_value;
} else
return -5;
}
}
}
return len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
int wp_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
BACNET_WRITE_PROPERTY_DATA * wpdata)
{
int len = 0;
unsigned offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST)
return -1;
/* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_WRITE_PROPERTY)
return -1;
offset = 4;
if (apdu_len > offset) {
len =
wp_decode_service_request(&apdu[offset], apdu_len - offset,
wpdata);
}
return len;
}
void testWritePropertyTag(
Test * pTest,
BACNET_APPLICATION_DATA_VALUE * value)
{
BACNET_WRITE_PROPERTY_DATA wpdata = { 0 };
BACNET_WRITE_PROPERTY_DATA test_data = { 0 };
BACNET_APPLICATION_DATA_VALUE test_value;
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t invoke_id = 128;
uint8_t test_invoke_id = 0;
wpdata.application_data_len =
bacapp_encode_application_data(&wpdata.application_data[0], value);
len = wp_encode_apdu(&apdu[0], invoke_id, &wpdata);
ct_test(pTest, len != 0);
/* decode the data */
apdu_len = len;
len = wp_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data);
ct_test(pTest, len != -1);
ct_test(pTest, test_data.object_type == wpdata.object_type);
ct_test(pTest, test_data.object_instance == wpdata.object_instance);
ct_test(pTest, test_data.object_property == wpdata.object_property);
ct_test(pTest, test_data.array_index == wpdata.array_index);
/* decode the application value of the request */
len =
bacapp_decode_application_data(test_data.application_data,
test_data.application_data_len, &test_value);
ct_test(pTest, test_value.tag == value->tag);
switch (test_value.tag) {
case BACNET_APPLICATION_TAG_NULL:
break;
case BACNET_APPLICATION_TAG_BOOLEAN:
ct_test(pTest, test_value.type.Boolean == value->type.Boolean);
break;
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
ct_test(pTest,
test_value.type.Unsigned_Int == value->type.Unsigned_Int);
break;
case BACNET_APPLICATION_TAG_SIGNED_INT:
ct_test(pTest,
test_value.type.Signed_Int == value->type.Signed_Int);
break;
case BACNET_APPLICATION_TAG_REAL:
ct_test(pTest, test_value.type.Real == value->type.Real);
break;
case BACNET_APPLICATION_TAG_ENUMERATED:
ct_test(pTest,
test_value.type.Enumerated == value->type.Enumerated);
break;
case BACNET_APPLICATION_TAG_DATE:
ct_test(pTest, test_value.type.Date.year == value->type.Date.year);
ct_test(pTest,
test_value.type.Date.month == value->type.Date.month);
ct_test(pTest, test_value.type.Date.day == value->type.Date.day);
ct_test(pTest, test_value.type.Date.wday == value->type.Date.wday);
break;
case BACNET_APPLICATION_TAG_TIME:
ct_test(pTest, test_value.type.Time.hour == value->type.Time.hour);
ct_test(pTest, test_value.type.Time.min == value->type.Time.min);
ct_test(pTest, test_value.type.Time.sec == value->type.Time.sec);
ct_test(pTest,
test_value.type.Time.hundredths ==
value->type.Time.hundredths);
break;
case BACNET_APPLICATION_TAG_OBJECT_ID:
ct_test(pTest,
test_value.type.Object_Id.type == value->type.Object_Id.type);
ct_test(pTest,
test_value.type.Object_Id.instance ==
value->type.Object_Id.instance);
break;
default:
break;
}
}
void testWriteProperty(
Test * pTest)
{
BACNET_APPLICATION_DATA_VALUE value;
value.tag = BACNET_APPLICATION_TAG_NULL;
testWritePropertyTag(pTest, &value);
value.tag = BACNET_APPLICATION_TAG_BOOLEAN;
value.type.Boolean = true;
testWritePropertyTag(pTest, &value);
value.type.Boolean = false;
testWritePropertyTag(pTest, &value);
value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT;
value.type.Unsigned_Int = 0;
testWritePropertyTag(pTest, &value);
value.type.Unsigned_Int = 0xFFFF;
testWritePropertyTag(pTest, &value);
value.type.Unsigned_Int = 0xFFFFFFFF;
testWritePropertyTag(pTest, &value);
value.tag = BACNET_APPLICATION_TAG_SIGNED_INT;
value.type.Signed_Int = 0;
testWritePropertyTag(pTest, &value);
value.type.Signed_Int = -1;
testWritePropertyTag(pTest, &value);
value.type.Signed_Int = 32768;
testWritePropertyTag(pTest, &value);
value.type.Signed_Int = -32768;
testWritePropertyTag(pTest, &value);
value.tag = BACNET_APPLICATION_TAG_REAL;
value.type.Real = 0.0;
testWritePropertyTag(pTest, &value);
value.type.Real = -1.0;
testWritePropertyTag(pTest, &value);
value.type.Real = 1.0;
testWritePropertyTag(pTest, &value);
value.type.Real = 3.14159;
testWritePropertyTag(pTest, &value);
value.type.Real = -3.14159;
testWritePropertyTag(pTest, &value);
value.tag = BACNET_APPLICATION_TAG_ENUMERATED;
value.type.Enumerated = 0;
testWritePropertyTag(pTest, &value);
value.type.Enumerated = 0xFFFF;
testWritePropertyTag(pTest, &value);
value.type.Enumerated = 0xFFFFFFFF;
testWritePropertyTag(pTest, &value);
value.tag = BACNET_APPLICATION_TAG_DATE;
value.type.Date.year = 2005;
value.type.Date.month = 5;
value.type.Date.day = 22;
value.type.Date.wday = 1;
testWritePropertyTag(pTest, &value);
value.tag = BACNET_APPLICATION_TAG_TIME;
value.type.Time.hour = 23;
value.type.Time.min = 59;
value.type.Time.sec = 59;
value.type.Time.hundredths = 12;
testWritePropertyTag(pTest, &value);
value.tag = BACNET_APPLICATION_TAG_OBJECT_ID;
value.type.Object_Id.type = OBJECT_ANALOG_INPUT;
value.type.Object_Id.instance = 0;
testWritePropertyTag(pTest, &value);
value.type.Object_Id.type = OBJECT_LIFE_SAFETY_ZONE;
value.type.Object_Id.instance = BACNET_MAX_INSTANCE;
testWritePropertyTag(pTest, &value);
return;
}
#ifdef TEST_WRITE_PROPERTY
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet WriteProperty", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testWriteProperty);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_WRITE_PROPERTY */
#endif /* TEST */
+357
View File
@@ -0,0 +1,357 @@
/**************************************************************************
*
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
* Contributions by Nikola Jelic 2011 <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include <stdint.h>
#include "bacapp.h"
#include "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "wp.h"
#include "wpm.h"
#include "string.h"
/** @file wpm.c Encode/Decode BACnet Write Property Multiple APDUs */
/** Decoding for WritePropertyMultiple service, object ID.
* @ingroup DSWPM
* This handler will be invoked by write_property_multiple handler
* if it has been enabled by a call to apdu_set_confirmed_handler().
* This function decodes only the first tagged entity, which is
* an object identifier. This function will return an error if:
* - the tag is not the right value
* - the number of bytes is not enough to decode for this entity
* - the subsequent tag number is incorrect
*
* @param apdu [in] The contents of the APDU buffer.
* @param apdu_len [in] The length of the APDU buffer.
* @param data [out] The BACNET_WRITE_PROPERTY_DATA structure
* which will contain the reponse values or error.
*/
int wpm_decode_object_id(
uint8_t * apdu,
uint16_t apdu_len,
BACNET_WRITE_PROPERTY_DATA * wp_data)
{
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t object_instance = 0;
uint16_t object_type = 0;
uint16_t len = 0;
if (apdu && (apdu_len > 5) && wp_data) {
/* Context tag 0 - Object ID */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if ((tag_number == 0) && (apdu_len > len)) {
apdu_len -= len;
if (apdu_len >= 4) {
len +=
decode_object_id(&apdu[len], &object_type,
&object_instance);
wp_data->object_type = object_type;
wp_data->object_instance = object_instance;
apdu_len -= len;
} else {
wp_data->error_code =
ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
return BACNET_STATUS_REJECT;
}
} else {
wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
/* just test for the next tag - no need to decode it here */
/* Context tag 1: sequence of BACnetPropertyValue */
if (apdu_len && !decode_is_opening_tag_number(&apdu[len], 1)) {
wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
} else {
wp_data->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
return BACNET_STATUS_REJECT;
}
return (int) len;
}
int wpm_decode_object_property(
uint8_t * apdu,
uint16_t apdu_len,
BACNET_WRITE_PROPERTY_DATA * wp_data)
{
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t ulVal = 0;
int len = 0, i = 0;
if ((apdu) && (apdu_len) && (wp_data)) {
wp_data->array_index = BACNET_ARRAY_ALL;
wp_data->priority = BACNET_MAX_PRIORITY;
wp_data->application_data_len = 0;
/* tag 0 - Property Identifier */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number == 0) {
len += decode_enumerated(&apdu[len], len_value, &ulVal);
wp_data->object_property = ulVal;
} else {
wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
/* tag 1 - Property Array Index - optional */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number == 1) {
len += decode_unsigned(&apdu[len], len_value, &ulVal);
wp_data->array_index = ulVal;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
}
/* tag 2 - Property Value */
if ((tag_number == 2) && (decode_is_opening_tag(&apdu[len - 1]))) {
len--;
wp_data->application_data_len =
bacapp_data_len(&apdu[len], (unsigned) (apdu_len - len),
wp_data->object_property);
len++;
/* copy application data */
for (i = 0; i < wp_data->application_data_len; i++) {
wp_data->application_data[i] = apdu[len + i];
}
len += wp_data->application_data_len;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
/* closing tag 2 */
if ((tag_number != 2) && (decode_is_closing_tag(&apdu[len - 1]))) {
wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
} else {
wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
/* tag 3 - Priority - optional */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number == 3) {
len += decode_unsigned(&apdu[len], len_value, &ulVal);
wp_data->priority = ulVal;
} else
len--;
} else {
wp_data->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
return BACNET_STATUS_REJECT;
}
return len;
}
/* encode functions */
int wpm_encode_apdu_init(
uint8_t * apdu,
uint8_t invoke_id)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE; /* service choice */
apdu_len = 4;
}
return apdu_len;
}
int wpm_encode_apdu_object_begin(
uint8_t * apdu,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu_len =
encode_context_object_id(&apdu[0], 0, object_type,
object_instance);
/* Tag 1: sequence of WriteAccessSpecification */
apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
}
return apdu_len;
}
int wpm_encode_apdu_object_end(
uint8_t * apdu)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu_len = encode_closing_tag(&apdu[0], 1);
}
return apdu_len;
}
int wpm_encode_apdu_object_property(
uint8_t * apdu,
BACNET_WRITE_PROPERTY_DATA * wpdata)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0;
if (apdu) {
apdu_len =
encode_context_enumerated(&apdu[0], 0, wpdata->object_property);
/* optional array index */
if (wpdata->array_index != BACNET_ARRAY_ALL) {
apdu_len +=
encode_context_unsigned(&apdu[apdu_len], 1,
wpdata->array_index);
}
apdu_len += encode_opening_tag(&apdu[apdu_len], 2);
for (len = 0; len < wpdata->application_data_len; len++) {
apdu[apdu_len] = wpdata->application_data[len];
apdu_len++;
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 2);
if (wpdata->priority != BACNET_NO_PRIORITY) {
apdu_len +=
encode_context_unsigned(&apdu[apdu_len], 3, wpdata->priority);
}
}
return apdu_len;
}
int wpm_encode_apdu(
uint8_t * apdu,
size_t max_apdu,
uint8_t invoke_id,
BACNET_WRITE_ACCESS_DATA * write_access_data)
{
int apdu_len = 0;
int len = 0;
BACNET_WRITE_ACCESS_DATA *wpm_object; /* current object */
uint8_t apdu_temp[MAX_APDU]; /* temp for data before copy */
BACNET_PROPERTY_VALUE *wpm_property; /* current property */
BACNET_WRITE_PROPERTY_DATA wpdata; /* for compatibility with wpm_encode_apdu_object_property function */
if (apdu) {
len = wpm_encode_apdu_init(&apdu[0], invoke_id);
apdu_len += len;
wpm_object = write_access_data;
while (wpm_object) {
len =
wpm_encode_apdu_object_begin(&apdu[apdu_len],
wpm_object->object_type, wpm_object->object_instance);
apdu_len += len;
wpm_property = wpm_object->listOfProperties;
while (wpm_property) {
wpdata.object_property = wpm_property->propertyIdentifier;
wpdata.array_index = wpm_property->propertyArrayIndex;
wpdata.priority = wpm_property->priority;
wpdata.application_data_len = bacapp_encode_data(&apdu_temp[0],
&wpm_property->value);
memcpy(&wpdata.application_data[0], &apdu_temp[0],
(size_t)wpdata.application_data_len);
len =
wpm_encode_apdu_object_property(&apdu[apdu_len], &wpdata);
apdu_len += len;
wpm_property = wpm_property->next;
}
len = wpm_encode_apdu_object_end(&apdu[apdu_len]);
apdu_len += len;
wpm_object = wpm_object->next;
}
}
return apdu_len;
}
int wpm_ack_encode_apdu_init(
uint8_t * apdu,
uint8_t invoke_id)
{
int len = 0;
if (apdu) {
apdu[len++] = PDU_TYPE_SIMPLE_ACK;
apdu[len++] = invoke_id;
apdu[len++] = SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE;
}
return len;
}
int wpm_error_ack_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
BACNET_WRITE_PROPERTY_DATA * wp_data)
{
int len = 0;
if (apdu) {
apdu[len++] = PDU_TYPE_ERROR;
apdu[len++] = invoke_id;
apdu[len++] = SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE;
len += encode_opening_tag(&apdu[len], 0);
len += encode_application_enumerated(&apdu[len], wp_data->error_class);
len += encode_application_enumerated(&apdu[len], wp_data->error_code);
len += encode_closing_tag(&apdu[len], 0);
len += encode_opening_tag(&apdu[len], 1);
len +=
encode_context_object_id(&apdu[len], 0, wp_data->object_type,
wp_data->object_instance);
len +=
encode_context_enumerated(&apdu[len], 1, wp_data->object_property);
if (wp_data->array_index != BACNET_ARRAY_ALL)
len +=
encode_context_unsigned(&apdu[len], 2, wp_data->array_index);
len += encode_closing_tag(&apdu[len], 1);
}
return len;
}