Moved header files into the include directory. Moved source files into the src directory. Still need to updated the makefiles and projects.

This commit is contained in:
skarg
2007-10-10 03:34:37 +00:00
parent c25265201d
commit a2506536ed
92 changed files with 34 additions and 87 deletions
+348
View File
@@ -0,0 +1,348 @@
/*####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 <stdio.h>
#include "config.h"
#include "bacaddr.h"
#include "address.h"
#include "bacdef.h"
#include "bacdcode.h"
/* This module is used to handle the address binding that */
/* occurs in BACnet. A device id is bound to a MAC address. */
/* The normal method is using Who-Is, and using the data from I-Am */
static struct Address_Cache_Entry {
bool valid;
bool bind_request;
uint32_t device_id;
unsigned max_apdu;
BACNET_ADDRESS address;
} Address_Cache[MAX_ADDRESS_CACHE];
void address_remove_device(uint32_t device_id)
{
unsigned i;
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if ((Address_Cache[i].valid ||
Address_Cache[i].bind_request) &&
(Address_Cache[i].device_id == device_id)) {
Address_Cache[i].valid = false;
break;
}
}
return;
}
void address_init(void)
{
unsigned i;
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
Address_Cache[i].valid = false;
Address_Cache[i].bind_request = false;
}
return;
}
bool address_get_by_device(uint32_t device_id,
unsigned *max_apdu, BACNET_ADDRESS * src)
{
unsigned i;
bool found = false; /* return value */
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (Address_Cache[i].valid &&
(Address_Cache[i].device_id == device_id)) {
bacnet_address_copy(src, &Address_Cache[i].address);
*max_apdu = Address_Cache[i].max_apdu;
found = true;
break;
}
}
return found;
}
void address_add(uint32_t device_id,
unsigned max_apdu, BACNET_ADDRESS * src)
{
unsigned i;
bool found = false; /* return value */
/* existing device - update address */
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (Address_Cache[i].valid &&
(Address_Cache[i].device_id == device_id)) {
bacnet_address_copy(&Address_Cache[i].address, src);
Address_Cache[i].max_apdu = max_apdu;
found = true;
break;
}
}
/* new device */
if (!found) {
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (!Address_Cache[i].valid) {
Address_Cache[i].valid = true;
Address_Cache[i].device_id = device_id;
Address_Cache[i].max_apdu = max_apdu;
bacnet_address_copy(&Address_Cache[i].address, src);
break;
}
}
}
return;
}
/* returns true if device is already bound */
/* also returns the address and max apdu if already bound */
bool address_bind_request(uint32_t device_id,
unsigned *max_apdu, BACNET_ADDRESS * src)
{
unsigned i;
bool found = false; /* return value */
/* existing device - update address */
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (Address_Cache[i].valid &&
(Address_Cache[i].device_id == device_id)) {
found = true;
bacnet_address_copy(src, &Address_Cache[i].address);
*max_apdu = Address_Cache[i].max_apdu;
break;
}
/* already have a bind request active for this puppy */
else if (Address_Cache[i].bind_request &&
(Address_Cache[i].device_id == device_id)) {
return found;
}
}
if (!found) {
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (!(Address_Cache[i].bind_request || Address_Cache[i].valid)) {
Address_Cache[i].bind_request = true;
Address_Cache[i].device_id = device_id;
/* now would be a good time to do a Who-Is request */
break;
}
}
}
return found;
}
void address_add_binding(uint32_t device_id,
unsigned max_apdu, BACNET_ADDRESS * src)
{
unsigned i;
bool found = false; /* return value */
/* existing device - update address */
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (Address_Cache[i].valid &&
(Address_Cache[i].device_id == device_id)) {
bacnet_address_copy(&Address_Cache[i].address, src);
Address_Cache[i].max_apdu = max_apdu;
found = true;
break;
}
}
/* add new device - but only if bind requested */
if (!found) {
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (!Address_Cache[i].valid && Address_Cache[i].bind_request) {
Address_Cache[i].valid = true;
Address_Cache[i].bind_request = false;
Address_Cache[i].device_id = device_id;
Address_Cache[i].max_apdu = max_apdu;
bacnet_address_copy(&Address_Cache[i].address, src);
break;
}
}
}
return;
}
bool address_get_by_index(unsigned index,
uint32_t * device_id, unsigned *max_apdu, BACNET_ADDRESS * src)
{
bool found = false; /* return value */
if (index < MAX_ADDRESS_CACHE) {
if (Address_Cache[index].valid) {
bacnet_address_copy(src, &Address_Cache[index].address);
*device_id = Address_Cache[index].device_id;
*max_apdu = Address_Cache[index].max_apdu;
found = true;
}
}
return found;
}
unsigned address_count(void)
{
unsigned i;
unsigned count = 0; /* return value */
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (Address_Cache[i].valid)
count++;
}
return count;
}
bool address_match(BACNET_ADDRESS * dest, BACNET_ADDRESS * src)
{
unsigned i;
unsigned max_len;
bool match = true; /* return value */
if (dest->mac_len != src->mac_len)
match = 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])
match = false;
}
if (dest->net != src->net)
match = false;
if (dest->len != src->len)
match = 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])
match = false;
}
return match;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
static void set_address(unsigned index, BACNET_ADDRESS * dest)
{
unsigned i;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->mac[i] = index;
}
dest->mac_len = MAX_MAC_LEN;
dest->net = 7;
dest->len = MAX_MAC_LEN;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = index;
}
}
void testAddress(Test * pTest)
{
unsigned i, count;
BACNET_ADDRESS src;
uint32_t device_id = 0;
unsigned max_apdu = 480;
BACNET_ADDRESS test_address;
uint32_t test_device_id = 0;
unsigned test_max_apdu = 0;
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
set_address(i, &src);
device_id = i * 255;
address_add(device_id, max_apdu, &src);
count = address_count();
ct_test(pTest, count == (i + 1));
}
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
device_id = i * 255;
ct_test(pTest, address_get_by_device(device_id, &test_max_apdu,
&test_address));
set_address(i, &src);
ct_test(pTest, test_max_apdu == max_apdu);
ct_test(pTest, address_match(&test_address, &src));
ct_test(pTest, address_get_by_index(i, &test_device_id,
&test_max_apdu, &test_address));
ct_test(pTest, test_device_id == device_id);
ct_test(pTest, test_max_apdu == max_apdu);
ct_test(pTest, address_match(&test_address, &src));
ct_test(pTest, address_count() == MAX_ADDRESS_CACHE);
}
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
device_id = i * 255;
address_remove_device(device_id);
ct_test(pTest, !address_get_by_device(device_id, &test_max_apdu,
&test_address));
count = address_count();
ct_test(pTest, count == (MAX_ADDRESS_CACHE - i - 1));
}
}
#ifdef TEST_ADDRESS
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Address", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAddress);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ADDRESS */
#endif /* TEST */
+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 <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"
/* 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_PROPERTY_CONDITIONAL,
SERVICE_SUPPORTED_READ_PROPERTY_MULTIPLE,
SERVICE_SUPPORTED_WRITE_PROPERTY,
SERVICE_SUPPORTED_WRITE_PROPERTY_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] = {
NULL
};
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) {
if (Confirmed_Function[i] != NULL)
status = true;
found = 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;
}
/* Confirmed ACK Function Handlers */
static void *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_PROPERTY_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] = (void *) 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_PROPERTY_CONDITIONAL:
case SERVICE_CONFIRMED_READ_PROPERTY_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] = (void *) 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] & BIT3) ? true : false;
service_data->more_follows = (apdu[0] & BIT2) ? true : false;
service_data->segmented_response_accepted =
(apdu[0] & BIT1) ? 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;
}
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;
uint16_t len = 0; /* counts where we are in PDU */
uint8_t tag_number = 0;
uint32_t len_value = 0;
int error_code = 0;
int error_class = 0;
uint8_t reason = 0;
bool server = false;
if (apdu) {
/* PDU Type */
switch (apdu[0] & 0xF0) {
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
len = apdu_decode_confirmed_service_request(&apdu[0], /* APDU data */
apdu_len,
&service_data,
&service_choice, &service_request, &service_request_len);
/* When network communications are completely disabled,
only DeviceCommunicationControl and ReinitializeDevice APDUs
shall be processed and no messages shall be initiated. */
if (dcc_communication_disabled() &&
((service_choice !=
SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL)
&& (service_choice !=
SERVICE_CONFIRMED_REINITIALIZE_DEVICE)))
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:
if (dcc_communication_disabled())
break;
service_choice = apdu[1];
service_request = &apdu[2];
service_request_len = apdu_len - 2;
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_PROPERTY_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]) {
((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] & BIT3) ? true : false;
service_ack_data.more_follows =
(apdu[0] & BIT2) ? 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 - 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_PROPERTY_CONDITIONAL:
case SERVICE_CONFIRMED_READ_PROPERTY_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]) {
((confirmed_ack_function)
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;
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 < 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;
}
+473
View File
@@ -0,0 +1,473 @@
/*####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 "device.h"
#include "arf.h"
/* 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, Device_Max_APDU_Length_Accepted());
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_ATOMIC_READ_FILE; /* service choice */
apdu_len = 4;
apdu_len += encode_tagged_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_tagged_signed(&apdu[apdu_len],
data->type.stream.fileStartPosition);
apdu_len += encode_tagged_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_tagged_signed(&apdu[apdu_len],
data->type.record.fileStartRecord);
apdu_len += encode_tagged_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;
int 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 = 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, Device_Max_APDU_Length_Accepted()); */
*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 */
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_tagged_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_tagged_signed(&apdu[apdu_len],
data->type.stream.fileStartPosition);
apdu_len += encode_tagged_octet_string(&apdu[apdu_len],
&data->fileData);
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_tagged_signed(&apdu[apdu_len],
data->type.record.fileStartRecord);
apdu_len += encode_tagged_unsigned(&apdu[apdu_len],
data->type.record.RecordCount);
apdu_len += encode_tagged_octet_string(&apdu[apdu_len],
&data->fileData);
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;
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 != 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;
len += decode_octet_string(&apdu[len],
len_value_type, &data->fileData);
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);
/* 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;
len += decode_octet_string(&apdu[len],
len_value_type, &data->fileData);
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;
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);
} 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);
}
ct_test(pTest, octetstring_length(&test_data.fileData) ==
octetstring_length(&data->fileData));
ct_test(pTest, memcmp(octetstring_value(&test_data.fileData),
octetstring_value(&data->fileData),
octetstring_length(&test_data.fileData)) == 0);
}
void testAtomicReadFileAck(Test * pTest)
{
BACNET_ATOMIC_READ_FILE_DATA data = { 0 };
uint8_t test_octet_string[32] = "Joshua-Mary-Anna-Christopher";
data.endOfFile = true;
data.access = FILE_STREAM_ACCESS;
data.type.stream.fileStartPosition = 0;
octetstring_init(&data.fileData,
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 = 2;
octetstring_init(&data.fileData,
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
uint16_t Device_Max_APDU_Length_Accepted(void)
{
return MAX_APDU;
}
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 */
+408
View File
@@ -0,0 +1,408 @@
/*####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 "device.h"
#include "awf.h"
/* 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 */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] =
encode_max_segs_max_apdu(0, Device_Max_APDU_Length_Accepted());
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_ATOMIC_WRITE_FILE; /* service choice */
apdu_len = 4;
apdu_len += encode_tagged_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_tagged_signed(&apdu[apdu_len],
data->type.stream.fileStartPosition);
apdu_len += encode_tagged_octet_string(&apdu[apdu_len],
&data->fileData);
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_tagged_signed(&apdu[apdu_len],
data->type.record.fileStartRecord);
apdu_len += encode_tagged_unsigned(&apdu[apdu_len],
data->type.record.returnedRecordCount);
apdu_len += encode_tagged_octet_string(&apdu[apdu_len],
&data->fileData);
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;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
int32_t signed_value = 0;
uint32_t unsigned_value = 0;
int 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 = 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;
len += decode_octet_string(&apdu[len],
len_value_type, &data->fileData);
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 */
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;
len += decode_octet_string(&apdu[len],
len_value_type, &data->fileData);
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, Device_Max_APDU_Length_Accepted()); */
*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_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) ==
octetstring_length(&data->fileData));
ct_test(pTest, memcmp(octetstring_value(&test_data.fileData),
octetstring_value(&data->fileData),
octetstring_length(&test_data.fileData)) == 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,
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 = 2;
octetstring_init(&data.fileData,
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
uint16_t Device_Max_APDU_Length_Accepted(void)
{
return MAX_APDU;
}
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 */
+79
View File
@@ -0,0 +1,79 @@
/*####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"
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)
{
int i = 0;
if (!dest || !src)
return false;
if (dest->mac_len != src->mac_len)
return false;
for (i = 0; i < dest->mac_len; i++) {
if (dest->mac[i] != src->mac[i])
return false;
}
if (dest->net != src->net)
return false;
if (dest->len != src->len)
return false;
for (i = 0; i < dest->len; i++) {
if (dest->adr[i] != src->adr[i])
return false;
}
return true;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+259
View File
@@ -0,0 +1,259 @@
/*####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"
/* 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_tagged_enumerated(&apdu[apdu_len], error_class);
apdu_len += encode_tagged_enumerated(&apdu[apdu_len], error_code);
}
return apdu_len;
}
/* 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;
int 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;
}
#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 < MAX_BACNET_ERROR_CLASS; error_class++) {
for (error_code = 0;
error_code < MAX_BACNET_ERROR_CODE; 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 = LAST_PROPRIETARY_ERROR_CLASS;
error_code = LAST_PROPRIETARY_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);
}
#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 */
+434
View File
@@ -0,0 +1,434 @@
/*####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>
int encode_unsigned16(uint8_t * apdu, uint16_t value)
{
#if BIG_ENDIAN
apdu[0] = (uint8_t)(value & 0x00ff);
apdu[1] = (uint8_t)((value & 0xff00) >> 8);
#else
apdu[0] = (uint8_t)((value & 0xff00) >> 8);
apdu[1] = (uint8_t)(value & 0x00ff);
#endif
return 2;
}
int decode_unsigned16(uint8_t * apdu, uint16_t * value)
{
if (value) {
#if BIG_ENDIAN
*value = (uint16_t)(((uint16_t)apdu[0]) & 0x00ff);
*value |= ((uint16_t)((((uint16_t)apdu[1]) << 8) & 0xff00));
#else
*value = (uint16_t)((((uint16_t)apdu[0]) << 8) & 0xff00);
*value |= ((uint16_t)(((uint16_t)apdu[1]) & 0x00ff));
#endif
}
return 2;
}
int encode_unsigned24(uint8_t * apdu, uint32_t value)
{
#if BIG_ENDIAN
apdu[0] = (uint8_t)(value & 0x0000ff);
apdu[1] = (uint8_t)((value & 0x00ff00) >> 8);
apdu[2] = (uint8_t)((value & 0xff0000) >> 16);
#else
apdu[0] = (uint8_t)((value & 0xff0000) >> 16);
apdu[1] = (uint8_t)((value & 0x00ff00) >> 8);
apdu[2] = (uint8_t)(value & 0x0000ff);
#endif
return 3;
}
int decode_unsigned24(uint8_t * apdu, uint32_t * value)
{
if (value) {
#if BIG_ENDIAN
*value = (uint32_t)(apdu[0] & 0x000000ff);
*value |= ((uint32_t)((((uint32_t)apdu[1]) << 8) & 0x0000ff00));
*value |= ((uint32_t)((((uint32_t)apdu[2]) << 16) & 0x00ff0000));
#else
*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));
#endif
}
return 3;
}
int encode_unsigned32(uint8_t * apdu, uint32_t value)
{
#if BIG_ENDIAN
apdu[0] = (uint8_t)(value & 0x000000ff);
apdu[1] = (uint8_t)((value & 0x0000ff00) >> 8);
apdu[2] = (uint8_t)((value & 0x00ff0000) >> 16);
apdu[3] = (uint8_t)((value & 0xff000000) >> 24);
#else
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);
#endif
return 4;
}
int decode_unsigned32(uint8_t * apdu, uint32_t * value)
{
if (value) {
#if BIG_ENDIAN
*value = (uint32_t)(((uint32_t)apdu[0]) & 0x000000ff);
*value |= ((uint32_t)((((uint32_t)apdu[1]) << 8) & 0x0000ff00));
*value |= ((uint32_t)((((uint32_t)apdu[2]) << 16) & 0x00ff0000));
*value |= ((uint32_t)((((uint32_t)apdu[3]) << 24) & 0xff000000));
#else
*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));
#endif
}
return 4;
}
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) {
#if BIG_ENDIAN
/* negative - bit 7 is set */
if (apdu[0] & 0x80)
*value = 0x00FFFFFF;
else
*value = 0;
*value |= ((int32_t)((((int32_t)apdu[0]) << 24) & 0xff000000));
#else
/* negative - bit 7 is set */
if (apdu[0] & 0x80)
*value = 0xFFFFFF00;
else
*value = 0;
*value |= ((int32_t)(((int32_t)apdu[0]) & 0x000000ff));
#endif
}
return 1;
}
int encode_signed16(uint8_t * apdu, int16_t value)
{
#if BIG_ENDIAN
apdu[0] = (uint8_t)(value & 0x00ff);
apdu[1] = (uint8_t)((value & 0xff00) >> 8);
#else
apdu[0] = (uint8_t)((value & 0xff00) >> 8);
apdu[1] = (uint8_t)(value & 0x00ff);
#endif
return 2;
}
int decode_signed16(uint8_t * apdu, int32_t * value)
{
if (value) {
#if BIG_ENDIAN
/* negative - bit 7 is set */
if (apdu[0] & 0x80)
*value = 0x0000FFFF;
else
*value = 0;
*value |= ((int32_t)((((int32_t)apdu[0]) << 24) & 0xff000000));
*value |= ((int32_t)((((int32_t)apdu[1]) << 16) & 0x00ff0000));
#else
/* 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));
#endif
}
return 2;
}
int encode_signed24(uint8_t * apdu, int32_t value)
{
#if BIG_ENDIAN
apdu[0] = (uint8_t)(value & 0x0000ff);
apdu[1] = (uint8_t)((value & 0x00ff00) >> 8);
apdu[2] = (uint8_t)((value & 0xff0000) >> 16);
#else
apdu[0] = (uint8_t)((value & 0xff0000) >> 16);
apdu[1] = (uint8_t)((value & 0x00ff00) >> 8);
apdu[2] = (uint8_t)(value & 0x0000ff);
#endif
return 3;
}
int decode_signed24(uint8_t * apdu, int32_t * value)
{
if (value) {
#if BIG_ENDIAN
/* negative - bit 7 is set */
if (apdu[0] & 0x80)
*value = 0x000000FF;
else
*value = 0;
*value |= ((int32_t)((((int32_t)apdu[0]) << 8) & 0x0000ff00));
*value |= ((int32_t)((((int32_t)apdu[1]) << 16) & 0x00ff0000));
*value |= ((int32_t)((((int32_t)apdu[2]) << 24) & 0xff000000));
#else
/* 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));
#endif
}
return 3;
}
int encode_signed32(uint8_t * apdu, int32_t value)
{
#if BIG_ENDIAN
apdu[0] = (uint8_t)(value & 0x000000ff);
apdu[1] = (uint8_t)((value & 0x0000ff00) >> 8);
apdu[2] = (uint8_t)((value & 0x00ff0000) >> 16);
apdu[3] = (uint8_t)((value & 0xff000000) >> 24);
#else
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);
#endif
return 4;
}
int decode_signed32(uint8_t * apdu, int32_t * value)
{
if (value) {
#if BIG_ENDIAN
*value = (int32_t)(((int32_t)apdu[0]) & 0x000000ff);
*value |= ((int32_t)((((int32_t)apdu[1]) << 8) & 0x0000ff00));
*value |= ((int32_t)((((int32_t)apdu[2]) << 16) & 0x00ff0000));
*value |= ((int32_t)((((int32_t)apdu[3]) << 24) & 0xff000000));
#else
*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));
#endif
}
return 4;
}
/* end of decoding_encoding.c */
#ifdef TEST
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include "ctest.h"
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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);
}
}
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);
}
}
#ifdef TEST_BACINT
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACint", NULL);
/* 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);
/* configure output */
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_BACINT */
#endif /* TEST */
+111
View File
@@ -0,0 +1,111 @@
/*####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>
#include <stdio.h>
#include "bacprop.h"
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:
fprintf(stderr, "Unsupported object type");
break;
}
return -1;
}
+637
View File
@@ -0,0 +1,637 @@
/*####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 <stdbool.h>
#include <stdint.h>
#include <string.h> /* for strlen */
#include "bacstr.h"
#include "bits.h"
void bitstring_init(BACNET_BIT_STRING * bit_string)
{
int i;
bit_string->bits_used = 0;
for (i = 0; i < MAX_BITSTRING_BYTES; i++) {
bit_string->value[i] = 0;
}
}
void bitstring_set_bit(BACNET_BIT_STRING * bit_string, uint8_t bit,
bool value)
{
uint8_t byte_number = bit / 8;
uint8_t bit_mask = 1;
if (byte_number < MAX_BITSTRING_BYTES) {
/* set max bits used */
if (bit_string->bits_used < (bit + 1))
bit_string->bits_used = bit + 1;
bit_mask = bit_mask << (bit - (byte_number * 8));
if (value)
bit_string->value[byte_number] |= bit_mask;
else
bit_string->value[byte_number] &= (~(bit_mask));
}
}
bool bitstring_bit(BACNET_BIT_STRING * bit_string, uint8_t bit)
{
bool value = false;
uint8_t byte_number = bit / 8;
uint8_t bit_mask = 1;
if (bit < (MAX_BITSTRING_BYTES * 8)) {
bit_mask = bit_mask << (bit - (byte_number * 8));
if (bit_string->value[byte_number] & bit_mask)
value = true;
}
return value;
}
uint8_t bitstring_bits_used(BACNET_BIT_STRING * bit_string)
{
return bit_string->bits_used;
}
/* returns the number of bytes that a bit string is using */
int bitstring_bytes_used(BACNET_BIT_STRING * bit_string)
{
int len = 0; /* return value */
uint8_t used_bytes = 0;
uint8_t last_bit = 0;
if (bit_string->bits_used) {
last_bit = bit_string->bits_used - 1;
used_bytes = last_bit / 8;
/* add one for the first byte */
used_bytes++;
len = used_bytes;
}
return len;
}
uint8_t bitstring_octet(BACNET_BIT_STRING * bit_string, uint8_t index)
{
uint8_t octet = 0;
if (bit_string) {
if (index < MAX_BITSTRING_BYTES) {
octet = bit_string->value[index];
}
}
return octet;
}
bool bitstring_set_octet(BACNET_BIT_STRING * bit_string,
uint8_t index, uint8_t octet)
{
bool status = false;
if (bit_string) {
if (index < MAX_BITSTRING_BYTES) {
bit_string->value[index] = octet;
status = true;
}
}
return status;
}
bool bitstring_set_bits_used(BACNET_BIT_STRING * bit_string,
uint8_t bytes_used, uint8_t unused_bits)
{
bool status = false;
if (bit_string) {
/* FIXME: check that bytes_used is at least one? */
bit_string->bits_used = bytes_used * 8;
bit_string->bits_used -= unused_bits;
status = true;
}
return status;
}
uint8_t bitstring_bits_capacity(BACNET_BIT_STRING * bit_string)
{
if (bit_string)
return (sizeof(bit_string->value) * 8);
else
return 0;
}
#define CHARACTER_STRING_CAPACITY (MAX_CHARACTER_STRING_BYTES - 1)
/* returns false if the string exceeds capacity
initialize by using length=0 */
bool characterstring_init(BACNET_CHARACTER_STRING * char_string,
uint8_t encoding, char *value, size_t length)
{
bool status = false; /* return value */
size_t i; /* counter */
if (char_string) {
char_string->length = 0;
char_string->encoding = encoding;
/* save a byte at the end for NULL -
note: assumes printable characters */
if (length <= CHARACTER_STRING_CAPACITY) {
if (value) {
for (i = 0; i < MAX_CHARACTER_STRING_BYTES; i++) {
if (i < length) {
char_string->value[char_string->length] = value[i];
char_string->length++;
} else
char_string->value[i] = 0;
}
} else {
for (i = 0; i < MAX_CHARACTER_STRING_BYTES; i++) {
char_string->value[i] = 0;
}
}
status = true;
}
}
return status;
}
bool characterstring_init_ansi(BACNET_CHARACTER_STRING * char_string,
char *value)
{
return characterstring_init(char_string,
CHARACTER_ANSI_X34, value, value ? strlen(value) : 0);
}
bool characterstring_copy(BACNET_CHARACTER_STRING * dest,
BACNET_CHARACTER_STRING * src)
{
return characterstring_init(dest,
characterstring_encoding(src),
characterstring_value(src), characterstring_length(src));
}
bool characterstring_same(BACNET_CHARACTER_STRING * dest,
BACNET_CHARACTER_STRING * src)
{
size_t i; /* counter */
bool same_status = false;
if (src && dest) {
if ((src->length == dest->length) &&
(src->encoding == dest->encoding)) {
same_status = true;
for (i = 0; i < src->length; i++) {
if (src->value[i] != dest->value[i]) {
same_status = false;
break;
}
}
}
} else if (src) {
if (src->length == 0)
same_status = true;
} else if (dest) {
if (dest->length == 0)
same_status = true;
}
return same_status;
}
bool characterstring_ansi_same(BACNET_CHARACTER_STRING * dest,
const char *src)
{
size_t i; /* counter */
bool same_status = false;
if (src && dest) {
if ((dest->length == strlen(src)) &&
(dest->encoding == CHARACTER_ANSI_X34)) {
same_status = true;
for (i = 0; i < dest->length; i++) {
if (src[i] != dest->value[i]) {
same_status = false;
break;
}
}
}
}
/* NULL matches an empty string in our world */
else if (src) {
if (strlen(src) == 0)
same_status = true;
} else if (dest) {
if (dest->length == 0)
same_status = true;
}
return same_status;
}
/* returns false if the string exceeds capacity */
bool characterstring_append(BACNET_CHARACTER_STRING * char_string,
char *value, size_t length)
{
size_t i; /* counter */
bool status = false; /* return value */
if (char_string) {
if ((length + char_string->length) <= CHARACTER_STRING_CAPACITY) {
for (i = 0; i < length; i++) {
char_string->value[char_string->length] = value[i];
char_string->length++;
}
status = true;
}
}
return status;
}
/* This function sets a new length without changing the value.
If length exceeds capacity, no modification happens and
function returns false. */
bool characterstring_truncate(BACNET_CHARACTER_STRING * char_string,
size_t length)
{
bool status = false; /* return value */
if (char_string) {
if (length <= CHARACTER_STRING_CAPACITY) {
char_string->length = length;
status = true;
}
}
return status;
}
/* Returns the value. */
char *characterstring_value(BACNET_CHARACTER_STRING * char_string)
{
char *value = NULL;
if (char_string) {
value = char_string->value;
}
return value;
}
/* returns the length. */
size_t characterstring_length(BACNET_CHARACTER_STRING * char_string)
{
size_t length = 0;
if (char_string) {
/* FIXME: validate length is within bounds? */
length = char_string->length;
}
return length;
}
size_t characterstring_capacity(BACNET_CHARACTER_STRING * char_string)
{
size_t length = 0;
if (char_string) {
length = CHARACTER_STRING_CAPACITY;
}
return length;
}
/* returns the encoding. */
uint8_t characterstring_encoding(BACNET_CHARACTER_STRING * char_string)
{
uint8_t encoding = 0;
if (char_string) {
encoding = char_string->encoding;
}
return encoding;
}
/* returns false if the string exceeds capacity
initialize by using length=0 */
bool octetstring_init(BACNET_OCTET_STRING * octet_string,
uint8_t * value, size_t length)
{
bool status = false; /* return value */
size_t i; /* counter */
if (octet_string) {
octet_string->length = 0;
if (length <= sizeof(octet_string->value)) {
if (value) {
for (i = 0; i < length; i++) {
octet_string->value[octet_string->length] = value[i];
octet_string->length++;
}
} else {
for (i = 0; i < sizeof(octet_string->value); i++) {
octet_string->value[i] = 0;
}
}
status = true;
}
}
return status;
}
bool octetstring_copy(BACNET_OCTET_STRING * dest,
BACNET_OCTET_STRING * src)
{
return octetstring_init(dest,
octetstring_value(src), octetstring_length(src));
}
/* returns false if the string exceeds capacity */
bool octetstring_append(BACNET_OCTET_STRING * octet_string,
uint8_t * value, size_t length)
{
size_t i; /* counter */
bool status = false; /* return value */
if (octet_string) {
if ((length + octet_string->length) <= sizeof(octet_string->value)) {
for (i = 0; i < length; i++) {
octet_string->value[octet_string->length] = value[i];
octet_string->length++;
}
status = true;
}
}
return status;
}
/* This function sets a new length without changing the value.
If length exceeds capacity, no modification happens and
function returns false. */
bool octetstring_truncate(BACNET_OCTET_STRING * octet_string,
size_t length)
{
bool status = false; /* return value */
if (octet_string) {
if (length <= sizeof(octet_string->value)) {
octet_string->length = length;
status = true;
}
}
return status;
}
/* returns the length. Returns the value in parameter. */
uint8_t *octetstring_value(BACNET_OCTET_STRING * octet_string)
{
uint8_t *value = NULL;
if (octet_string) {
value = octet_string->value;
}
return value;
}
/* returns the length. */
size_t octetstring_length(BACNET_OCTET_STRING * octet_string)
{
size_t length = 0;
if (octet_string) {
/* FIXME: validate length is within bounds? */
length = octet_string->length;
}
return length;
}
/* returns the length. */
size_t octetstring_capacity(BACNET_OCTET_STRING * octet_string)
{
size_t length = 0;
if (octet_string) {
/* FIXME: validate length is within bounds? */
length = sizeof(octet_string->value);
}
return length;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testBitString(Test * pTest)
{
uint8_t bit = 0;
BACNET_BIT_STRING bit_string;
bitstring_init(&bit_string);
/* verify initialization */
ct_test(pTest, bitstring_bits_used(&bit_string) == 0);
for (bit = 0; bit < (MAX_BITSTRING_BYTES * 8); bit++) {
ct_test(pTest, bitstring_bit(&bit_string, bit) == false);
}
/* test for true */
for (bit = 0; bit < (MAX_BITSTRING_BYTES * 8); bit++) {
bitstring_set_bit(&bit_string, bit, true);
ct_test(pTest, bitstring_bits_used(&bit_string) == (bit + 1));
ct_test(pTest, bitstring_bit(&bit_string, bit) == true);
}
/* test for false */
bitstring_init(&bit_string);
for (bit = 0; bit < (MAX_BITSTRING_BYTES * 8); bit++) {
bitstring_set_bit(&bit_string, bit, false);
ct_test(pTest, bitstring_bits_used(&bit_string) == (bit + 1));
ct_test(pTest, bitstring_bit(&bit_string, bit) == false);
}
}
void testCharacterString(Test * pTest)
{
BACNET_CHARACTER_STRING bacnet_string;
char *value = "Joshua,Mary,Anna,Christopher";
char test_value[MAX_APDU] = "Patricia";
char test_append_value[MAX_APDU] = " and the Kids";
char test_append_string[MAX_APDU] = "";
bool status = false;
size_t length = 0;
size_t test_length = 0;
size_t i = 0;
/* verify initialization */
status =
characterstring_init(&bacnet_string, CHARACTER_ANSI_X34, NULL, 0);
ct_test(pTest, status == true);
ct_test(pTest, characterstring_length(&bacnet_string) == 0);
ct_test(pTest,
characterstring_encoding(&bacnet_string) == CHARACTER_ANSI_X34);
/* bounds check */
status = characterstring_init(&bacnet_string,
CHARACTER_ANSI_X34,
NULL, characterstring_capacity(&bacnet_string) + 1);
ct_test(pTest, status == false);
status =
characterstring_truncate(&bacnet_string,
characterstring_capacity(&bacnet_string) + 1);
ct_test(pTest, status == false);
status =
characterstring_truncate(&bacnet_string,
characterstring_capacity(&bacnet_string));
ct_test(pTest, status == true);
test_length = strlen(test_value);
status = characterstring_init(&bacnet_string,
CHARACTER_ANSI_X34, &test_value[0], test_length);
ct_test(pTest, status == true);
value = characterstring_value(&bacnet_string);
length = characterstring_length(&bacnet_string);
ct_test(pTest, length == test_length);
for (i = 0; i < test_length; i++) {
ct_test(pTest, value[i] == test_value[i]);
}
test_length = strlen(test_append_value);
status = characterstring_append(&bacnet_string,
&test_append_value[0], test_length);
strcat(test_append_string, test_value);
strcat(test_append_string, test_append_value);
test_length = strlen(test_append_string);
ct_test(pTest, status == true);
length = characterstring_length(&bacnet_string);
value = characterstring_value(&bacnet_string);
ct_test(pTest, length == test_length);
for (i = 0; i < test_length; i++) {
ct_test(pTest, value[i] == test_append_string[i]);
}
}
void testOctetString(Test * pTest)
{
BACNET_OCTET_STRING bacnet_string;
uint8_t *value = NULL;
uint8_t test_value[MAX_APDU] = "Patricia";
uint8_t test_append_value[MAX_APDU] = " and the Kids";
uint8_t test_append_string[MAX_APDU] = "";
bool status = false;
size_t length = 0;
size_t test_length = 0;
size_t i = 0;
/* verify initialization */
status = octetstring_init(&bacnet_string, NULL, 0);
ct_test(pTest, status == true);
ct_test(pTest, octetstring_length(&bacnet_string) == 0);
value = octetstring_value(&bacnet_string);
for (i = 0; i < octetstring_capacity(&bacnet_string); i++) {
ct_test(pTest, value[i] == 0);
}
/* bounds check */
status = octetstring_init(&bacnet_string, NULL,
octetstring_capacity(&bacnet_string) + 1);
ct_test(pTest, status == false);
status = octetstring_init(&bacnet_string, NULL,
octetstring_capacity(&bacnet_string));
ct_test(pTest, status == true);
status =
octetstring_truncate(&bacnet_string,
octetstring_capacity(&bacnet_string) + 1);
ct_test(pTest, status == false);
status =
octetstring_truncate(&bacnet_string,
octetstring_capacity(&bacnet_string));
ct_test(pTest, status == true);
test_length = strlen((char *) test_value);
status = octetstring_init(&bacnet_string, &test_value[0], test_length);
ct_test(pTest, status == true);
length = octetstring_length(&bacnet_string);
value = octetstring_value(&bacnet_string);
ct_test(pTest, length == test_length);
for (i = 0; i < test_length; i++) {
ct_test(pTest, value[i] == test_value[i]);
}
test_length = strlen((char *) test_append_value);
status = octetstring_append(&bacnet_string,
&test_append_value[0], test_length);
strcat((char *) test_append_string, (char *) test_value);
strcat((char *) test_append_string, (char *) test_append_value);
test_length = strlen((char *) test_append_string);
ct_test(pTest, status == true);
length = octetstring_length(&bacnet_string);
value = octetstring_value(&bacnet_string);
ct_test(pTest, length == test_length);
for (i = 0; i < test_length; i++) {
ct_test(pTest, value[i] == test_append_string[i]);
}
}
#ifdef TEST_BACSTR
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Strings", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBitString);
assert(rc);
rc = ct_addTestFunction(pTest, testCharacterString);
assert(rc);
rc = ct_addTestFunction(pTest, testOctetString);
assert(rc);
/* configure output */
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_BACSTR */
#endif /* TEST */
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
/* 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 */
int big_endian(void)
{
union {
long l;
char c[sizeof(long)];
} u;
u.l = 1;
return (u.c[sizeof(long) - 1] == 1);
}
+284
View File
@@ -0,0 +1,284 @@
/*####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 "net.h" /* custom per port */
static int BIP_Socket = -1;
/* port to use - stored in host byte order */
static uint16_t BIP_Port = 0xBAC0;
/* IP Address - stored in host byte order */
static struct in_addr BIP_Address;
/* Broadcast Address - stored in host byte order */
static struct in_addr BIP_Broadcast_Address;
void bip_set_socket(int sock_fd)
{
BIP_Socket = sock_fd;
}
int bip_socket(void)
{
return BIP_Socket;
}
bool bip_valid(void)
{
return (BIP_Socket != -1);
}
void bip_cleanup(void)
{
if (bip_valid())
close(BIP_Socket);
BIP_Socket = -1;
return;
}
/* set using network byte order */
void bip_set_addr(uint32_t net_address)
{
BIP_Address.s_addr = ntohl(net_address);
}
/* returns host byte order */
uint32_t bip_get_addr(void)
{
return BIP_Address.s_addr;
}
/* set using network byte order */
void bip_set_broadcast_addr(uint32_t net_address)
{
BIP_Broadcast_Address.s_addr = ntohl(net_address);
}
/* returns host byte order */
uint32_t bip_get_broadcast_addr(void)
{
return BIP_Broadcast_Address.s_addr;
}
/* set using host byte order */
void bip_set_port(uint16_t port)
{
BIP_Port = port;
}
/* returns host byte order */
uint16_t bip_get_port(void)
{
return BIP_Port;
}
/* function to send a packet out the BACnet/IP socket (Annex J) */
/* returns 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;
(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) {
/* broadcast */
bip_dest.sin_addr.s_addr = htonl(BIP_Broadcast_Address.s_addr);
bip_dest.sin_port = htons(BIP_Port);
memset(&(bip_dest.sin_zero), '\0', 8);
mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
} else if (dest->mac_len == 6) {
/* valid unicast */
(void) decode_unsigned32(&dest->mac[0],
(uint32_t *)&(bip_dest.sin_addr.s_addr));
(void) decode_unsigned16(&dest->mac[4], &(bip_dest.sin_port));
memset(&(bip_dest.sin_zero), '\0', 8);
mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU;
} else {
/* invalid address */
return -1;
}
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;
}
/* receives a BACnet/IP packet */
/* returns the number of octets 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)
{ /* number of milliseconds to wait for a packet */
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 = { -1 };
socklen_t sin_len = sizeof(sin);
unsigned i = 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((unsigned int) 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 ((pdu[1] == BVLC_ORIGINAL_UNICAST_NPDU) ||
(pdu[1] == BVLC_ORIGINAL_BROADCAST_NPDU)) {
/* ignore messages from me */
if (sin.sin_addr.s_addr == htonl(BIP_Address.s_addr))
pdu_len = 0;
else {
/* copy the source address
FIXME: IPv6? */
src->mac_len = 6;
(void) encode_unsigned32(&src->mac[0], sin.sin_addr.s_addr);
(void) encode_unsigned16(&src->mac[4], sin.sin_port);
/* 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) {
/* shift the buffer to return a valid PDU */
for (i = 0; i < pdu_len; i++) {
pdu[i] = pdu[i+4];
}
}
/* ignore packets that are too large */
/* clients should check my max-apdu first */
else
pdu_len = 0;
}
}
return pdu_len;
}
void bip_get_my_address(BACNET_ADDRESS * my_address)
{
int i = 0;
my_address->mac_len = 6;
(void) encode_unsigned32(&my_address->mac[0],
htonl(BIP_Address.s_addr));
(void) encode_unsigned16(&my_address->mac[4], htons(BIP_Port));
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;
(void) encode_unsigned32(&dest->mac[0],
htonl(BIP_Broadcast_Address.s_addr));
(void) encode_unsigned16(&dest->mac[4], htons(BIP_Port));
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;
}
+831
View File
@@ -0,0 +1,831 @@
/*####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> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include <time.h> /* for the standard bool type. */
#include "bacdcode.h"
#include "bacint.h"
#include "bvlc.h"
#include "bip.h"
/* Handle the BACnet Virtual Link Control (BVLC), which includes:
BACnet Broadcast Management Device,
Broadcast Distribution Table, and
Foreign Device Registration */
typedef struct
{
/* true if valid entry - false if not */
bool valid;
/* BACnet/IP address */
struct in_addr dest_address;
/* BACnet/IP port number - not always 47808=BAC0h */
uint16_t dest_port;
/* Broadcast Distribution Mask - stored in host byte order */
struct in_addr broadcast_mask;
} BBMD_TABLE_ENTRY;
#define MAX_BBMD_ENTRIES 128
static BBMD_TABLE_ENTRY BBMD_Table[MAX_BBMD_ENTRIES];
/*Each device that registers as a foreign device shall be placed
in an entry in the BBMD's Foreign Device Table (FDT). Each
entry shall consist of the 6-octet B/IP address of the registrant;
the 2-octet Time-to-Live value supplied at the time of
registration; and a 2-octet value representing the number of
seconds remaining before the BBMD will purge the registrant's FDT
entry if no re-registration occurs. This value will be initialized
to the 2-octet Time-to-Live value supplied at the time of
registration.*/
typedef struct
{
bool valid;
/* BACnet/IP address */
struct in_addr dest_address;
/* BACnet/IP port number - not always 47808=BAC0h */
uint16_t dest_port;
/* seconds for valid entry lifetime */
uint16_t time_to_live;
/* our counter */
time_t seconds_remaining; /* includes 30 second grace period */
} FD_TABLE_ENTRY;
#define MAX_FD_ENTRIES 128
static FD_TABLE_ENTRY FD_Table[MAX_FD_ENTRIES];
void bvlc_maintenance_timer(unsigned seconds)
{
unsigned i = 0;
for (i = 0; i < MAX_FD_ENTRIES; i++) {
if (FD_Table[i].valid) {
if (FD_Table[i].seconds_remaining) {
if (FD_Table[i].seconds_remaining < seconds) {
FD_Table[i].seconds_remaining = 0;
} else {
FD_Table[i].seconds_remaining -= seconds;
}
if (FD_Table[i].seconds_remaining == 0) {
FD_Table[i].valid = false;
}
}
}
}
}
int bvlc_encode_bip_address(
uint8_t * pdu, /* buffer to store encoding */
struct in_addr *address, /* in host format */
uint16_t port)
{
int len = 0;
if (pdu) {
len = encode_unsigned32(&pdu[0], address->s_addr);
len += encode_unsigned16(&pdu[len], port);
}
return len;
}
int bvlc_decode_bip_address(
uint8_t * pdu, /* buffer to extract encoded address */
struct in_addr * address, /* in host format */
uint16_t * port)
{
int len = 0;
if (pdu) {
(void) decode_unsigned32(&pdu[0], &(address->s_addr));
(void) decode_unsigned16(&pdu[4], port);
len = 6;
}
return len;
}
/* used for both read and write entries */
int bvlc_encode_address_entry(uint8_t * pdu,
struct in_addr *address,
uint16_t port,
struct in_addr *mask)
{
int len = 0;
if (pdu) {
len = bvlc_encode_bip_address(pdu, address, port);
len += encode_unsigned32(&pdu[len], mask->s_addr);
}
return len;
}
int bvlc_encode_bvlc_result(
uint8_t * pdu,
BACNET_BVLC_RESULT result_code)
{
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_RESULT;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 6);
encode_unsigned16(&pdu[4], result_code);
}
return 6;
}
int bvlc_encode_write_bdt_init(
uint8_t * pdu,
unsigned entries)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 4 + entries * 10);
len = 4;
}
return len;
}
int bvlc_encode_read_bdt(
uint8_t * pdu)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_READ_BROADCAST_DISTRIBUTION_TABLE;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 4);
len = 4;
}
return len;
}
int bvlc_encode_read_bdt_ack_init(
uint8_t * pdu,
unsigned entries)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_READ_BROADCAST_DISTRIBUTION_TABLE_ACK;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 4 + entries * 10);
len = 4;
}
return len;
}
int bvlc_encode_forwarded_npdu(uint8_t * pdu,
BACNET_ADDRESS * src,
uint8_t * npdu,
unsigned npdu_length)
{
int len = 0;
unsigned i; /* for loop counter */
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_FORWARDED_NPDU;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 4 + 6 + npdu_length);
len = 4;
for (i = 0; i < 6; i++) {
pdu[len] = src->adr[i];
len++;
}
for (i = 0; i < npdu_length; i++) {
pdu[len] = npdu[i];
len++;
}
}
return len;
}
int bvlc_encode_register_foreign_device(uint8_t * pdu,
uint16_t time_to_live_seconds)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_REGISTER_FOREIGN_DEVICE;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 6);
encode_unsigned16(&pdu[2], time_to_live_seconds);
len = 6;
}
return len;
}
int bvlc_encode_read_fdt(
uint8_t * pdu)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_READ_FOREIGN_DEVICE_TABLE;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 4);
len = 4;
}
return len;
}
int bvlc_encode_read_fdt_ack_init(
uint8_t * pdu,
unsigned entries)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_READ_FOREIGN_DEVICE_TABLE_ACK;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 4 + entries * 10);
len = 4;
}
return len;
}
int bvlc_encode_delete_fdt_entry(uint8_t * pdu,
struct in_addr *address,
uint16_t port)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_READ_FOREIGN_DEVICE_TABLE;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 10);
/* FDT Entry */
encode_unsigned32(&pdu[0], address->s_addr);
encode_unsigned16(&pdu[4], port);
len = 10;
}
return len;
}
int bvlc_encode_distribute_broadcast_to_network(uint8_t * pdu,
uint8_t * npdu,
unsigned npdu_length)
{
int len = 0; /* return value */
unsigned i; /* for loop counter */
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
len = encode_unsigned16(&pdu[2], 4 + npdu_length) + 2;
for (i = 0; i < npdu_length; i++) {
pdu[len] = npdu[i];
len++;
}
}
return len;
}
int bvlc_encode_original_unicast_npdu(uint8_t * pdu,
uint8_t * npdu,
unsigned npdu_length)
{
int len = 0; /* return value */
unsigned i = 0; /* loop counter */
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_ORIGINAL_UNICAST_NPDU;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
len = encode_unsigned16(&pdu[2], 4 + npdu_length) + 2;
for (i = 0; i < npdu_length; i++) {
pdu[len] = npdu[i];
len++;
}
}
return len;
}
int bvlc_encode_original_broadcast_npdu(uint8_t * pdu,
uint8_t * npdu,
unsigned npdu_length)
{
int len = 0; /* return value */
unsigned i = 0; /* loop counter */
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
len = encode_unsigned16(&pdu[2], 4 + npdu_length) + 2;
for (i = 0; i < npdu_length; i++) {
pdu[len] = npdu[i];
len++;
}
}
return len;
}
/* copy the source internet address to the BACnet address */
/* FIXME: IPv6? */
void bvlc_internet_to_bacnet_address(
BACNET_ADDRESS * src, /* returns the BACnet source address */
struct sockaddr_in *sin)
{ /* source internet address */
int len = 0;
uint32_t address;
uint16_t port;
if (src && sin) {
address = ntohl(sin->sin_addr.s_addr);
len = encode_unsigned32(&src->mac[0], address);
port = ntohs(sin->sin_port);
len += encode_unsigned16(&src->mac[4], port);
src->mac_len = len;
src->net = 0;
src->len = 0;
}
return;
}
/* copy the source internet address to the BACnet address */
/* FIXME: IPv6? */
/* FIXME: is sockaddr_in host or network order? */
void bvlc_bacnet_to_internet_address(
struct sockaddr_in *sin, /* source internet address */
BACNET_ADDRESS * src) /* returns the BACnet source address */
{
int len = 0;
uint32_t address;
uint16_t port;
if (src && sin) {
if (src->mac_len == 6) {
len = decode_unsigned32(&src->mac[0], &address);
len += decode_unsigned16(&src->mac[4], &port );
sin->sin_addr.s_addr = htonl(address);
sin->sin_port = htons(port);
}
}
return;
}
void bvlc_bdt_forward_npdu(
struct sockaddr_in *sin, /* the source address */
uint8_t * npdu, /* the NPDU */
uint16_t npdu_length)
{ /* length of the NPDU */
uint8_t mtu[MAX_MPDU] = {0};
int mtu_len = 0;
int bytes_sent = 0;
unsigned i = 0; /* loop counter */
struct sockaddr_in bip_dest;
BACNET_ADDRESS src;
/* assumes that the driver has already been initialized */
if (bip_socket() < 0) {
return;
}
bvlc_internet_to_bacnet_address(&src, sin);
mtu_len = bvlc_encode_forwarded_npdu(
&mtu[0],
&src,
npdu,
npdu_length);
/* load destination IP address */
bip_dest.sin_family = AF_INET;
/* loop through the BDT and send one to each entry, except us */
for (i = 0; i < MAX_BBMD_ENTRIES; i++) {
if (BBMD_Table[i].valid) {
/* The B/IP address to which the Forwarded-NPDU message is
sent is formed by inverting the broadcast distribution
mask in the BDT entry and logically ORing it with the
BBMD address of the same entry. */
bip_dest.sin_addr.s_addr =
htonl(((~BBMD_Table[i].broadcast_mask.s_addr) |
BBMD_Table[i].dest_address.s_addr));
bip_dest.sin_port = htons(BBMD_Table[i].dest_port);
/* don't send to my broadcast address and same port */
if ((bip_dest.sin_addr.s_addr == htonl(bip_get_broadcast_addr())) &&
(bip_dest.sin_port == htons(bip_get_port))) {
continue;
}
/* don't send to my ip address and same port */
if ((bip_dest.sin_addr.s_addr == htonl(bip_get_addr())) &&
(bip_dest.sin_port == htons(bip_get_port))) {
continue;
}
/* Send the packet */
bytes_sent =
sendto(bip_socket(), (char *) mtu, mtu_len, 0,
(struct sockaddr *) &bip_dest,
sizeof(struct sockaddr));
}
}
return;
}
void bvlc_fdt_forward_npdu(
struct sockaddr_in *sin, /* the source address */
uint8_t * npdu, /* returns the NPDU */
uint16_t max_npdu) /* amount of space available in the NPDU */
{
uint8_t mtu[MAX_MPDU] = {0};
int mtu_len = 0;
int bytes_sent = 0;
unsigned i = 0; /* loop counter */
struct sockaddr_in bvlc_dest;
BACNET_ADDRESS src;
/* assumes that the driver has already been initialized */
if (bip_socket() < 0) {
return;
}
bvlc_internet_to_bacnet_address(&src, sin);
mtu_len = bvlc_encode_forwarded_npdu(
&mtu[0],
&src,
npdu,
npdu_length);
/* load destination IP address */
bvlc_dest.sin_family = AF_INET;
/* loop through the FDT and send one to each entry */
for (i = 0; i < MAX_FD_ENTRIES; i++) {
if (FD_Table[i].valid && FD_Table[i].seconds_remaining) {
bvlc_dest.sin_addr.s_addr =
htonl(FD_Table[i].dest_address.s_addr);
bvlc_dest.sin_port = htons(FD_Table[i].dest_port);
/* Send the packet */
bytes_sent =
sendto(bip_socket(), (char *) mtu, mtu_len, 0,
(struct sockaddr *) &bvlc_dest,
sizeof(struct sockaddr));
}
}
return;
}
/* returns:
Number of bytes received, or 0 if none or timeout. */
uint16_t bvlc_receive(
BACNET_ADDRESS * src, /* returns the source address */
uint8_t * npdu, /* returns the NPDU */
uint16_t max_npdu, /* amount of space available in the NPDU */
unsigned timeout) /* number of milliseconds to wait for a packet */
{
uint16_t pdu_len = 0; /* return value */
fd_set read_fds;
int max = 0;
struct timeval select_timeout;
struct sockaddr_in sin = { -1 };
socklen_t sin_len = sizeof(sin);
int function_type = 0;
int received_bytes = 0;
unsigned i =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((unsigned int) 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 *) &npdu[0],
max_npdu, 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 (buf[0] != BVLL_TYPE_BACNET_IP) {
return 0;
}
function_type = npdu[1];
/* decode the length of the PDU - length is inclusive of BVLC */
(void) decode_unsigned16(&npdu[2], &npdu_len);
/* subtract off the BVLC header */
npdu_len -= 4;
switch (function_type) {
case BVLC_RESULT:
break;
case BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE:
/* Upon receipt of a BVLL Write-Broadcast-Distribution-Table
message, a BBMD shall attempt to create or replace its BDT,
depending on whether or not a BDT has previously existed.
If the creation or replacement of the BDT is successful, the BBMD
shall return a BVLC-Result message to the originating device with
a result code of X'0000'. Otherwise, the BBMD shall return a
BVLC-Result message to the originating device with a result code
of X'0010' indicating that the write attempt has failed. */
break;
case BVLC_READ_BROADCAST_DISTRIBUTION_TABLE:
break;
case BVLC_READ_BROADCAST_DISTRIBUTION_TABLE_ACK:
break;
case BVLC_FORWARDED_NPDU:
/* Upon receipt of a BVLL Forwarded-NPDU message, a BBMD shall
process it according to whether it was received from a peer
BBMD as the result of a directed broadcast or a unicast
transmission. A BBMD may ascertain the method by which Forwarded-
NPDU messages will arrive by inspecting the broadcast distribution
mask field in its own BDT entry since all BDTs are required
to be identical. If the message arrived via directed broadcast,
it was also received by the other devices on the BBMD's subnet. In
this case the BBMD merely retransmits the message directly to each
foreign device currently in the BBMD's FDT. If the
message arrived via a unicast transmission it has not yet been
received by the other devices on the BBMD's subnet. In this case,
the message is sent to the devices on the BBMD's subnet using the
B/IP broadcast address as well as to each foreign device
currently in the BBMD's FDT. A BBMD on a subnet with no other
BACnet devices may omit the broadcast using the B/IP
broadcast address. The method by which a BBMD determines whether
or not other BACnet devices are present is a local matter. */
bvlc_broadcast_npdu(&sin, &npdu[4], npdu_len);
bvlc_fdt_forward_npdu(&sin, &npdu[4], npdu_len);
break;
case BVLC_REGISTER_FOREIGN_DEVICE:
break;
case BVLC_READ_FOREIGN_DEVICE_TABLE:
break;
case BVLC_READ_FOREIGN_DEVICE_TABLE_ACK:
break;
case BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY:
break;
case BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK:
bvlc_broadcast_forward_npdu(&sin, &npdu[4], npdu_len);
bvlc_fdt_forward_npdu(&sin, &npdu[4], npdu_len);
break;
case BVLC_ORIGINAL_UNICAST_NPDU:
/* ignore messages from me */
if (sin.sin_addr.s_addr == htonl(bip_get_addr())) {
npdu_len = 0;
} else {
bvlc_internet_to_bacnet_address(src, &sin);
if (npdu_len < max_npdu) {
/* shift the buffer to return a valid PDU */
for (i = 0; i < npdu_len; i++) {
npdu[i] = npdu[i+4];
}
} else {
/* ignore packets that are too large */
/* clients should check my max-apdu first */
npdu_len = 0;
}
}
break;
case BVLC_ORIGINAL_BROADCAST_NPDU:
/* Upon receipt of a BVLL Original-Broadcast-NPDU message,
a BBMD shall construct a BVLL Forwarded-NPDU message and
send it to each IP subnet in its BDT with the exception
of its own. The B/IP address to which the Forwarded-NPDU
message is sent is formed by inverting the broadcast
distribution mask in the BDT entry and logically ORing it
with the BBMD address of the same entry. This process
produces either the directed broadcast address of the remote
subnet or the unicast address of the BBMD on that subnet
depending on the contents of the broadcast distribution
mask. See J.4.3.2.. In addition, the received BACnet NPDU
shall be sent directly to each foreign device currently in
the BBMD's FDT also using the BVLL Forwarded-NPDU message. */
bvlc_internet_to_bacnet_address(src, &sin);
if (npdu_len < max_npdu) {
/* shift the buffer to return a valid PDU */
for (i = 0; i < npdu_len; i++) {
npdu[i] = npdu[i+4];
}
/* if BDT or FDT entries exist, Forward the NPDU */
bvlc_bdt_forward_npdu(&sin, &npdu[0], npdu_len);
bvlc_fdt_forward_npdu(&sin, &npdu[0], npdu_len);
} else {
/* ignore packets that are too large */
npdu_len = 0;
}
break;
default:
break;
}
return npdu_len;
}
/* function to send a packet out the BACnet/IP socket (Annex J) */
/* returns number of bytes sent on success, negative number on failure */
int bvlc_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 bvlc_dest;
uint8_t mtu[MAX_MPDU] = { 0 };
int mtu_len = 0;
int bytes_sent = 0;
/* bip datalink doesn't need to know the npdu data */
(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) {
/* broadcast */
bip_dest.sin_addr.s_addr = htonl(BIP_Broadcast_Address.s_addr);
bip_dest.sin_port = htons(BIP_Port);
memset(&(bip_dest.sin_zero), '\0', 8);
mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
} else if (dest->mac_len == 6) {
/* valid unicast */
(void) decode_unsigned32(&dest->mac[0],
&(bip_dest.sin_addr.s_addr));
(void) decode_unsigned16(&dest->mac[4], &(bip_dest.sin_port));
memset(&(bip_dest.sin_zero), '\0', 8);
mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU;
} else {
/* invalid address */
return -1;
}
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;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testBIPAddress(Test * pTest)
{
uint8_t apdu[50] = { 0 };
uint32_t value = 0, test_value = 0;
int len = 0, test_len = 0;
struct in_addr address;
struct in_addr test_address;
uint16_t port = 0, test_port = 0;
address.s_addr = 42;
len = bvlc_encode_bip_address(&apdu[0],
&address, port);
test_len = bvlc_decode_bip_address(&apdu[0],
&test_address, &test_port);
ct_test(pTest, len == test_len);
ct_test(pTest, address.s_addr == test_address.s_addr);
ct_test(pTest, port == test_port);
}
void testInternetAddress(Test * pTest)
{
BACNET_ADDRESS src;
BACNET_ADDRESS test_src;
struct sockaddr_in sin;
struct sockaddr_in test_sin;
sin.sin_port = htons(0xBAC0);
sin.sin_addr.s_addr = inet_addr("192.168.0.1");
bvlc_internet_to_bacnet_address(&src, &sin);
bvlc_bacnet_to_internet_address(&test_sin, &src);
ct_test(pTest, sin.sin_port == test_sin.sin_port);
ct_test(pTest, sin.sin_addr.s_addr == test_sin.sin_addr.s_addr);
}
#ifdef TEST_BVLC
int main(void)
{
Test * pTest;
bool rc;
pTest = ct_create("BACnet Virtual Link Control", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBIPAddress);
assert(rc);
rc = ct_addTestFunction(pTest, testInternetAddress);
assert(rc);
/* configure output */
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_BBMD */
#endif /* TEST */
+979
View File
@@ -0,0 +1,979 @@
/*####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 "cov.h"
#include "device.h"
#include "datalink.h"
#include "npdu.h"
/* encode service */
/* Change-Of-Value Services
COV Subscribe
COV Subscribe Property
COV Notification
Unconfirmed COV Notification
*/
static int notify_encode_adpu(uint8_t * apdu, BACNET_COV_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
BACNET_PROPERTY_VALUE *value = NULL; /* value in list */
if (apdu) {
/* tag 0 - subscriberProcessIdentifier */
len = encode_context_unsigned(&apdu[apdu_len],
0, data->subscriberProcessIdentifier);
apdu_len += len;
/* tag 1 - initiatingDeviceIdentifier */
len = encode_context_object_id(&apdu[apdu_len],
1, OBJECT_DEVICE, data->initiatingDeviceIdentifier);
apdu_len += len;
/* tag 2 - monitoredObjectIdentifier */
len = encode_context_object_id(&apdu[apdu_len],
2,
data->monitoredObjectIdentifier.type,
data->monitoredObjectIdentifier.instance);
apdu_len += len;
/* tag 3 - timeRemaining */
len = encode_context_unsigned(&apdu[apdu_len],
3, data->timeRemaining);
apdu_len += len;
/* tag 4 - listOfValues */
len = encode_opening_tag(&apdu[apdu_len], 4);
apdu_len += len;
/* the first value includes a pointer to the next value, etc */
/* FIXME: for small implementations, we might try a partial
approach like the rpm.c where the values are encoded with
a separate function */
value = &data->listOfValues;
while (value != NULL) {
/* tag 0 - propertyIdentifier */
len = encode_context_enumerated(&apdu[apdu_len],
0, value->propertyIdentifier);
apdu_len += len;
/* tag 1 - propertyArrayIndex OPTIONAL */
if (value->propertyArrayIndex != BACNET_ARRAY_ALL) {
len = encode_context_unsigned(&apdu[apdu_len],
1, value->propertyArrayIndex);
apdu_len += len;
}
/* tag 2 - value */
/* abstract syntax gets enclosed in a context tag */
len = encode_opening_tag(&apdu[apdu_len], 2);
apdu_len += len;
len = bacapp_encode_application_data(&apdu[apdu_len],
&value->value);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], 2);
apdu_len += len;
/* tag 3 - priority OPTIONAL */
if (value->priority != BACNET_NO_PRIORITY) {
len = encode_context_unsigned(&apdu[apdu_len], 3,
value->priority);
apdu_len += len;
}
/* is there another one to encode? */
/* FIXME: check to see if there is room in the APDU */
value = value->next;
}
len = encode_closing_tag(&apdu[apdu_len], 4);
apdu_len += len;
}
return apdu_len;
}
int ccov_notify_encode_apdu(uint8_t * apdu,
uint8_t invoke_id, BACNET_COV_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
uint16_t max_apdu = Device_Max_APDU_Length_Accepted();
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_COV_NOTIFICATION;
apdu_len = 4;
len = notify_encode_adpu(&apdu[apdu_len], data);
apdu_len += len;
}
return apdu_len;
}
int ucov_notify_encode_apdu(uint8_t * apdu, BACNET_COV_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_COV_NOTIFICATION; /* service choice */
apdu_len = 2;
len = notify_encode_adpu(&apdu[apdu_len], data);
apdu_len += len;
}
return apdu_len;
}
/* decode the service request only */
/* COV and Unconfirmed COV are the same */
int cov_notify_decode_service_request(uint8_t * apdu,
unsigned apdu_len, BACNET_COV_DATA * data)
{
int len = 0; /* return value */
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t decoded_value = 0; /* for decoding */
int decoded_type = 0; /* for decoding */
int property = 0; /* for decoding */
BACNET_PROPERTY_VALUE *value = NULL; /* value in list */
if (apdu_len && data) {
/* tag 0 - subscriberProcessIdentifier */
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);
data->subscriberProcessIdentifier = decoded_value;
} else
return -1;
/* tag 1 - initiatingDeviceIdentifier */
if (decode_is_context_tag(&apdu[len], 1)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_object_id(&apdu[len], &decoded_type,
&data->initiatingDeviceIdentifier);
if (decoded_type != OBJECT_DEVICE)
return -1;
} else
return -1;
/* tag 2 - monitoredObjectIdentifier */
if (decode_is_context_tag(&apdu[len], 2)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_object_id(&apdu[len], &decoded_type,
&data->monitoredObjectIdentifier.instance);
data->monitoredObjectIdentifier.type = decoded_type;
} else
return -1;
/* tag 3 - timeRemaining */
if (decode_is_context_tag(&apdu[len], 3)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
data->timeRemaining = decoded_value;
} else
return -1;
/* tag 4: opening context tag - listOfValues */
if (!decode_is_opening_tag_number(&apdu[len], 4))
return -1;
/* a tag number of 4 is not extended so only one octet */
len++;
/* the first value includes a pointer to the next value, etc */
value = &data->listOfValues;
while (value != NULL) {
/* tag 0 - propertyIdentifier */
if (decode_is_context_tag(&apdu[len], 0)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_enumerated(&apdu[len], len_value, &property);
value->propertyIdentifier = property;
} else
return -1;
/* tag 1 - propertyArrayIndex OPTIONAL */
if (decode_is_context_tag(&apdu[len], 1)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_unsigned(&apdu[len], len_value, &decoded_value);
value->propertyArrayIndex = decoded_value;
} else
value->propertyArrayIndex = BACNET_ARRAY_ALL;
/* tag 2: opening context tag - value */
if (!decode_is_opening_tag_number(&apdu[len], 2))
return -1;
/* a tag number of 2 is not extended so only one octet */
len++;
len += bacapp_decode_application_data(&apdu[len],
apdu_len - len, &value->value);
/* FIXME: check the return value; abort if no valid data? */
/* FIXME: there might be more than one data element in here! */
if (!decode_is_closing_tag_number(&apdu[len], 2))
return -1;
/* a tag number of 2 is not extended so only one octet */
len++;
/* tag 3 - priority OPTIONAL */
if (decode_is_context_tag(&apdu[len], 3)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_unsigned(&apdu[len], len_value, &decoded_value);
value->priority = decoded_value;
} else
value->priority = BACNET_NO_PRIORITY;
/* end of list? */
if (decode_is_closing_tag_number(&apdu[len], 4))
break;
/* is there another one to decode? */
value = value->next;
/* out of room to store more values */
if (value == NULL)
return -1;
}
}
return len;
}
/*
12.11.38Active_COV_Subscriptions
The Active_COV_Subscriptions property is a List of BACnetCOVSubscription, each of which consists of a Recipient, a
Monitored Property Reference, an Issue Confirmed Notifications flag, a Time Remaining value and an optional COV
Increment. This property provides a network-visible indication of those COV subscriptions that are active at any given time.
Whenever a COV Subscription is created with the SubscribeCOV or SubscribeCOVProperty service, a new entry is added to
the Active_COV_Subscriptions list. Similarly, whenever a COV Subscription is terminated, the corresponding entry shall be
removed from the Active_COV_Subscriptions list.
*/
/*
SubscribeCOV-Request ::= SEQUENCE {
subscriberProcessIdentifier [0] Unsigned32,
monitoredObjectIdentifier [1] BACnetObjectIdentifier,
issueConfirmedNotifications [2] BOOLEAN OPTIONAL,
lifetime [3] Unsigned OPTIONAL
}
*/
int cov_subscribe_encode_adpu(uint8_t * apdu,
uint8_t invoke_id, BACNET_SUBSCRIBE_COV_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
uint16_t max_apdu = Device_Max_APDU_Length_Accepted();
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_SUBSCRIBE_COV;
apdu_len = 4;
/* tag 0 - subscriberProcessIdentifier */
len = encode_context_unsigned(&apdu[apdu_len],
0, data->subscriberProcessIdentifier);
apdu_len += len;
/* tag 1 - monitoredObjectIdentifier */
len = encode_context_object_id(&apdu[apdu_len],
1,
data->monitoredObjectIdentifier.type,
data->monitoredObjectIdentifier.instance);
apdu_len += len;
/*
If both the 'Issue Confirmed Notifications' and
'Lifetime' parameters are absent, then this shall
indicate a cancellation request.
*/
if (!data->cancellationRequest) {
/* tag 2 - issueConfirmedNotifications */
len = encode_context_boolean(&apdu[apdu_len],
2, data->issueConfirmedNotifications);
apdu_len += len;
/* tag 3 - lifetime */
len = encode_context_unsigned(&apdu[apdu_len],
3, data->lifetime);
apdu_len += len;
}
}
return apdu_len;
}
/* decode the service request only */
int cov_subscribe_decode_service_request(uint8_t * apdu,
unsigned apdu_len, BACNET_SUBSCRIBE_COV_DATA * data)
{
int len = 0; /* return value */
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t decoded_value = 0; /* for decoding */
int decoded_type = 0; /* for decoding */
if (apdu_len && data) {
/* tag 0 - subscriberProcessIdentifier */
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);
data->subscriberProcessIdentifier = decoded_value;
} else
return -1;
/* tag 1 - monitoredObjectIdentifier */
if (decode_is_context_tag(&apdu[len], 1)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_object_id(&apdu[len], &decoded_type,
&data->monitoredObjectIdentifier.instance);
data->monitoredObjectIdentifier.type = decoded_type;
} else
return -1;
/* optional parameters - if missing, means cancellation */
if (len < apdu_len) {
/* tag 2 - issueConfirmedNotifications - optional */
if (decode_is_context_tag(&apdu[len], 2)) {
data->cancellationRequest = false;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
data->issueConfirmedNotifications =
decode_context_boolean(&apdu[len]);
len += len_value;
} else {
data->cancellationRequest = true;
}
/* tag 3 - lifetime - optional */
if (decode_is_context_tag(&apdu[len], 3)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
data->lifetime = decoded_value;
} else
data->lifetime = 0;
} else {
data->cancellationRequest = true;
}
}
return len;
}
/*
SubscribeCOVProperty-Request ::= SEQUENCE {
subscriberProcessIdentifier [0] Unsigned32,
monitoredObjectIdentifier [1] BACnetObjectIdentifier,
issueConfirmedNotifications [2] BOOLEAN OPTIONAL,
lifetime [3] Unsigned OPTIONAL,
monitoredPropertyIdentifier [4] BACnetPropertyReference,
covIncrement [5] REAL OPTIONAL
}
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 cov_subscribe_property_encode_adpu(uint8_t * apdu,
uint8_t invoke_id, BACNET_SUBSCRIBE_COV_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
uint16_t max_apdu = Device_Max_APDU_Length_Accepted();
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_SUBSCRIBE_COV_PROPERTY;
apdu_len = 4;
/* tag 0 - subscriberProcessIdentifier */
len = encode_context_unsigned(&apdu[apdu_len],
0, data->subscriberProcessIdentifier);
apdu_len += len;
/* tag 1 - monitoredObjectIdentifier */
len = encode_context_object_id(&apdu[apdu_len],
1,
data->monitoredObjectIdentifier.type,
data->monitoredObjectIdentifier.instance);
apdu_len += len;
if (!data->cancellationRequest) {
/* tag 2 - issueConfirmedNotifications */
len = encode_context_boolean(&apdu[apdu_len],
2, data->issueConfirmedNotifications);
apdu_len += len;
/* tag 3 - lifetime */
len = encode_context_unsigned(&apdu[apdu_len],
3, data->lifetime);
apdu_len += len;
}
/* tag 4 - monitoredPropertyIdentifier */
len = encode_opening_tag(&apdu[apdu_len], 4);
apdu_len += len;
len = encode_context_enumerated(&apdu[apdu_len],
0, data->monitoredProperty.propertyIdentifier);
apdu_len += len;
if (data->monitoredProperty.propertyArrayIndex != BACNET_ARRAY_ALL) {
len = encode_context_unsigned(&apdu[apdu_len],
1, data->monitoredProperty.propertyArrayIndex);
apdu_len += len;
}
len = encode_closing_tag(&apdu[apdu_len], 4);
apdu_len += len;
/* tag 5 - covIncrement */
if (data->covIncrementPresent) {
len = encode_context_real(&apdu[apdu_len],
5, data->covIncrement);
apdu_len += len;
}
}
return apdu_len;
}
/* decode the service request only */
int cov_subscribe_property_decode_service_request(uint8_t * apdu,
unsigned apdu_len, BACNET_SUBSCRIBE_COV_DATA * data)
{
int len = 0; /* return value */
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t decoded_value = 0; /* for decoding */
int decoded_type = 0; /* for decoding */
int property = 0; /* for decoding */
if (apdu_len && data) {
/* tag 0 - subscriberProcessIdentifier */
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);
data->subscriberProcessIdentifier = decoded_value;
} else
return -1;
/* tag 1 - monitoredObjectIdentifier */
if (decode_is_context_tag(&apdu[len], 1)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_object_id(&apdu[len], &decoded_type,
&data->monitoredObjectIdentifier.instance);
data->monitoredObjectIdentifier.type = decoded_type;
} else
return -2;
/* tag 2 - issueConfirmedNotifications - optional */
if (decode_is_context_tag(&apdu[len], 2)) {
data->cancellationRequest = false;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
data->issueConfirmedNotifications =
decode_context_boolean(&apdu[len]);
len++;
} else
data->cancellationRequest = true;
/* tag 3 - lifetime - optional */
if (decode_is_context_tag(&apdu[len], 3)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
data->lifetime = decoded_value;
} else
data->lifetime = 0;
/* tag 4 - monitoredPropertyIdentifier */
if (!decode_is_opening_tag_number(&apdu[len], 4))
return -3;
/* a tag number of 4 is not extended so only one octet */
len++;
/* the propertyIdentifier is tag 0 */
if (decode_is_context_tag(&apdu[len], 0)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_enumerated(&apdu[len], len_value, &property);
data->monitoredProperty.propertyIdentifier = property;
} else
return -4;
/* the optional array index is tag 1 */
if (decode_is_context_tag(&apdu[len], 1)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
data->monitoredProperty.propertyArrayIndex = decoded_value;
} else {
data->monitoredProperty.propertyArrayIndex = BACNET_ARRAY_ALL;
}
if (!decode_is_closing_tag_number(&apdu[len], 4))
return -5;
/* a tag number of 4 is not extended so only one octet */
len++;
/* tag 5 - covIncrement - optional */
if (decode_is_context_tag(&apdu[len], 5)) {
data->covIncrementPresent = true;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_real(&apdu[len], &data->covIncrement);
} else
data->covIncrementPresent = false;
}
return len;
}
int ucov_notify_send(uint8_t * buffer, BACNET_COV_DATA * data)
{
int len = 0;
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
/* unconfirmed is a broadcast */
datalink_get_broadcast_address(&dest);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&buffer[0], &dest, NULL, &npdu_data);
/* encode the APDU portion of the packet */
len = ucov_notify_encode_apdu(&buffer[pdu_len], data);
pdu_len += len;
/* send the data */
bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len);
return bytes_sent;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
#include "bacapp.h"
int ccov_notify_decode_apdu(uint8_t * apdu,
unsigned apdu_len, uint8_t * invoke_id, BACNET_COV_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 -2;
/* apdu[1] = encode_max_segs_max_apdu(0, Device_Max_APDU_Length_Accepted()); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_COV_NOTIFICATION)
return -3;
offset = 4;
/* optional limits - must be used as a pair */
if (apdu_len > offset) {
len =
cov_notify_decode_service_request(&apdu[offset],
apdu_len - offset, data);
}
return len;
}
int ucov_notify_decode_apdu(uint8_t * apdu,
unsigned apdu_len, BACNET_COV_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_UNCONFIRMED_SERVICE_REQUEST)
return -2;
if (apdu[1] != SERVICE_UNCONFIRMED_COV_NOTIFICATION)
return -3;
/* optional limits - must be used as a pair */
offset = 2;
if (apdu_len > offset) {
len =
cov_notify_decode_service_request(&apdu[offset],
apdu_len - offset, data);
}
return len;
}
int cov_subscribe_decode_apdu(uint8_t * apdu,
unsigned apdu_len, uint8_t * invoke_id,
BACNET_SUBSCRIBE_COV_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 -2;
/* apdu[1] = encode_max_segs_max_apdu(0, Device_Max_APDU_Length_Accepted()); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_SUBSCRIBE_COV)
return -3;
offset = 4;
/* optional limits - must be used as a pair */
if (apdu_len > offset) {
len =
cov_subscribe_decode_service_request(&apdu[offset],
apdu_len - offset, data);
}
return len;
}
int cov_subscribe_property_decode_apdu(uint8_t * apdu,
unsigned apdu_len, uint8_t * invoke_id,
BACNET_SUBSCRIBE_COV_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 -2;
/* apdu[1] = encode_max_segs_max_apdu(0, Device_Max_APDU_Length_Accepted()); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY)
return -3;
offset = 4;
/* optional limits - must be used as a pair */
if (apdu_len > offset) {
len =
cov_subscribe_property_decode_service_request(&apdu[offset],
apdu_len - offset, data);
}
return len;
}
/* dummy function stubs */
int npdu_encode_pdu(uint8_t * npdu,
BACNET_ADDRESS * dest,
BACNET_ADDRESS * src, BACNET_NPDU_DATA * npdu_data)
{
return 0;
}
void npdu_encode_npdu_data(BACNET_NPDU_DATA * npdu,
bool data_expecting_reply, BACNET_MESSAGE_PRIORITY priority)
{
}
/* 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;
}
/* dummy function stubs */
uint16_t Device_Max_APDU_Length_Accepted(void)
{
return MAX_APDU;
}
void testCOVNotifyData(Test * pTest,
BACNET_COV_DATA * data, BACNET_COV_DATA * test_data)
{
ct_test(pTest,
test_data->subscriberProcessIdentifier ==
data->subscriberProcessIdentifier);
ct_test(pTest,
test_data->initiatingDeviceIdentifier ==
data->initiatingDeviceIdentifier);
ct_test(pTest,
test_data->monitoredObjectIdentifier.type ==
data->monitoredObjectIdentifier.type);
ct_test(pTest,
test_data->monitoredObjectIdentifier.instance ==
data->monitoredObjectIdentifier.instance);
ct_test(pTest, test_data->timeRemaining == data->timeRemaining);
/* FIXME: test the listOfValues in some clever manner */
}
void testUCOVNotifyData(Test * pTest, BACNET_COV_DATA * data)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_COV_DATA test_data;
len = ucov_notify_encode_apdu(&apdu[0], data);
ct_test(pTest, len > 0);
apdu_len = len;
test_data.listOfValues.next = NULL;
len = ucov_notify_decode_apdu(&apdu[0], apdu_len, &test_data);
ct_test(pTest, len != -1);
testCOVNotifyData(pTest, data, &test_data);
}
void testCCOVNotifyData(Test * pTest, uint8_t invoke_id,
BACNET_COV_DATA * data)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_COV_DATA test_data;
uint8_t test_invoke_id = 0;
len = ccov_notify_encode_apdu(&apdu[0], invoke_id, data);
ct_test(pTest, len != 0);
apdu_len = len;
test_data.listOfValues.next = NULL;
len = ccov_notify_decode_apdu(&apdu[0], apdu_len,
&test_invoke_id, &test_data);
ct_test(pTest, len > 0);
ct_test(pTest, test_invoke_id == invoke_id);
testCOVNotifyData(pTest, data, &test_data);
}
void testCOVNotify(Test * pTest)
{
uint8_t invoke_id = 12;
BACNET_COV_DATA data;
/* BACNET_PROPERTY_VALUE value2; */
data.subscriberProcessIdentifier = 1;
data.initiatingDeviceIdentifier = 123;
data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
data.monitoredObjectIdentifier.instance = 321;
data.timeRemaining = 456;
data.listOfValues.propertyIdentifier = PROP_PRESENT_VALUE;
data.listOfValues.propertyArrayIndex = BACNET_ARRAY_ALL;
bacapp_parse_application_data(BACNET_APPLICATION_TAG_REAL,
"21.0", &data.listOfValues.value);
data.listOfValues.priority = 0;
data.listOfValues.next = NULL;
testUCOVNotifyData(pTest, &data);
testCCOVNotifyData(pTest, invoke_id, &data);
/* FIXME: add more values to the list of values */
}
void testCOVSubscribeData(Test * pTest,
BACNET_SUBSCRIBE_COV_DATA * data,
BACNET_SUBSCRIBE_COV_DATA * test_data)
{
ct_test(pTest,
test_data->subscriberProcessIdentifier ==
data->subscriberProcessIdentifier);
ct_test(pTest,
test_data->monitoredObjectIdentifier.type ==
data->monitoredObjectIdentifier.type);
ct_test(pTest,
test_data->monitoredObjectIdentifier.instance ==
data->monitoredObjectIdentifier.instance);
ct_test(pTest,
test_data->cancellationRequest == data->cancellationRequest);
if (test_data->cancellationRequest != data->cancellationRequest) {
printf("cancellation request failed!\n");
}
if (!test_data->cancellationRequest) {
ct_test(pTest,
test_data->issueConfirmedNotifications ==
data->issueConfirmedNotifications);
ct_test(pTest, test_data->lifetime == data->lifetime);
}
}
void testCOVSubscribePropertyData(Test * pTest,
BACNET_SUBSCRIBE_COV_DATA * data,
BACNET_SUBSCRIBE_COV_DATA * test_data)
{
testCOVSubscribeData(pTest, data, test_data);
ct_test(pTest,
test_data->monitoredProperty.propertyIdentifier ==
data->monitoredProperty.propertyIdentifier);
ct_test(pTest,
test_data->monitoredProperty.propertyArrayIndex ==
data->monitoredProperty.propertyArrayIndex);
ct_test(pTest,
test_data->covIncrementPresent == data->covIncrementPresent);
if (test_data->covIncrementPresent) {
ct_test(pTest, test_data->covIncrement == data->covIncrement);
}
}
void testCOVSubscribeEncoding(Test * pTest, uint8_t invoke_id,
BACNET_SUBSCRIBE_COV_DATA * data)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_SUBSCRIBE_COV_DATA test_data;
uint8_t test_invoke_id = 0;
len = cov_subscribe_encode_adpu(&apdu[0], invoke_id, data);
ct_test(pTest, len != 0);
apdu_len = len;
len = cov_subscribe_decode_apdu(&apdu[0], apdu_len,
&test_invoke_id, &test_data);
ct_test(pTest, len > 0);
ct_test(pTest, test_invoke_id == invoke_id);
testCOVSubscribeData(pTest, data, &test_data);
}
void testCOVSubscribePropertyEncoding(Test * pTest, uint8_t invoke_id,
BACNET_SUBSCRIBE_COV_DATA * data)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_SUBSCRIBE_COV_DATA test_data;
uint8_t test_invoke_id = 0;
len = cov_subscribe_property_encode_adpu(&apdu[0], invoke_id, data);
ct_test(pTest, len != 0);
apdu_len = len;
len = cov_subscribe_property_decode_apdu(&apdu[0], apdu_len,
&test_invoke_id, &test_data);
ct_test(pTest, len > 0);
ct_test(pTest, test_invoke_id == invoke_id);
testCOVSubscribePropertyData(pTest, data, &test_data);
}
void testCOVSubscribe(Test * pTest)
{
uint8_t invoke_id = 12;
BACNET_SUBSCRIBE_COV_DATA data;
data.subscriberProcessIdentifier = 1;
data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
data.monitoredObjectIdentifier.instance = 321;
data.cancellationRequest = false;
data.issueConfirmedNotifications = true;
data.lifetime = 456;
testCOVSubscribeEncoding(pTest, invoke_id, &data);
data.cancellationRequest = true;
testCOVSubscribeEncoding(pTest, invoke_id, &data);
}
void testCOVSubscribeProperty(Test * pTest)
{
uint8_t invoke_id = 12;
BACNET_SUBSCRIBE_COV_DATA data;
data.subscriberProcessIdentifier = 1;
data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
data.monitoredObjectIdentifier.instance = 321;
data.cancellationRequest = false;
data.issueConfirmedNotifications = true;
data.lifetime = 456;
data.monitoredProperty.propertyIdentifier = PROP_FILE_SIZE;
data.monitoredProperty.propertyArrayIndex = BACNET_ARRAY_ALL;
data.covIncrementPresent = true;
data.covIncrement = 1.0;
testCOVSubscribePropertyEncoding(pTest, invoke_id, &data);
data.cancellationRequest = true;
testCOVSubscribePropertyEncoding(pTest, invoke_id, &data);
data.cancellationRequest = false;
data.covIncrementPresent = false;
testCOVSubscribePropertyEncoding(pTest, invoke_id, &data);
}
#ifdef TEST_COV
int main(int argc, char *argv[])
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet COV", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testCOVNotify);
assert(rc);
rc = ct_addTestFunction(pTest, testCOVSubscribe);
assert(rc);
rc = ct_addTestFunction(pTest, testCOVSubscribeProperty);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_COV */
#endif /* TEST */
+288
View File
@@ -0,0 +1,288 @@
/*####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>
#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
+83
View File
@@ -0,0 +1,83 @@
/*####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 "datalink.h"
/* Function pointers - point to your datalink */
bool (*datalink_init)(char *ifname);
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);
void (*datalink_cleanup)(void);
void (*datalink_get_broadcast_address)(BACNET_ADDRESS * dest);
void (*datalink_get_my_address)(BACNET_ADDRESS * my_address);
void datalink_configure(void)
{
#if defined(BACDL_ETHERNET)
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;
#elif defined(BACDL_ARCNET)
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;
#elif defined(BACDL_MSTP)
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;
#elif defined(BACDL_BIP)
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;
#endif
}
}
+727
View File
@@ -0,0 +1,727 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2007 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 "datetime.h"
/* BACnet Date */
/* year = years since 1900 */
/* month 1=Jan */
/* day = day of month 1..31 */
/* wday 1=Monday...7=Sunday */
/* Wildcards:
A value of X'FF' in any of the four octets
shall indicate that the value is unspecified.
If all four octets = X'FF', the corresponding
time or date may be interpreted as "any" or "don't care"
*/
static bool is_leap_year(uint16_t year)
{
if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
return (true);
else
return (false);
}
static uint8_t month_days(uint16_t year, uint8_t month)
{
/* note: start with a zero in the first element to save us from a
month - 1 calculation in the lookup */
int month_days[13] =
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
/* February */
if ((month == 2) && is_leap_year(year))
return 29;
else if (month >= 1 && month <= 12)
return month_days[month];
else
return 0;
}
static uint32_t days_since_epoch(uint16_t year, uint8_t month, uint8_t day)
{
uint32_t days = 0; /* return value */
uint8_t monthdays; /* days in a month */
uint16_t years = 0; /* loop counter for years */
uint8_t months = 0; /* loop counter for months */
monthdays = month_days(year, month);
if ((year >= 1900) && (monthdays) && (day >= 1) && (day <= monthdays)) {
for (years = 1900; years < year; years++) {
days += 365;
if (is_leap_year(years))
days++;
}
for (months = 1; months < month; months++) {
days += month_days(years, months);
}
days += (day - 1);
}
return (days);
}
static void days_since_epoch_into_ymd(uint32_t days,
uint16_t * pYear, uint8_t * pMonth, uint8_t * pDay)
{
uint16_t year = 1900;
uint8_t month = 1;
uint8_t day = 1;
while (days >= 365) {
if ((is_leap_year(year)) && (days == 365))
break;
days -= 365;
if (is_leap_year(year))
--days;
year++;
}
while (days >= (uint32_t) month_days(year, month)) {
days -= month_days(year, month);
month++;
}
day += ((uint8_t) days);
if (pYear)
*pYear = year;
if (pMonth)
*pMonth = month;
if (pDay)
*pDay = day;
return;
}
/* Jan 1, 1900 is a Monday */
/* wday 1=Monday...7=Sunday */
static uint8_t day_of_week(uint16_t year, uint8_t month, uint8_t day)
{
return ((uint8_t) (days_since_epoch(year, month, day) % 7) + 1);
}
/* if the date1 is the same as date2, return is 0
if date1 is after date2, returns positive
if date1 is before date2, returns negative */
int datetime_compare_date(BACNET_DATE * date1, BACNET_DATE * date2)
{
int diff = 0;
if (date1 && date2) {
diff = (int) date1->year - (int) date2->year;
if (diff == 0) {
diff = (int) date1->month - (int) date2->month;
if (diff == 0) {
diff = (int) date1->day - (int) date2->day;
}
}
}
return diff;
}
/* if the time1 is the same as time2, return is 0
if time1 is after time2, returns positive
if time1 is before time2, returns negative */
int datetime_compare_time(BACNET_TIME * time1, BACNET_TIME * time2)
{
int diff = 0;
if (time1 && time2) {
diff = (int) time1->hour - (int) time2->hour;
if (diff == 0) {
diff = (int) time1->min - (int) time2->min;
if (diff == 0) {
diff = (int) time1->sec - (int) time2->sec;
if (diff == 0) {
diff =
(int) time1->hundredths - (int) time2->hundredths;
}
}
}
}
return diff;
}
/* if the datetime1 is the same as datetime2, return is 0
if datetime1 is before datetime2, returns negative
if datetime1 is after datetime2, returns positive */
int datetime_compare(BACNET_DATE_TIME * datetime1,
BACNET_DATE_TIME * datetime2)
{
int diff = 0;
diff = datetime_compare_date(&datetime1->date, &datetime2->date);
if (diff == 0) {
diff = datetime_compare_time(&datetime1->time, &datetime2->time);
}
return diff;
}
void datetime_copy_date(BACNET_DATE * dest_date, BACNET_DATE * src_date)
{
if (dest_date && src_date) {
dest_date->year = src_date->year;
dest_date->month = src_date->month;
dest_date->day = src_date->day;
dest_date->wday = src_date->wday;
}
}
void datetime_copy_time(BACNET_TIME * dest_time, BACNET_TIME * src_time)
{
if (dest_time && src_time) {
dest_time->hour = src_time->hour;
dest_time->min = src_time->min;
dest_time->sec = src_time->sec;
dest_time->hundredths = src_time->hundredths;
}
}
void datetime_copy(BACNET_DATE_TIME * dest_datetime,
BACNET_DATE_TIME * src_datetime)
{
datetime_copy_time(&dest_datetime->time, &src_datetime->time);
datetime_copy_date(&dest_datetime->date, &src_datetime->date);
}
void datetime_set_date(BACNET_DATE * bdate,
uint16_t year, uint8_t month, uint8_t day)
{
if (bdate) {
bdate->year = year;
bdate->month = month;
bdate->day = day;
bdate->wday = day_of_week(year, month, day);
}
}
void datetime_set_time(BACNET_TIME * btime,
uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths)
{
if (btime) {
btime->hour = hour;
btime->min = minute;
btime->sec = seconds;
btime->hundredths = hundredths;
}
}
void datetime_set(BACNET_DATE_TIME * bdatetime,
BACNET_DATE * bdate, BACNET_TIME * btime)
{
if (bdate && btime && bdatetime) {
bdatetime->time.hour = btime->hour;
bdatetime->time.min = btime->min;
bdatetime->time.sec = btime->sec;
bdatetime->time.hundredths = btime->hundredths;
bdatetime->date.year = bdate->year;
bdatetime->date.month = bdate->month;
bdatetime->date.day = bdate->day;
bdatetime->date.wday = bdate->wday;
}
}
void datetime_set_values(BACNET_DATE_TIME * bdatetime,
uint16_t year, uint8_t month, uint8_t day,
uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths)
{
if (bdatetime) {
bdatetime->date.year = year;
bdatetime->date.month = month;
bdatetime->date.day = day;
bdatetime->date.wday = day_of_week(year, month, day);
bdatetime->time.hour = hour;
bdatetime->time.min = minute;
bdatetime->time.sec = seconds;
bdatetime->time.hundredths = hundredths;
}
}
static uint32_t seconds_since_midnight(uint8_t hours, uint8_t minutes,
uint8_t seconds)
{
return ((hours * 60 * 60) + (minutes * 60) + seconds);
}
static void seconds_since_midnight_into_hms(uint32_t seconds,
uint8_t * pHours, uint8_t * pMinutes, uint8_t * pSeconds)
{
uint8_t hour = 0;
uint8_t minute = 0;
hour = (uint8_t) (seconds / (60 * 60));
seconds -= (hour * 60 * 60);
minute = (uint8_t) (seconds / 60);
seconds -= (minute * 60);
if (pHours)
*pHours = hour;
if (pMinutes)
*pMinutes = minute;
if (pSeconds)
*pSeconds = (uint8_t) seconds;
}
void datetime_add_minutes(BACNET_DATE_TIME * bdatetime, uint32_t minutes)
{
uint32_t bdatetime_minutes = 0;
uint32_t bdatetime_days = 0;
uint32_t days = 0;
/* convert bdatetime to seconds and days */
bdatetime_minutes = seconds_since_midnight(bdatetime->time.hour,
bdatetime->time.min, bdatetime->time.sec) / 60;
bdatetime_days = days_since_epoch(bdatetime->date.year,
bdatetime->date.month, bdatetime->date.day);
/* add */
days = minutes / (24 * 60);
bdatetime_days += days;
minutes -= (days * 24 * 60);
bdatetime_minutes += minutes;
days = bdatetime_minutes / (24 * 60);
bdatetime_days += days;
/* convert bdatetime from seconds and days */
seconds_since_midnight_into_hms(bdatetime_minutes * 60,
&bdatetime->time.hour, &bdatetime->time.min, &bdatetime->time.sec);
days_since_epoch_into_ymd(bdatetime_days,
&bdatetime->date.year,
&bdatetime->date.month, &bdatetime->date.day);
bdatetime->date.wday = day_of_week(bdatetime->date.year,
bdatetime->date.month, bdatetime->date.day);
}
bool datetime_wildcard(BACNET_DATE_TIME * bdatetime)
{
bool wildcard_present = false;
if (bdatetime) {
if ((bdatetime->date.year == (1900 + 0xFF)) &&
(bdatetime->date.month == 0xFF) &&
(bdatetime->date.day == 0xFF) &&
(bdatetime->date.wday == 0xFF) &&
(bdatetime->time.hour == 0xFF) &&
(bdatetime->time.min == 0xFF) &&
(bdatetime->time.sec == 0xFF) &&
(bdatetime->time.hundredths == 0xFF)) {
wildcard_present = true;
}
}
return wildcard_present;
}
void datetime_date_wildcard_set(BACNET_DATE * bdate)
{
if (bdate) {
bdate->year = 1900 + 0xFF;
bdate->month = 0xFF;
bdate->day = 0xFF;
bdate->wday = 0xFF;
}
}
void datetime_time_wildcard_set(BACNET_TIME * btime)
{
if (btime) {
btime->hour = 0xFF;
btime->min = 0xFF;
btime->sec = 0xFF;
btime->hundredths = 0xFF;
}
}
void datetime_wildcard_set(BACNET_DATE_TIME * bdatetime)
{
if (bdatetime) {
datetime_date_wildcard_set(&bdatetime->date);
datetime_time_wildcard_set(&bdatetime->time);
}
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testBACnetDateTimeWildcard(Test * pTest)
{
BACNET_DATE_TIME bdatetime;
bool status = false;
datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0);
status = datetime_wildcard(&bdatetime);
ct_test(pTest, status == false);
datetime_wildcard_set(&bdatetime);
status = datetime_wildcard(&bdatetime);
ct_test(pTest, status == true);
}
void testBACnetDateTimeAdd(Test * pTest)
{
BACNET_DATE_TIME bdatetime, test_bdatetime;
uint32_t minutes = 0;
int diff = 0;
datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0);
datetime_copy(&test_bdatetime, &bdatetime);
datetime_add_minutes(&bdatetime, minutes);
diff = datetime_compare(&test_bdatetime, &bdatetime);
ct_test(pTest, diff == 0);
datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0);
datetime_add_minutes(&bdatetime, 60);
datetime_set_values(&test_bdatetime, 1900, 1, 1, 1, 0, 0, 0);
diff = datetime_compare(&test_bdatetime, &bdatetime);
ct_test(pTest, diff == 0);
datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0);
datetime_add_minutes(&bdatetime, (24 * 60));
datetime_set_values(&test_bdatetime, 1900, 1, 2, 0, 0, 0, 0);
diff = datetime_compare(&test_bdatetime, &bdatetime);
ct_test(pTest, diff == 0);
datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0);
datetime_add_minutes(&bdatetime, (31 * 24 * 60));
datetime_set_values(&test_bdatetime, 1900, 2, 1, 0, 0, 0, 0);
diff = datetime_compare(&test_bdatetime, &bdatetime);
ct_test(pTest, diff == 0);
}
void testBACnetDateTimeSeconds(Test * pTest)
{
uint8_t hour = 0, minute = 0, second = 0;
uint8_t test_hour = 0, test_minute = 0, test_second = 0;
uint32_t seconds = 0, test_seconds;
for (hour = 0; hour < 24; hour++) {
for (minute = 0; minute < 60; minute += 3) {
for (second = 0; second < 60; second += 17) {
seconds = seconds_since_midnight(hour, minute, second);
seconds_since_midnight_into_hms(seconds,
&test_hour, &test_minute, &test_second);
test_seconds =
seconds_since_midnight(test_hour, test_minute,
test_second);
ct_test(pTest, seconds == test_seconds);
}
}
}
}
void testBACnetDate(Test * pTest)
{
BACNET_DATE bdate1, bdate2;
int diff = 0;
datetime_set_date(&bdate1, 1900, 1, 1);
datetime_copy_date(&bdate2, &bdate1);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff == 0);
datetime_set_date(&bdate2, 1900, 1, 2);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
datetime_set_date(&bdate2, 1900, 2, 1);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
datetime_set_date(&bdate2, 1901, 1, 1);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
/* midpoint */
datetime_set_date(&bdate1, 2007, 7, 15);
datetime_copy_date(&bdate2, &bdate1);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff == 0);
datetime_set_date(&bdate2, 2007, 7, 14);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff > 0);
datetime_set_date(&bdate2, 2007, 7, 1);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff > 0);
datetime_set_date(&bdate2, 2007, 7, 31);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
datetime_set_date(&bdate2, 2007, 8, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
datetime_set_date(&bdate2, 2007, 12, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
datetime_set_date(&bdate2, 2007, 6, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff > 0);
datetime_set_date(&bdate2, 2007, 1, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff > 0);
datetime_set_date(&bdate2, 2006, 7, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff > 0);
datetime_set_date(&bdate2, 1900, 7, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff > 0);
datetime_set_date(&bdate2, 2008, 7, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
datetime_set_date(&bdate2, 2154, 7, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
return;
}
void testBACnetTime(Test * pTest)
{
BACNET_TIME btime1, btime2;
int diff = 0;
datetime_set_time(&btime1, 0, 0, 0, 0);
datetime_copy_time(&btime2, &btime1);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff == 0);
datetime_set_time(&btime1, 23, 59, 59, 99);
datetime_copy_time(&btime2, &btime1);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff == 0);
/* midpoint */
datetime_set_time(&btime1, 12, 30, 30, 50);
datetime_copy_time(&btime2, &btime1);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff == 0);
datetime_set_time(&btime2, 12, 30, 30, 51);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff < 0);
datetime_set_time(&btime2, 12, 30, 31, 50);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff < 0);
datetime_set_time(&btime2, 12, 31, 30, 50);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff < 0);
datetime_set_time(&btime2, 13, 30, 30, 50);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff < 0);
datetime_set_time(&btime2, 12, 30, 30, 49);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff > 0);
datetime_set_time(&btime2, 12, 30, 29, 50);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff > 0);
datetime_set_time(&btime2, 12, 29, 30, 50);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff > 0);
datetime_set_time(&btime2, 11, 30, 30, 50);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff > 0);
return;
}
void testBACnetDateTime(Test * pTest)
{
BACNET_DATE_TIME bdatetime1, bdatetime2;
BACNET_DATE bdate;
BACNET_TIME btime;
int diff = 0;
datetime_set_values(&bdatetime1, 1900, 1, 1, 0, 0, 0, 0);
datetime_copy(&bdatetime2, &bdatetime1);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff == 0);
datetime_set_time(&btime, 0, 0, 0, 0);
datetime_set_date(&bdate, 1900, 1, 1);
datetime_set(&bdatetime1, &bdate, &btime);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff == 0);
/* midpoint */
/* if datetime1 is before datetime2, returns negative */
datetime_set_values(&bdatetime1, 2000, 7, 15, 12, 30, 30, 50);
datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 30, 30, 51);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 30, 31, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 31, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 13, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2000, 7, 16, 12, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2000, 8, 15, 12, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2001, 7, 15, 12, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 30, 30, 49);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 30, 29, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 29, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 11, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
datetime_set_values(&bdatetime2, 2000, 7, 14, 12, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
datetime_set_values(&bdatetime2, 2000, 6, 15, 12, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
datetime_set_values(&bdatetime2, 1999, 7, 15, 12, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
return;
}
void testDateEpoch(Test * pTest)
{
uint32_t days = 0;
uint16_t year = 0, test_year = 0;
uint8_t month = 0, test_month = 0;
uint8_t day = 0, test_day = 0;
days = days_since_epoch(1900, 1, 1);
ct_test(pTest, days == 0);
days_since_epoch_into_ymd(days, &year, &month, &day);
ct_test(pTest, year == 1900);
ct_test(pTest, month == 1);
ct_test(pTest, day == 1);
for (year = 1900; year <= 2154; year++) {
for (month = 1; month <= 12; month++) {
for (day = 1; day <= month_days(year, month); day++) {
days = days_since_epoch(year, month, day);
days_since_epoch_into_ymd(days,
&test_year, &test_month, &test_day);
ct_test(pTest, year == test_year);
ct_test(pTest, month == test_month);
ct_test(pTest, day == test_day);
}
}
}
}
void testBACnetDayOfWeek(Test * pTest)
{
uint8_t dow = 0;
/* 1/1/1900 is a Monday */
dow = day_of_week(1900, 1, 1);
ct_test(pTest, dow == 1);
/* 1/1/2007 is a Monday */
dow = day_of_week(2007, 1, 1);
ct_test(pTest, dow == 1);
dow = day_of_week(2007, 1, 2);
ct_test(pTest, dow == 2);
dow = day_of_week(2007, 1, 3);
ct_test(pTest, dow == 3);
dow = day_of_week(2007, 1, 4);
ct_test(pTest, dow == 4);
dow = day_of_week(2007, 1, 5);
ct_test(pTest, dow == 5);
dow = day_of_week(2007, 1, 6);
ct_test(pTest, dow == 6);
dow = day_of_week(2007, 1, 7);
ct_test(pTest, dow == 7);
dow = day_of_week(2007, 1, 31);
ct_test(pTest, dow == 3);
}
#ifdef TEST_DATE_TIME
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Date Time", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBACnetDate);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetTime);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetDateTime);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetDayOfWeek);
assert(rc);
rc = ct_addTestFunction(pTest, testDateEpoch);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetDateTimeSeconds);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetDateTimeAdd);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetDateTimeWildcard);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_DATE_TIME */
#endif /* TEST */
+308
View File
@@ -0,0 +1,308 @@
/*####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"
/* 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. */
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);
}
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;
}
/* 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;
}
/* 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;
int value = 0;
uint32_t value32 = 0;
/* check for value pointers */
if (apdu_len) {
/* Tag 0: timeDuration --optional-- */
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)
*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, &value);
if (enable_disable)
*enable_disable = (BACNET_COMMUNICATION_ENABLE_DISABLE)value;
/* 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, Device_Max_APDU_Length_Accepted()); */
*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 */
+101
View File
@@ -0,0 +1,101 @@
/*####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>
char *filename_remove_path(const char *filename_in)
{
char *filename_out = NULL;
/* 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++;
}
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 *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);
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 */
+281
View File
@@ -0,0 +1,281 @@
/*####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 "datalink.h"
#include "device.h"
#include "bacdcode.h"
#include "address.h"
/* 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_tagged_object_id(&apdu[apdu_len],
OBJECT_DEVICE, device_id);
apdu_len += len;
len = encode_tagged_unsigned(&apdu[apdu_len], max_apdu);
apdu_len += len;
len = encode_tagged_enumerated(&apdu[apdu_len], segmentation);
apdu_len += len;
len = encode_tagged_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 */
int 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;
int decoded_integer = 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 = 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_integer);
apdu_len += len;
if (decoded_integer >= MAX_BACNET_SEGMENTATION)
return -1;
if (pSegmentation)
*pSegmentation = decoded_integer;
/* 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;
}
int iam_send(uint8_t * buffer)
{
int len = 0;
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return 0;
/* I-Am is a global broadcast */
datalink_get_broadcast_address(&dest);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&buffer[0], &dest, NULL, &npdu_data);
/* encode the APDU portion of the packet */
len = iam_encode_apdu(&buffer[pdu_len],
Device_Object_Instance_Number(),
MAX_APDU, SEGMENTATION_NONE, Device_Vendor_Identifier());
pdu_len += len;
/* send data */
bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len);
return bytes_sent;
}
#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
/* 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;
}
void datalink_get_broadcast_address(BACNET_ADDRESS * dest)
{ /* destination address */
(void) dest;
}
uint16_t Device_Vendor_Identifier(void)
{
return 0;
}
uint32_t Device_Object_Instance_Number(void)
{
return 0;
}
void address_add_binding(uint32_t device_id,
unsigned max_apdu, BACNET_ADDRESS * src)
{
(void) device_id;
(void) max_apdu;
(void) src;
}
/* dummy for apdu dependency */
void tsm_free_invoke_id(uint8_t invokeID)
{
/* dummy stub for testing */
(void) invokeID;
}
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 */
+196
View File
@@ -0,0 +1,196 @@
/*####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"
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_tagged_object_id(&apdu[apdu_len],
data->device_id.type, data->device_id.instance);
apdu_len += len;
/* objectIdentifier */
len = encode_tagged_object_id(&apdu[apdu_len],
data->object_id.type, data->object_id.instance);
apdu_len += len;
/* objectName */
len = encode_tagged_character_string(&apdu[apdu_len],
&data->object_name);
apdu_len += len;
}
return apdu_len;
}
/* 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;
int 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;
}
#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 */
+228
View File
@@ -0,0 +1,228 @@
/*####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"
#if defined(__BORLANDC__) || defined(_MSC_VER)
#define strcasecmp 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 (strcasecmp(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,
int index,
int 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 */
+116
View File
@@ -0,0 +1,116 @@
/*####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"
#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
+716
View File
@@ -0,0 +1,716 @@
/*####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####*/
/* 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 data specified by key */
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 encode and decode macros */
void testKeySample(Test * pTest)
{
int type, id;
int type_list[] =
{ 0, 1, KEY_TYPE_MAX / 2, KEY_TYPE_MAX - 2, KEY_TYPE_MAX - 1, -1 };
int id_list[] =
{ 0, 1, KEY_ID_MAX / 2, 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;
}
/* test the FIFO */
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 */
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;
}
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;
}
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 */
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;
}
#ifdef TEST_KEYLIST
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("keylist", NULL);
/* 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, testKeySample);
assert(rc);
rc = ct_addTestFunction(pTest, testKeyListDataIndex);
assert(rc);
rc = ct_addTestFunction(pTest, testKeyListLarge);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_KEYLIST */
#endif /* TEST */
File diff suppressed because it is too large Load Diff
+58
View File
@@ -0,0 +1,58 @@
#include <stdio.h>
#include "mstp.h"
#include "indtext.h"
#include "bacenum.h"
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_HEADER_CRC, "HEADER_CRC"},
{MSTP_RECEIVE_STATE_DATA, "DATA"},
{0, NULL}
};
const char *mstptext_receive_state(int 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(int 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(int index)
{
return indtext_by_index_split_default(mstp_frame_type_text,
index,
FRAME_TYPE_PROPRIETARY_MIN,
"UNKNOWN", "PROPRIETARY");
}
+545
View File
@@ -0,0 +1,545 @@
/*####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"
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
*/
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 */
int 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)
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) {
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] = 0xFF;
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.
*/
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 = 0;
}
}
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 */
int i = 0; /* counter */
uint16_t src_net = 0;
uint16_t dest_net = 0;
uint8_t address_len = 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 */
address_len = npdu[len++];
if (dest) {
dest->net = dest_net;
dest->len = address_len;
}
if (address_len) {
for (i = 0; i < address_len; 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 */
/* SLEN = 0 Invalid */
/* SLEN > 0 specifies length of SADR field */
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 */
address_len = npdu[len++];
if (src) {
src->net = src_net;
src->len = address_len;
}
if (address_len) {
for (i = 0; i < address_len; i++) {
mac_octet = npdu[len++];
if (src)
src->adr[i] = mac_octet;
}
}
} else if (src) {
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 {
/* FIXME: another value for this? */
npdu_data->network_message_type = NETWORK_MESSAGE_INVALID;
}
}
return len;
}
void npdu_handler(BACNET_ADDRESS * src, /* source address */
uint8_t * pdu, /* PDU data */
uint16_t pdu_len)
{ /* length PDU */
int apdu_offset = 0;
BACNET_ADDRESS dest = { 0 };
BACNET_NPDU_DATA npdu_data = { 0 };
apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data);
if (npdu_data.network_layer_message) {
/*FIXME: network layer message received! Handle it! */
} else if ((apdu_offset > 0) && (apdu_offset <= pdu_len)) {
/* only handle the version that we know how to handle */
if (npdu_data.protocol_version == BACNET_PROTOCOL_VERSION)
apdu_handler(src, &pdu[apdu_offset],
(uint16_t) (pdu_len - apdu_offset));
}
return;
}
#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 */
+182
View File
@@ -0,0 +1,182 @@
/*####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"
/* 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;
}
/* 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;
int 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, Device_Max_APDU_Length_Accepted()); */
*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 */
+168
View File
@@ -0,0 +1,168 @@
/*####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"
/* 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;
}
/* 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;
}
#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 */
+273
View File
@@ -0,0 +1,273 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2004 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####*/
/* Functional Description: Generic ring buffer library for deeply
embedded system. See the unit tests for usage examples. */
#include <stddef.h>
#include <stdint.h>
#include "ringbuf.h"
/****************************************************************************
* DESCRIPTION: Returns the empty/full status of the ring buffer
* RETURN: true if the ring buffer is empty, false if it is not.
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
bool Ringbuf_Empty(RING_BUFFER const *b)
{
return (b->count == 0);
}
/****************************************************************************
* DESCRIPTION: Looks at the data from the head of the list without removing it
* RETURN: pointer to the data, or NULL if nothing in the list
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
char *Ringbuf_Get_Front(RING_BUFFER const *b)
{
return (b->count ? &(b->data[b->head * b->element_size]) : NULL);
}
/****************************************************************************
* DESCRIPTION: Gets the data from the front of the list, and removes it
* RETURN: pointer to the data, or NULL if nothing in the list
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
char *Ringbuf_Pop_Front(RING_BUFFER * b)
{
char *data = NULL; /* return value */
if (b->count) {
data = &(b->data[b->head * b->element_size]);
b->head++;
if (b->head >= b->element_count)
b->head = 0;
b->count--;
}
return data;
}
/****************************************************************************
* DESCRIPTION: Adds an element of data to the ring buffer
* RETURN: true on succesful add, false if not added
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
bool Ringbuf_Put(RING_BUFFER * b, /* ring buffer structure */
char *data_element)
{ /* one element to add to the ring */
bool status = false; /* return value */
unsigned offset = 0; /* offset into array of data */
char *ring_data = NULL; /* used to help point ring data */
unsigned i; /* loop counter */
if (b && data_element) {
/* limit the amount of data that we accept */
if (b->count < b->element_count) {
offset = b->head + b->count;
if (offset >= b->element_count)
offset -= b->element_count;
ring_data = b->data + offset * b->element_size;
for (i = 0; i < b->element_size; i++) {
ring_data[i] = data_element[i];
}
b->count++;
status = true;
}
}
return status;
}
/****************************************************************************
* DESCRIPTION: Configures the ring buffer
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void Ringbuf_Init(RING_BUFFER * b, /* ring buffer structure */
char *data, /* data block or array of data */
unsigned element_size, /* size of one element in the data block */
unsigned element_count)
{ /* number of elements in the data block */
b->head = 0;
b->count = 0;
b->data = data;
b->element_size = element_size;
b->element_count = element_count;
return;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
/* test the FIFO */
#define RING_BUFFER_DATA_SIZE 5
#define RING_BUFFER_COUNT 16
void testRingBuf(Test * pTest)
{
RING_BUFFER test_buffer;
char data_store[RING_BUFFER_DATA_SIZE * RING_BUFFER_COUNT];
char data[RING_BUFFER_DATA_SIZE];
char *test_data;
unsigned index;
unsigned data_index;
unsigned count;
unsigned dummy;
bool status;
Ringbuf_Init(&test_buffer, data_store, RING_BUFFER_DATA_SIZE,
RING_BUFFER_COUNT);
ct_test(pTest, Ringbuf_Empty(&test_buffer));
for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++) {
data[data_index] = data_index;
}
status = Ringbuf_Put(&test_buffer, data);
ct_test(pTest, status == true);
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
test_data = Ringbuf_Get_Front(&test_buffer);
for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++) {
ct_test(pTest, test_data[data_index] == data[data_index]);
}
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
test_data = Ringbuf_Pop_Front(&test_buffer);
for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++) {
ct_test(pTest, test_data[data_index] == data[data_index]);
}
ct_test(pTest, Ringbuf_Empty(&test_buffer));
/* fill to max */
for (index = 0; index < RING_BUFFER_COUNT; index++) {
for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE;
data_index++) {
data[data_index] = index;
}
status = Ringbuf_Put(&test_buffer, data);
ct_test(pTest, status == true);
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
}
/* verify actions on full buffer */
for (index = 0; index < RING_BUFFER_COUNT; index++) {
for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE;
data_index++) {
data[data_index] = index;
}
status = Ringbuf_Put(&test_buffer, data);
ct_test(pTest, status == false);
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
}
/* check buffer full */
for (index = 0; index < RING_BUFFER_COUNT; index++) {
test_data = Ringbuf_Get_Front(&test_buffer);
for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE;
data_index++) {
ct_test(pTest, test_data[data_index] == index);
}
test_data = Ringbuf_Pop_Front(&test_buffer);
for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE;
data_index++) {
ct_test(pTest, test_data[data_index] == index);
}
}
ct_test(pTest, Ringbuf_Empty(&test_buffer));
/* test the ring around the buffer */
for (index = 0; index < RING_BUFFER_COUNT; index++) {
for (count = 1; count < 4; count++) {
dummy = index * count;
for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE;
data_index++) {
data[data_index] = dummy;
}
status = Ringbuf_Put(&test_buffer, data);
ct_test(pTest, status == true);
}
for (count = 1; count < 4; count++) {
dummy = index * count;
test_data = Ringbuf_Get_Front(&test_buffer);
for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE;
data_index++) {
ct_test(pTest, test_data[data_index] == dummy);
}
test_data = Ringbuf_Pop_Front(&test_buffer);
for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE;
data_index++) {
ct_test(pTest, test_data[data_index] == dummy);
}
}
}
ct_test(pTest, Ringbuf_Empty(&test_buffer));
return;
}
#ifdef TEST_RINGBUF
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("ringbuf", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testRingBuf);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif
+341
View File
@@ -0,0 +1,341 @@
/*####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"
/* encode service */
int rp_encode_apdu(uint8_t * apdu,
uint8_t invoke_id, BACNET_READ_PROPERTY_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_READ_PROPERTY; /* service choice */
apdu_len = 4;
len = encode_context_object_id(&apdu[apdu_len], 0,
data->object_type, data->object_instance);
apdu_len += len;
len = encode_context_enumerated(&apdu[apdu_len], 1,
data->object_property);
apdu_len += len;
/* optional array index */
if (data->array_index != BACNET_ARRAY_ALL) {
len = encode_context_unsigned(&apdu[apdu_len], 2,
data->array_index);
apdu_len += len;
}
}
return apdu_len;
}
/* decode the service request only */
int rp_decode_service_request(uint8_t * apdu,
unsigned apdu_len, BACNET_READ_PROPERTY_DATA * data)
{
unsigned len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
int type = 0; /* for decoding */
int property = 0; /* for decoding */
uint32_t array_value = 0; /* for decoding */
/* check for value pointers */
if (apdu_len && data) {
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[len++], 0))
return -1;
len += decode_object_id(&apdu[len], &type, &data->object_instance);
data->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);
data->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 += decode_unsigned(&apdu[len], len_value_type,
&array_value);
data->array_index = array_value;
} else
data->array_index = BACNET_ARRAY_ALL;
} else
data->array_index = BACNET_ARRAY_ALL;
}
return (int) len;
}
int rp_ack_encode_apdu(uint8_t * apdu,
uint8_t invoke_id, BACNET_READ_PROPERTY_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_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 */
apdu_len += encode_context_object_id(&apdu[apdu_len], 0,
data->object_type, data->object_instance);
apdu_len += encode_context_enumerated(&apdu[apdu_len], 1,
data->object_property);
/* context 2 array index is optional */
if (data->array_index != BACNET_ARRAY_ALL) {
apdu_len += encode_context_unsigned(&apdu[apdu_len], 2,
data->array_index);
}
/* propertyValue */
apdu_len += encode_opening_tag(&apdu[apdu_len], 3);
for (len = 0; len < data->application_data_len; len++) {
apdu[apdu_len++] = data->application_data[len];
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 3);
}
return apdu_len;
}
int rp_ack_decode_service_request(uint8_t * apdu, int apdu_len, /* total length of the apdu */
BACNET_READ_PROPERTY_DATA * data)
{
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 object = 0, 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, &data->object_instance);
data->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);
data->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);
data->array_index = array_value;
} else
data->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 */
data->application_data = &apdu[len];
data->application_data_len = apdu_len - len - 1 /*closing tag */ ;
} else
return -1;
return len;
}
#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 * 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, Device_Max_APDU_Length_Accepted()); */
*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, data);
}
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 * 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_READ_PROPERTY)
return -1;
offset = 3;
if (apdu_len > offset) {
len = rp_ack_decode_service_request(&apdu[offset],
apdu_len - offset, data);
}
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 data;
BACNET_READ_PROPERTY_DATA test_data;
BACNET_OBJECT_TYPE object_type = OBJECT_DEVICE;
uint32_t object_instance = 0;
int object = 0;
data.object_type = OBJECT_DEVICE;
data.object_instance = 1;
data.object_property = PROP_OBJECT_IDENTIFIER;
data.array_index = BACNET_ARRAY_ALL;
data.application_data_len = encode_bacnet_object_id(&apdu2[0],
data.object_type, data.object_instance);
data.application_data = &apdu2[0];
len = rp_ack_encode_apdu(&apdu[0], invoke_id, &data);
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 == data.object_type);
ct_test(pTest, test_data.object_instance == data.object_instance);
ct_test(pTest, test_data.object_property == data.object_property);
ct_test(pTest, test_data.array_index == data.array_index);
ct_test(pTest,
test_data.application_data_len == data.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 == data.object_type);
ct_test(pTest, object_instance == data.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 data;
BACNET_READ_PROPERTY_DATA test_data;
data.object_type = OBJECT_DEVICE;
data.object_instance = 1;
data.object_property = PROP_OBJECT_IDENTIFIER;
data.array_index = BACNET_ARRAY_ALL;
len = rp_encode_apdu(&apdu[0], invoke_id, &data);
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 == data.object_type);
ct_test(pTest, test_data.object_instance == data.object_instance);
ct_test(pTest, test_data.object_property == data.object_property);
ct_test(pTest, test_data.array_index == data.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 */
+764
View File
@@ -0,0 +1,764 @@
/*####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 "rpm.h"
/* 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_PROPERTY_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, int32_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;
}
/* decode the object portion of the service request only */
int rpm_decode_object_id(uint8_t * apdu,
unsigned apdu_len,
BACNET_OBJECT_TYPE * object_type, uint32_t * object_instance)
{
unsigned len = 0;
int type = 0; /* for decoding */
/* check for value pointers */
if (apdu && apdu_len && object_type && object_instance) {
/* Tag 0: Object ID */
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: sequence of ReadAccessSpecification */
if (!decode_is_opening_tag_number(&apdu[len], 1))
return -1;
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))
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_PROPERTY_ID * object_property, int32_t * array_index)
{
unsigned len = 0;
unsigned option_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
int property = 0; /* for decoding */
uint32_t array_value = 0; /* for decoding */
/* check for valid pointers */
if (apdu && apdu_len && object_property && array_index) {
/* Tag 0: propertyIdentifier */
if (!decode_is_context_specific(&apdu[len]))
return -1;
len += decode_tag_number_and_value(&apdu[len],
&tag_number, &len_value_type);
if (tag_number != 0)
return -1;
len += decode_enumerated(&apdu[len], len_value_type, &property);
if (object_property)
*object_property = (BACNET_PROPERTY_ID)property;
/* Tag 1: Optional propertyArrayIndex */
if ((len < apdu_len) &&
decode_is_context_specific(&apdu[len]) &&
(!decode_is_closing_tag(&apdu[len]))) {
option_len =
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
if (tag_number == 1) {
len += option_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;
}
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_PROPERTY_MULTIPLE; /* service choice */
apdu_len = 3;
}
return apdu_len;
}
int rpm_ack_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) {
/* Tag 0: objectIdentifier */
apdu_len = encode_context_object_id(&apdu[0], 0,
object_type, 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, int32_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);
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_tagged_enumerated(&apdu[apdu_len], error_class);
apdu_len += encode_tagged_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;
}
/* 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;
int 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, int32_t * array_index)
{
unsigned len = 0;
unsigned tag_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
int 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 (!decode_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) &&
decode_is_context_specific(&apdu[len]) &&
(!decode_is_closing_tag(&apdu[len]))) {
tag_len = 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;
}
#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_PROPERTY_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, Device_Max_APDU_Length_Accepted()); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_READ_PROPERTY_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_OBJECT_TYPE object_type = OBJECT_DEVICE;
uint32_t object_instance = 0;
BACNET_PROPERTY_ID object_property = PROP_OBJECT_IDENTIFIER;
int32_t 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, &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;
/* decode the object property portion of the service request */
test_len = rpm_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_IDENTIFIER);
ct_test(pTest, array_index == BACNET_ARRAY_ALL);
len += test_len;
test_len = rpm_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_NAME);
ct_test(pTest, 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, &object_property, &array_index);
ct_test(pTest, test_len == -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;
/* try to decode an object id */
test_len = rpm_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_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_IDENTIFIER);
ct_test(pTest, array_index == BACNET_ARRAY_ALL);
len += test_len;
test_len = rpm_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_ALL);
ct_test(pTest, array_index == BACNET_ARRAY_ALL);
len += test_len;
test_len = rpm_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_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;
int32_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;
/* 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 */
apdu_len += rpm_ack_encode_apdu_object_begin(&apdu[apdu_len],
OBJECT_DEVICE, 123);
/* 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 */
apdu_len += rpm_ack_encode_apdu_object_begin(&apdu[apdu_len],
OBJECT_ANALOG_INPUT, 33);
/* 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 */
+209
View File
@@ -0,0 +1,209 @@
/*####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####*/
/* 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, /* actual size, in bytes, of the data block or array of data */
unsigned size)
{ /* number of bytes used */
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, /* number of bytes used */
unsigned data_size)
{ /* how many 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, /* number of bytes used */
unsigned data_size)
{ /* how many 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 use */
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 */
+214
View File
@@ -0,0 +1,214 @@
/*####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"
/* 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_tagged_date(&apdu[apdu_len], my_date);
apdu_len += len;
len = encode_tagged_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);
}
/* 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;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
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, bacapp_same_time(my_time, &test_time));
ct_test(pTest, bacapp_same_date(my_date, &test_date));
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, bacapp_same_time(my_time, &test_time));
ct_test(pTest, bacapp_same_date(my_date, &test_date));
}
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 */
+356
View File
@@ -0,0 +1,356 @@
/*####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 "device.h"
#include "datalink.h"
#include "handlers.h"
#include "address.h"
#include "bacaddr.h"
#if (MAX_TSM_TRANSACTIONS)
/* Transaction State Machine */
/* 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];
/* 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 = 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 = 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;
}
/* 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)
{
static uint8_t current_invokeID = 1; /* incremented... */
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_invokeID);
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_invokeID;
TSM_List[index].state = TSM_STATE_IDLE;
TSM_List[index].RequestTimer = Device_APDU_Timeout();
/* update for the next call or check */
current_invokeID++;
/* skip zero - we treat that internally as invalid or no free */
if (current_invokeID == 0) {
current_invokeID = 1;
}
}
} else {
/* found! This invokeID is already used */
/* try next one */
current_invokeID++;
/* skip zero - we treat that internally as invalid or no free */
if (current_invokeID == 0) {
current_invokeID = 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) {
/* assign the transaction */
TSM_List[index].state = TSM_STATE_AWAIT_CONFIRMATION;
TSM_List[index].RetryCount = Device_Number_Of_APDU_Retries();
/* start the timer */
TSM_List[index].RequestTimer = Device_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 = 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 */
int bytes_sent = 0;
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;
/* timeout. retry? */
if (TSM_List[i].RequestTimer == 0) {
TSM_List[i].RetryCount--;
TSM_List[i].RequestTimer = Device_APDU_Timeout();
if (TSM_List[i].RetryCount) {
bytes_sent = 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;
}
}
}
}
}
/* 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 */
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 the invoke ID has failed get 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 */
+36
View File
@@ -0,0 +1,36 @@
/*####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"
char *BACnet_Version = BACNET_VERSION_TEXT;
+248
View File
@@ -0,0 +1,248 @@
/*####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"
/* 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->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,
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 */
int 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->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->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.object_name == data->object_name);
/* Object ID */
if (data->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.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.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.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 */
+182
View File
@@ -0,0 +1,182 @@
/*####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"
/* 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)
{
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 -1;
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
if (decoded_value <= BACNET_MAX_INSTANCE) {
if (pLow_limit)
*pLow_limit = decoded_value;
}
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
if (tag_number != 1)
return -1;
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
if (decoded_value <= BACNET_MAX_INSTANCE) {
if (pHigh_limit)
*pHigh_limit = decoded_value;
}
}
return 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 -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_IS)
return -1;
/* optional limits - must be used as a pair */
if (apdu_len > 2) {
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 = -1;
int32_t test_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 != -1);
ct_test(pTest, test_low_limit == low_limit);
ct_test(pTest, test_high_limit == high_limit);
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 != -1);
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 */
+372
View File
@@ -0,0 +1,372 @@
/*####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 "device.h"
#include "wp.h"
/* encode service */
int wp_encode_apdu(uint8_t * apdu,
uint8_t invoke_id, BACNET_WRITE_PROPERTY_DATA * data)
{
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, Device_Max_APDU_Length_Accepted());
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_WRITE_PROPERTY; /* service choice */
apdu_len = 4;
len = encode_context_object_id(&apdu[apdu_len], 0,
data->object_type, data->object_instance);
apdu_len += len;
len = encode_context_enumerated(&apdu[apdu_len], 1,
data->object_property);
apdu_len += len;
/* optional array index; ALL is -1 which is assumed when missing */
if (data->array_index != BACNET_ARRAY_ALL) {
len = encode_context_unsigned(&apdu[apdu_len], 2,
data->array_index);
apdu_len += len;
}
/* propertyValue */
len = encode_opening_tag(&apdu[apdu_len], 3);
apdu_len += len;
for (len = 0; len < data->application_data_len; len++) {
apdu[apdu_len + len] = data->application_data[len];
}
apdu_len += data->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 (data->priority != BACNET_NO_PRIORITY) {
len = encode_context_unsigned(&apdu[apdu_len], 4,
data->priority);
apdu_len += len;
}
}
return apdu_len;
}
/* 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 * data)
{
int len = 0;
int tag_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
int type = 0; /* for decoding */
int property = 0; /* for decoding */
uint32_t unsigned_value = 0;
int i = 0; /* loop counter */
/* check for value pointers */
if (apdu_len && data) {
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[len++], 0))
return -1;
len += decode_object_id(&apdu[len], &type, &data->object_instance);
data->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);
data->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);
data->array_index = unsigned_value;
} else
data->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 */
data->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 < data->application_data_len; i++) {
data->application_data[i] = apdu[len + i];
}
/* add on the data length */
len += data->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 */
data->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)) {
data->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 * 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, Device_Max_APDU_Length_Accepted()); */
*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, data);
}
return len;
}
void testWritePropertyTag(Test * pTest,
BACNET_APPLICATION_DATA_VALUE * value)
{
BACNET_WRITE_PROPERTY_DATA data = { 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;
data.application_data_len =
bacapp_encode_application_data(&data.application_data[0], value);
len = wp_encode_apdu(&apdu[0], invoke_id, &data);
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 == data.object_type);
ct_test(pTest, test_data.object_instance == data.object_instance);
ct_test(pTest, test_data.object_property == data.object_property);
ct_test(pTest, test_data.array_index == data.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
uint16_t Device_Max_APDU_Length_Accepted(void)
{
return MAX_APDU;
}
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 */