Ran the indent and comment script to standardize the source files.
This commit is contained in:
@@ -64,7 +64,7 @@ extern "C" {
|
||||
BACNET_PROPERTY_ID object_property,
|
||||
BACNET_APPLICATION_DATA_VALUE * object_value,
|
||||
uint8_t priority, int32_t array_index);
|
||||
|
||||
|
||||
/* returns the invoke ID for confirmed request, or 0 if failed */
|
||||
uint8_t Send_Reinitialize_Device_Request(uint32_t device_id,
|
||||
BACNET_REINITIALIZED_STATE state, char *password);
|
||||
|
||||
@@ -1,122 +1,122 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacerror.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "rp.h"
|
||||
/* demo objects */
|
||||
#include "device.h"
|
||||
|
||||
/* note: this is a minimal handler. See h_rp.c for another */
|
||||
|
||||
static uint8_t Temp_Buf[MAX_APDU] = { 0 };
|
||||
|
||||
void handler_read_property(uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_READ_PROPERTY_DATA data;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
bool send = false;
|
||||
bool error = false;
|
||||
int bytes_sent = 0;
|
||||
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
|
||||
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
len = rp_decode_service_request(service_request, service_len, &data);
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src,
|
||||
&my_address, &npdu_data);
|
||||
if (len < 0) {
|
||||
/* bad decoding - send an abort */
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_OTHER, true);
|
||||
} else if (service_data->segmented_message) {
|
||||
/* we don't support segmentation - send an abort */
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
} else {
|
||||
/* most cases will be error */
|
||||
error = true;
|
||||
switch (data.object_type) {
|
||||
case OBJECT_DEVICE:
|
||||
/* FIXME: probably need a length limitation sent with encode */
|
||||
if (data.object_instance == Device_Object_Instance_Number()) {
|
||||
len = Device_Encode_Property_APDU(&Temp_Buf[0],
|
||||
data.object_property,
|
||||
data.array_index, &error_class, &error_code);
|
||||
if (len >= 0) {
|
||||
/* encode the APDU portion of the packet */
|
||||
data.application_data = &Temp_Buf[0];
|
||||
data.application_data_len = len;
|
||||
/* FIXME: probably need a length limitation sent with encode */
|
||||
len =
|
||||
rp_ack_encode_apdu(&Handler_Transmit_Buffer
|
||||
[pdu_len], service_data->invoke_id, &data);
|
||||
error = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
switch (len) {
|
||||
/* BACnet APDU too small to fit data, so proper response is Abort */
|
||||
case -2:
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
break;
|
||||
case -1:
|
||||
default:
|
||||
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_READ_PROPERTY, error_class, error_code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pdu_len += len;
|
||||
bytes_sent = datalink_send_pdu(src, &npdu_data,
|
||||
&Handler_Transmit_Buffer[0], pdu_len);
|
||||
|
||||
return;
|
||||
}
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacerror.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "rp.h"
|
||||
/* demo objects */
|
||||
#include "device.h"
|
||||
|
||||
/* note: this is a minimal handler. See h_rp.c for another */
|
||||
|
||||
static uint8_t Temp_Buf[MAX_APDU] = { 0 };
|
||||
|
||||
void handler_read_property(uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_READ_PROPERTY_DATA data;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
bool send = false;
|
||||
bool error = false;
|
||||
int bytes_sent = 0;
|
||||
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
|
||||
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
len = rp_decode_service_request(service_request, service_len, &data);
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src,
|
||||
&my_address, &npdu_data);
|
||||
if (len < 0) {
|
||||
/* bad decoding - send an abort */
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_OTHER, true);
|
||||
} else if (service_data->segmented_message) {
|
||||
/* we don't support segmentation - send an abort */
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
} else {
|
||||
/* most cases will be error */
|
||||
error = true;
|
||||
switch (data.object_type) {
|
||||
case OBJECT_DEVICE:
|
||||
/* FIXME: probably need a length limitation sent with encode */
|
||||
if (data.object_instance == Device_Object_Instance_Number()) {
|
||||
len = Device_Encode_Property_APDU(&Temp_Buf[0],
|
||||
data.object_property,
|
||||
data.array_index, &error_class, &error_code);
|
||||
if (len >= 0) {
|
||||
/* encode the APDU portion of the packet */
|
||||
data.application_data = &Temp_Buf[0];
|
||||
data.application_data_len = len;
|
||||
/* FIXME: probably need a length limitation sent with encode */
|
||||
len =
|
||||
rp_ack_encode_apdu(&Handler_Transmit_Buffer
|
||||
[pdu_len], service_data->invoke_id, &data);
|
||||
error = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
switch (len) {
|
||||
/* BACnet APDU too small to fit data, so proper response is Abort */
|
||||
case -2:
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
break;
|
||||
case -1:
|
||||
default:
|
||||
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_READ_PROPERTY, error_class, error_code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pdu_len += len;
|
||||
bytes_sent = datalink_send_pdu(src, &npdu_data,
|
||||
&Handler_Transmit_Buffer[0], pdu_len);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ void handler_write_property(uint8_t * service_request,
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
#endif /* BACFILE */
|
||||
#endif /* BACFILE */
|
||||
default:
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
|
||||
@@ -48,9 +48,8 @@ uint8_t Send_Write_Property_Request_Data(uint32_t device_id,
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID object_property,
|
||||
uint8_t *application_data,
|
||||
int application_data_len,
|
||||
uint8_t priority, int32_t array_index)
|
||||
uint8_t * application_data,
|
||||
int application_data_len, uint8_t priority, int32_t array_index)
|
||||
{
|
||||
BACNET_ADDRESS dest;
|
||||
BACNET_ADDRESS my_address;
|
||||
@@ -83,7 +82,8 @@ uint8_t Send_Write_Property_Request_Data(uint32_t device_id,
|
||||
data.object_property = object_property;
|
||||
data.array_index = array_index;
|
||||
data.application_data_len = application_data_len;
|
||||
memcpy(&data.application_data[0],&application_data[0], application_data_len);
|
||||
memcpy(&data.application_data[0], &application_data[0],
|
||||
application_data_len);
|
||||
data.priority = priority;
|
||||
len = wp_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
invoke_id, &data);
|
||||
@@ -126,13 +126,12 @@ uint8_t Send_Write_Property_Request(uint32_t device_id,
|
||||
BACNET_APPLICATION_DATA_VALUE * object_value,
|
||||
uint8_t priority, int32_t array_index)
|
||||
{
|
||||
uint8_t application_data[MAX_APDU] = {0};
|
||||
uint8_t application_data[MAX_APDU] = { 0 };
|
||||
int apdu_len = 0, len = 0;
|
||||
|
||||
while (object_value) {
|
||||
len = bacapp_encode_data(
|
||||
&application_data[apdu_len],
|
||||
object_value);
|
||||
len = bacapp_encode_data(&application_data[apdu_len],
|
||||
object_value);
|
||||
if ((len + apdu_len) < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
@@ -140,14 +139,10 @@ uint8_t Send_Write_Property_Request(uint32_t device_id,
|
||||
}
|
||||
object_value = object_value->next;
|
||||
}
|
||||
|
||||
return Send_Write_Property_Request_Data(
|
||||
device_id,
|
||||
|
||||
return Send_Write_Property_Request_Data(device_id,
|
||||
object_type,
|
||||
object_instance,
|
||||
object_property,
|
||||
&application_data[0],
|
||||
apdu_len,
|
||||
priority,
|
||||
array_index);
|
||||
&application_data[0], apdu_len, priority, array_index);
|
||||
}
|
||||
|
||||
@@ -139,9 +139,9 @@ float Analog_Output_Present_Value(uint32_t object_instance)
|
||||
|
||||
unsigned Analog_Output_Present_Value_Priority(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = 0; /* instance to index conversion */
|
||||
unsigned i = 0; /* loop counter */
|
||||
unsigned priority = 0; /* return value */
|
||||
unsigned index = 0; /* instance to index conversion */
|
||||
unsigned i = 0; /* loop counter */
|
||||
unsigned priority = 0; /* return value */
|
||||
|
||||
Analog_Output_Init();
|
||||
index = Analog_Output_Instance_To_Index(object_instance);
|
||||
@@ -157,20 +157,18 @@ unsigned Analog_Output_Present_Value_Priority(uint32_t object_instance)
|
||||
return priority;
|
||||
}
|
||||
|
||||
bool Analog_Output_Present_Value_Set(
|
||||
uint32_t object_instance,
|
||||
float value,
|
||||
unsigned priority)
|
||||
bool Analog_Output_Present_Value_Set(uint32_t object_instance,
|
||||
float value, unsigned priority)
|
||||
{
|
||||
unsigned index = 0;
|
||||
bool status = false;
|
||||
|
||||
|
||||
index = Analog_Output_Instance_To_Index(object_instance);
|
||||
if (index < MAX_ANALOG_OUTPUTS) {
|
||||
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
|
||||
(priority != 6 /* reserved */ ) &&
|
||||
(value >= 0.0) && (value <= 100.0)) {
|
||||
Analog_Output_Level[index][priority] = (uint8_t)value;
|
||||
Analog_Output_Level[index][priority] = (uint8_t) value;
|
||||
/* Note: you could set the physical output here to the next
|
||||
highest priority, or to the relinquish default if no
|
||||
priorities are set.
|
||||
@@ -180,17 +178,16 @@ bool Analog_Output_Present_Value_Set(
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool Analog_Output_Present_Value_Relinquish(
|
||||
uint32_t object_instance,
|
||||
bool Analog_Output_Present_Value_Relinquish(uint32_t object_instance,
|
||||
int priority)
|
||||
{
|
||||
unsigned index = 0;
|
||||
bool status = false;
|
||||
|
||||
|
||||
index = Analog_Output_Instance_To_Index(object_instance);
|
||||
if (index < MAX_ANALOG_OUTPUTS) {
|
||||
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
|
||||
@@ -205,7 +202,7 @@ bool Analog_Output_Present_Value_Relinquish(
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -366,10 +363,9 @@ bool Analog_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data,
|
||||
/* Command priority 6 is reserved for use by Minimum On/Off
|
||||
algorithm and may not be used for other purposes in any
|
||||
object. */
|
||||
status = Analog_Output_Present_Value_Set(
|
||||
wp_data->object_instance,
|
||||
value.type.Real,
|
||||
wp_data->priority);
|
||||
status =
|
||||
Analog_Output_Present_Value_Set(wp_data->object_instance,
|
||||
value.type.Real, wp_data->priority);
|
||||
if (wp_data->priority == 6) {
|
||||
/* Command priority 6 is reserved for use by Minimum On/Off
|
||||
algorithm and may not be used for other purposes in any
|
||||
@@ -384,9 +380,9 @@ bool Analog_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data,
|
||||
level = AO_LEVEL_NULL;
|
||||
object_index =
|
||||
Analog_Output_Instance_To_Index(wp_data->object_instance);
|
||||
status = Analog_Output_Present_Value_Relinquish(
|
||||
wp_data->object_instance,
|
||||
wp_data->priority);
|
||||
status =
|
||||
Analog_Output_Present_Value_Relinquish(wp_data->
|
||||
object_instance, wp_data->priority);
|
||||
if (!status) {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
|
||||
@@ -40,15 +40,13 @@ extern "C" {
|
||||
uint32_t Analog_Output_Index_To_Instance(unsigned index);
|
||||
char *Analog_Output_Name(uint32_t object_instance);
|
||||
float Analog_Output_Present_Value(uint32_t object_instance);
|
||||
unsigned Analog_Output_Present_Value_Priority(uint32_t object_instance);
|
||||
bool Analog_Output_Present_Value_Set(
|
||||
uint32_t object_instance,
|
||||
float value,
|
||||
unsigned priority);
|
||||
bool Analog_Output_Present_Value_Relinquish(
|
||||
uint32_t object_instance,
|
||||
unsigned Analog_Output_Present_Value_Priority(uint32_t
|
||||
object_instance);
|
||||
bool Analog_Output_Present_Value_Set(uint32_t object_instance,
|
||||
float value, unsigned priority);
|
||||
bool Analog_Output_Present_Value_Relinquish(uint32_t object_instance,
|
||||
int priority);
|
||||
|
||||
|
||||
|
||||
int Analog_Output_Encode_Property_APDU(uint8_t * apdu,
|
||||
uint32_t object_instance,
|
||||
|
||||
+137
-128
@@ -143,7 +143,7 @@ void Load_Control_Init(void)
|
||||
Shed_Duration[i] = 0;
|
||||
Duty_Window[i] = 0;
|
||||
Load_Control_Enable[i] = true;
|
||||
Full_Duty_Baseline[i] = 1.500; /* kilowatts */
|
||||
Full_Duty_Baseline[i] = 1.500; /* kilowatts */
|
||||
for (j = 0; j < MAX_SHED_LEVELS; j++) {
|
||||
/* FIXME: fake data for lighting application */
|
||||
/* The array shall be ordered by increasing shed amount. */
|
||||
@@ -254,12 +254,11 @@ struct tm {
|
||||
timer = time(NULL);
|
||||
tblock = localtime(&timer);
|
||||
datetime_set_values(bdatetime,
|
||||
(uint16_t)tblock->tm_year,
|
||||
(uint8_t)tblock->tm_mon,
|
||||
(uint8_t)tblock->tm_mday,
|
||||
(uint8_t)tblock->tm_hour,
|
||||
(uint8_t)tblock->tm_min,
|
||||
(uint8_t)tblock->tm_sec, 0);
|
||||
(uint16_t) tblock->tm_year,
|
||||
(uint8_t) tblock->tm_mon,
|
||||
(uint8_t) tblock->tm_mday,
|
||||
(uint8_t) tblock->tm_hour,
|
||||
(uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec, 0);
|
||||
}
|
||||
|
||||
/* convert the shed level request into an Analog Output Present_Value */
|
||||
@@ -270,62 +269,68 @@ static float Requested_Shed_Level_Value(int object_index)
|
||||
float requested_level = 0.0;
|
||||
|
||||
switch (Requested_Shed_Level[object_index].type) {
|
||||
case BACNET_SHED_TYPE_PERCENT:
|
||||
requested_level = (float)Requested_Shed_Level[object_index].value.percent;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_AMOUNT:
|
||||
/* Assumptions: wattage is linear with analog output level */
|
||||
requested_level = Full_Duty_Baseline[object_index] - Requested_Shed_Level[object_index].value.amount;
|
||||
requested_level /= Full_Duty_Baseline[object_index];
|
||||
requested_level *= 100.0;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_LEVEL:
|
||||
default:
|
||||
for (i = 0; i < MAX_SHED_LEVELS; i++) {
|
||||
if (Shed_Levels[object_index][i] <= Requested_Shed_Level[object_index].value.level)
|
||||
shed_level_index = i;
|
||||
}
|
||||
requested_level = Shed_Level_Values[shed_level_index];
|
||||
break;
|
||||
case BACNET_SHED_TYPE_PERCENT:
|
||||
requested_level =
|
||||
(float) Requested_Shed_Level[object_index].value.percent;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_AMOUNT:
|
||||
/* Assumptions: wattage is linear with analog output level */
|
||||
requested_level =
|
||||
Full_Duty_Baseline[object_index] -
|
||||
Requested_Shed_Level[object_index].value.amount;
|
||||
requested_level /= Full_Duty_Baseline[object_index];
|
||||
requested_level *= 100.0;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_LEVEL:
|
||||
default:
|
||||
for (i = 0; i < MAX_SHED_LEVELS; i++) {
|
||||
if (Shed_Levels[object_index][i] <=
|
||||
Requested_Shed_Level[object_index].value.level)
|
||||
shed_level_index = i;
|
||||
}
|
||||
requested_level = Shed_Level_Values[shed_level_index];
|
||||
break;
|
||||
}
|
||||
|
||||
return requested_level;
|
||||
return requested_level;
|
||||
}
|
||||
|
||||
static void Shed_Level_Copy(BACNET_SHED_LEVEL *dest, BACNET_SHED_LEVEL *src)
|
||||
static void Shed_Level_Copy(BACNET_SHED_LEVEL * dest,
|
||||
BACNET_SHED_LEVEL * src)
|
||||
{
|
||||
if (dest && src) {
|
||||
dest->type = src->type;
|
||||
switch (src->type) {
|
||||
case BACNET_SHED_TYPE_PERCENT:
|
||||
dest->value.percent = src->value.percent;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_AMOUNT:
|
||||
dest->value.amount = src->value.amount;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_LEVEL:
|
||||
default:
|
||||
dest->value.level = src->value.level;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_PERCENT:
|
||||
dest->value.percent = src->value.percent;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_AMOUNT:
|
||||
dest->value.amount = src->value.amount;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_LEVEL:
|
||||
default:
|
||||
dest->value.level = src->value.level;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Shed_Level_Default_Set(BACNET_SHED_LEVEL *dest, BACNET_SHED_LEVEL_TYPE type)
|
||||
static void Shed_Level_Default_Set(BACNET_SHED_LEVEL * dest,
|
||||
BACNET_SHED_LEVEL_TYPE type)
|
||||
{
|
||||
if (dest) {
|
||||
dest->type = type;
|
||||
switch (type) {
|
||||
case BACNET_SHED_TYPE_PERCENT:
|
||||
dest->value.percent = 100;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_AMOUNT:
|
||||
dest->value.amount = 0.0;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_LEVEL:
|
||||
default:
|
||||
dest->value.level = 0;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_PERCENT:
|
||||
dest->value.percent = 100;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_AMOUNT:
|
||||
dest->value.amount = 0.0;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_LEVEL:
|
||||
default:
|
||||
dest->value.level = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -337,7 +342,7 @@ static bool Able_To_Meet_Shed_Request(int object_index)
|
||||
unsigned priority = 0;
|
||||
bool status = false;
|
||||
int object_instance = 0;
|
||||
|
||||
|
||||
/* This demo is going to use the Analog Outputs as their Load */
|
||||
object_instance = object_index;
|
||||
priority = Analog_Output_Present_Value_Priority(object_instance);
|
||||
@@ -350,8 +355,8 @@ static bool Able_To_Meet_Shed_Request(int object_index)
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
typedef enum load_control_state {
|
||||
@@ -367,15 +372,15 @@ static LOAD_CONTROL_STATE Load_Control_State_Previously[MAX_LOAD_CONTROLS];
|
||||
static void Print_Load_Control_State(int object_index)
|
||||
{
|
||||
char *Load_Control_State_Text[MAX_LOAD_CONTROLS] = {
|
||||
"SHED_INACTIVE",
|
||||
"SHED_REQUEST_PENDING",
|
||||
"SHED_NON_COMPLIANT",
|
||||
"SHED_COMPLIANT"
|
||||
"SHED_INACTIVE",
|
||||
"SHED_REQUEST_PENDING",
|
||||
"SHED_NON_COMPLIANT",
|
||||
"SHED_COMPLIANT"
|
||||
};
|
||||
|
||||
if (object_index < MAX_LOAD_CONTROLS) {
|
||||
if (Load_Control_State[object_index] < MAX_LOAD_CONTROL_STATE) {
|
||||
printf("Load Control[%d]=%s\n",object_index,
|
||||
printf("Load Control[%d]=%s\n", object_index,
|
||||
Load_Control_State_Text[Load_Control_State[object_index]]);
|
||||
}
|
||||
}
|
||||
@@ -387,42 +392,43 @@ void Load_Control_State_Machine(int object_index)
|
||||
int diff = 0; /* used for datetime comparison */
|
||||
|
||||
switch (Load_Control_State[object_index]) {
|
||||
case SHED_REQUEST_PENDING:
|
||||
if (Load_Control_Request_Written[object_index]) {
|
||||
Load_Control_Request_Written[object_index] = false;
|
||||
/* request to cancel using default values? */
|
||||
switch (Requested_Shed_Level[object_index].type) {
|
||||
case BACNET_SHED_TYPE_PERCENT:
|
||||
if (Requested_Shed_Level[object_index].value.percent ==
|
||||
DEFAULT_VALUE_PERCENT)
|
||||
Load_Control_State[object_index] = SHED_INACTIVE;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_AMOUNT:
|
||||
if (Requested_Shed_Level[object_index].value.amount ==
|
||||
DEFAULT_VALUE_AMOUNT)
|
||||
Load_Control_State[object_index] = SHED_INACTIVE;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_LEVEL:
|
||||
default:
|
||||
if (Requested_Shed_Level[object_index].value.level ==
|
||||
DEFAULT_VALUE_LEVEL)
|
||||
Load_Control_State[object_index] = SHED_INACTIVE;
|
||||
break;
|
||||
}
|
||||
if (Load_Control_State[object_index] == SHED_INACTIVE) {
|
||||
printf("Load Control[%d]:Requested Shed Level=Default\n",object_index);
|
||||
break;
|
||||
case SHED_REQUEST_PENDING:
|
||||
if (Load_Control_Request_Written[object_index]) {
|
||||
Load_Control_Request_Written[object_index] = false;
|
||||
/* request to cancel using default values? */
|
||||
switch (Requested_Shed_Level[object_index].type) {
|
||||
case BACNET_SHED_TYPE_PERCENT:
|
||||
if (Requested_Shed_Level[object_index].value.percent ==
|
||||
DEFAULT_VALUE_PERCENT)
|
||||
Load_Control_State[object_index] = SHED_INACTIVE;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_AMOUNT:
|
||||
if (Requested_Shed_Level[object_index].value.amount ==
|
||||
DEFAULT_VALUE_AMOUNT)
|
||||
Load_Control_State[object_index] = SHED_INACTIVE;
|
||||
break;
|
||||
case BACNET_SHED_TYPE_LEVEL:
|
||||
default:
|
||||
if (Requested_Shed_Level[object_index].value.level ==
|
||||
DEFAULT_VALUE_LEVEL)
|
||||
Load_Control_State[object_index] = SHED_INACTIVE;
|
||||
break;
|
||||
}
|
||||
if (Load_Control_State[object_index] == SHED_INACTIVE) {
|
||||
printf("Load Control[%d]:Requested Shed Level=Default\n",
|
||||
object_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Start_Time_Property_Written[object_index]) {
|
||||
Start_Time_Property_Written[object_index] = false;
|
||||
/* request to cancel using wildcards in start time? */
|
||||
if (datetime_wildcard(&Start_Time[object_index])) {
|
||||
Start_Time_Property_Written[object_index] = false;
|
||||
/* request to cancel using wildcards in start time? */
|
||||
if (datetime_wildcard(&Start_Time[object_index])) {
|
||||
Load_Control_State[object_index] = SHED_INACTIVE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* cancel because current time is after start time + duration? */
|
||||
/* cancel because current time is after start time + duration? */
|
||||
Update_Current_Time(&Current_Time);
|
||||
datetime_copy(&End_Time[object_index], &Start_Time[object_index]);
|
||||
datetime_add_minutes(&End_Time[object_index],
|
||||
@@ -431,7 +437,9 @@ void Load_Control_State_Machine(int object_index)
|
||||
if (diff < 0) {
|
||||
/* CancelShed */
|
||||
/* FIXME: stop shedding! i.e. relinquish */
|
||||
printf("Load Control[%d]:Current Time is after Start Time + Duration\n",object_index);
|
||||
printf
|
||||
("Load Control[%d]:Current Time is after Start Time + Duration\n",
|
||||
object_index);
|
||||
Load_Control_State[object_index] = SHED_INACTIVE;
|
||||
break;
|
||||
}
|
||||
@@ -439,33 +447,28 @@ void Load_Control_State_Machine(int object_index)
|
||||
if (diff < 0) {
|
||||
/* current time prior to start time */
|
||||
/* ReconfigurePending */
|
||||
Shed_Level_Copy(
|
||||
&Expected_Shed_Level[object_index],
|
||||
Shed_Level_Copy(&Expected_Shed_Level[object_index],
|
||||
&Requested_Shed_Level[object_index]);
|
||||
Shed_Level_Default_Set(
|
||||
&Actual_Shed_Level[object_index],
|
||||
Shed_Level_Default_Set(&Actual_Shed_Level[object_index],
|
||||
Requested_Shed_Level[object_index].type);
|
||||
} else if (diff > 0) {
|
||||
/* current time after to start time */
|
||||
printf("Load Control[%d]:Current Time is after Start Time\n",object_index);
|
||||
printf("Load Control[%d]:Current Time is after Start Time\n",
|
||||
object_index);
|
||||
/* AbleToMeetShed */
|
||||
if (Able_To_Meet_Shed_Request(object_index)) {
|
||||
Shed_Level_Copy(
|
||||
&Expected_Shed_Level[object_index],
|
||||
Shed_Level_Copy(&Expected_Shed_Level[object_index],
|
||||
&Requested_Shed_Level[object_index]);
|
||||
Analog_Output_Present_Value_Set(object_index,
|
||||
Analog_Output_Present_Value_Set(object_index,
|
||||
Requested_Shed_Level_Value(object_index), 4);
|
||||
Shed_Level_Copy(
|
||||
&Actual_Shed_Level[object_index],
|
||||
Shed_Level_Copy(&Actual_Shed_Level[object_index],
|
||||
&Requested_Shed_Level[object_index]);
|
||||
Load_Control_State[object_index] = SHED_COMPLIANT;
|
||||
} else {
|
||||
/* CannotMeetShed */
|
||||
Shed_Level_Default_Set(
|
||||
&Expected_Shed_Level[object_index],
|
||||
Shed_Level_Default_Set(&Expected_Shed_Level[object_index],
|
||||
Requested_Shed_Level[object_index].type);
|
||||
Shed_Level_Default_Set(
|
||||
&Actual_Shed_Level[object_index],
|
||||
Shed_Level_Default_Set(&Actual_Shed_Level[object_index],
|
||||
Requested_Shed_Level[object_index].type);
|
||||
Load_Control_State[object_index] = SHED_NON_COMPLIANT;
|
||||
}
|
||||
@@ -479,14 +482,17 @@ void Load_Control_State_Machine(int object_index)
|
||||
diff = datetime_compare(&End_Time[object_index], &Current_Time);
|
||||
if (diff < 0) {
|
||||
/* FinishedUnsuccessfulShed */
|
||||
printf("Load Control[%d]:Current Time is after Start Time + Duration\n",object_index);
|
||||
printf
|
||||
("Load Control[%d]:Current Time is after Start Time + Duration\n",
|
||||
object_index);
|
||||
Load_Control_State[object_index] = SHED_INACTIVE;
|
||||
break;
|
||||
}
|
||||
if (Load_Control_Request_Written[object_index] ||
|
||||
if (Load_Control_Request_Written[object_index] ||
|
||||
Start_Time_Property_Written[object_index]) {
|
||||
/* UnsuccessfulShedReconfigured */
|
||||
printf("Load Control[%d]:Control Property written\n",object_index);
|
||||
printf("Load Control[%d]:Control Property written\n",
|
||||
object_index);
|
||||
Load_Control_Request_Written[object_index] = false;
|
||||
Start_Time_Property_Written[object_index] = false;
|
||||
Load_Control_State[object_index] = SHED_REQUEST_PENDING;
|
||||
@@ -494,14 +500,13 @@ void Load_Control_State_Machine(int object_index)
|
||||
}
|
||||
if (Able_To_Meet_Shed_Request(object_index)) {
|
||||
/* CanNowComplyWithShed */
|
||||
printf("Load Control[%d]:Able to meet Shed Request\n",object_index);
|
||||
Shed_Level_Copy(
|
||||
&Expected_Shed_Level[object_index],
|
||||
printf("Load Control[%d]:Able to meet Shed Request\n",
|
||||
object_index);
|
||||
Shed_Level_Copy(&Expected_Shed_Level[object_index],
|
||||
&Requested_Shed_Level[object_index]);
|
||||
Analog_Output_Present_Value_Set(object_index,
|
||||
Analog_Output_Present_Value_Set(object_index,
|
||||
Requested_Shed_Level_Value(object_index), 4);
|
||||
Shed_Level_Copy(
|
||||
&Actual_Shed_Level[object_index],
|
||||
Shed_Level_Copy(&Actual_Shed_Level[object_index],
|
||||
&Requested_Shed_Level[object_index]);
|
||||
Load_Control_State[object_index] = SHED_COMPLIANT;
|
||||
}
|
||||
@@ -514,15 +519,18 @@ void Load_Control_State_Machine(int object_index)
|
||||
diff = datetime_compare(&End_Time[object_index], &Current_Time);
|
||||
if (diff < 0) {
|
||||
/* FinishedSuccessfulShed */
|
||||
printf("Load Control[%d]:Current Time is after Start Time + Duration\n",object_index);
|
||||
printf
|
||||
("Load Control[%d]:Current Time is after Start Time + Duration\n",
|
||||
object_index);
|
||||
datetime_wildcard_set(&Start_Time[i]);
|
||||
Load_Control_State[object_index] = SHED_INACTIVE;
|
||||
break;
|
||||
}
|
||||
if (Load_Control_Request_Written[object_index] ||
|
||||
Start_Time_Property_Written[object_index]) {
|
||||
if (Load_Control_Request_Written[object_index] ||
|
||||
Start_Time_Property_Written[object_index]) {
|
||||
/* UnsuccessfulShedReconfigured */
|
||||
printf("Load Control[%d]:Control Property written\n",object_index);
|
||||
printf("Load Control[%d]:Control Property written\n",
|
||||
object_index);
|
||||
Load_Control_Request_Written[object_index] = false;
|
||||
Start_Time_Property_Written[object_index] = false;
|
||||
Load_Control_State[object_index] = SHED_REQUEST_PENDING;
|
||||
@@ -530,12 +538,11 @@ void Load_Control_State_Machine(int object_index)
|
||||
}
|
||||
if (!Able_To_Meet_Shed_Request(object_index)) {
|
||||
/* CanNoLongerComplyWithShed */
|
||||
printf("Load Control[%d]:Not able to meet Shed Request\n",object_index);
|
||||
Shed_Level_Default_Set(
|
||||
&Expected_Shed_Level[object_index],
|
||||
printf("Load Control[%d]:Not able to meet Shed Request\n",
|
||||
object_index);
|
||||
Shed_Level_Default_Set(&Expected_Shed_Level[object_index],
|
||||
Requested_Shed_Level[object_index].type);
|
||||
Shed_Level_Default_Set(
|
||||
&Actual_Shed_Level[object_index],
|
||||
Shed_Level_Default_Set(&Actual_Shed_Level[object_index],
|
||||
Requested_Shed_Level[object_index].type);
|
||||
Load_Control_State[object_index] = SHED_NON_COMPLIANT;
|
||||
}
|
||||
@@ -543,13 +550,11 @@ void Load_Control_State_Machine(int object_index)
|
||||
case SHED_INACTIVE:
|
||||
default:
|
||||
if (Start_Time_Property_Written[object_index]) {
|
||||
printf("Load Control[%d]:Start Time written\n",object_index);
|
||||
printf("Load Control[%d]:Start Time written\n", object_index);
|
||||
Start_Time_Property_Written[object_index] = false;
|
||||
Shed_Level_Copy(
|
||||
&Expected_Shed_Level[object_index],
|
||||
Shed_Level_Copy(&Expected_Shed_Level[object_index],
|
||||
&Requested_Shed_Level[object_index]);
|
||||
Shed_Level_Default_Set(
|
||||
&Actual_Shed_Level[object_index],
|
||||
Shed_Level_Default_Set(&Actual_Shed_Level[object_index],
|
||||
Requested_Shed_Level[object_index].type);
|
||||
Load_Control_State[object_index] = SHED_REQUEST_PENDING;
|
||||
}
|
||||
@@ -829,19 +834,22 @@ bool Load_Control_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data,
|
||||
&value, PROP_REQUESTED_SHED_LEVEL);
|
||||
if (value.tag == 0) {
|
||||
/* percent - Unsigned */
|
||||
Requested_Shed_Level[object_index].type = BACNET_SHED_TYPE_PERCENT;
|
||||
Requested_Shed_Level[object_index].type =
|
||||
BACNET_SHED_TYPE_PERCENT;
|
||||
Requested_Shed_Level[object_index].value.percent =
|
||||
value.type.Unsigned_Int;
|
||||
status = true;
|
||||
} else if (value.tag == 1) {
|
||||
/* level - Unsigned */
|
||||
Requested_Shed_Level[object_index].type = BACNET_SHED_TYPE_LEVEL;
|
||||
Requested_Shed_Level[object_index].type =
|
||||
BACNET_SHED_TYPE_LEVEL;
|
||||
Requested_Shed_Level[object_index].value.level =
|
||||
value.type.Unsigned_Int;
|
||||
status = true;
|
||||
} else if (value.tag == 2) {
|
||||
/* amount - REAL */
|
||||
Requested_Shed_Level[object_index].type = BACNET_SHED_TYPE_AMOUNT;
|
||||
Requested_Shed_Level[object_index].type =
|
||||
BACNET_SHED_TYPE_AMOUNT;
|
||||
Requested_Shed_Level[object_index].value.amount =
|
||||
value.type.Real;
|
||||
status = true;
|
||||
@@ -966,8 +974,9 @@ void testLoadControlStateMachine(Test * pTest)
|
||||
}
|
||||
}
|
||||
|
||||
/**/
|
||||
status = Load_Control_Write_Property(&wp_data, &error_class, &error_code);
|
||||
/**/
|
||||
status =
|
||||
Load_Control_Write_Property(&wp_data, &error_class, &error_code);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
void Load_Control_State_Machine_Handler(void);
|
||||
|
||||
bool Load_Control_Valid_Instance(uint32_t object_instance);
|
||||
|
||||
+376
-375
@@ -1,375 +1,376 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006-2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* command line tool that sends a BACnet service, and displays the response */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h> /* toupper */
|
||||
#include "bactext.h"
|
||||
#include "iam.h"
|
||||
#include "arf.h"
|
||||
#include "tsm.h"
|
||||
#include "address.h"
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "net.h"
|
||||
#include "datalink.h"
|
||||
#include "whois.h"
|
||||
/* some demo stuff needed */
|
||||
#include "filename.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "txbuf.h"
|
||||
|
||||
/* buffer used for receive */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/* global variables used in this file */
|
||||
static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE;
|
||||
static uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE;
|
||||
static BACNET_OBJECT_TYPE Target_Object_Type = OBJECT_ANALOG_INPUT;
|
||||
static BACNET_PROPERTY_ID Target_Object_Property = PROP_ACKED_TRANSITIONS;
|
||||
/* array index value or BACNET_ARRAY_ALL */
|
||||
static int32_t Target_Object_Property_Index = BACNET_ARRAY_ALL;
|
||||
#define MAX_PROPERTY_VALUES 16
|
||||
static BACNET_APPLICATION_DATA_VALUE Target_Object_Property_Value[MAX_PROPERTY_VALUES];
|
||||
|
||||
/* 0 if not set, 1..16 if set */
|
||||
static uint8_t Target_Object_Property_Priority = 0;
|
||||
|
||||
static BACNET_ADDRESS Target_Address;
|
||||
static bool Error_Detected = false;
|
||||
|
||||
static void MyErrorHandler(BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
/* FIXME: verify src and invoke id */
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("\r\nBACnet Error!\r\n");
|
||||
printf("Error Class: %s\r\n", bactext_error_class_name(error_class));
|
||||
printf("Error Code: %s\r\n", bactext_error_code_name(error_code));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
void MyAbortHandler(BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id, uint8_t abort_reason, bool server)
|
||||
{
|
||||
/* FIXME: verify src and invoke id */
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
(void) server;
|
||||
printf("\r\nBACnet Abort!\r\n");
|
||||
printf("Abort Reason: %s\r\n",
|
||||
bactext_abort_reason_name(abort_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
void MyRejectHandler(BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id, uint8_t reject_reason)
|
||||
{
|
||||
/* FIXME: verify src and invoke id */
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("\r\nBACnet Reject!\r\n");
|
||||
printf("Reject Reason: %s\r\n",
|
||||
bactext_reject_reason_name(reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
void MyWritePropertySimpleAckHandler(BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id)
|
||||
{
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("\r\nWriteProperty Acknowledged!\r\n");
|
||||
}
|
||||
|
||||
static void Init_Service_Handlers(void)
|
||||
{
|
||||
/* we need to handle who-is
|
||||
to support dynamic device binding to us */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
|
||||
handler_who_is);
|
||||
/* handle i-am to support binding to other devices */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM,
|
||||
handler_i_am_bind);
|
||||
/* set the handler for all the services we don't implement
|
||||
It is required to send the proper reject message... */
|
||||
apdu_set_unrecognized_service_handler_handler
|
||||
(handler_unrecognized_service);
|
||||
/* we must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
handler_read_property);
|
||||
/* handle the ack coming back */
|
||||
apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||
MyWritePropertySimpleAckHandler);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||
MyErrorHandler);
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
BACNET_ADDRESS src = { 0 }; /* address where message came from */
|
||||
uint16_t pdu_len = 0;
|
||||
unsigned timeout = 100; /* milliseconds */
|
||||
unsigned max_apdu = 0;
|
||||
time_t elapsed_seconds = 0;
|
||||
time_t last_seconds = 0;
|
||||
time_t current_seconds = 0;
|
||||
time_t timeout_seconds = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool found = false;
|
||||
char *value_string = NULL;
|
||||
bool status = false;
|
||||
int args_remaining = 0, tag_value_arg = 0, i = 0;
|
||||
BACNET_APPLICATION_TAG property_tag;
|
||||
uint8_t context_tag = 0;
|
||||
|
||||
if (argc < 9) {
|
||||
/* note: priority 16 and 0 should produce the same end results... */
|
||||
printf("Usage: %s device-instance object-type object-instance "
|
||||
"property priority index tag value [tag value...]\r\n",
|
||||
filename_remove_path(argv[0]));
|
||||
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
|
||||
printf("device-instance:\r\n"
|
||||
"BACnet Device Object Instance number that you are trying to\r\n"
|
||||
"communicate to. This number will be used to try and bind with\r\n"
|
||||
"the device using Who-Is and I-Am services. For example, if you were\r\n"
|
||||
"writing to Device Object 123, the device-instance would be 123.\r\n"
|
||||
"\r\n"
|
||||
"object-type:\r\n"
|
||||
"The object type is the integer value of the enumeration\r\n"
|
||||
"BACNET_OBJECT_TYPE in bacenum.h. It is the object that you are\r\n"
|
||||
"writing to. For example if you were writing to Analog Output 2, \r\n"
|
||||
"the object-type would be 1.\r\n"
|
||||
"\r\n"
|
||||
"object-instance:\r\n"
|
||||
"This is the object instance number of the object that you are \r\n"
|
||||
"writing to. For example, if you were writing to Analog Output 2, \r\n"
|
||||
"the object-instance would be 2.\r\n"
|
||||
"\r\n"
|
||||
"property:\r\n"
|
||||
"The property is an integer value of the enumeration \r\n"
|
||||
"BACNET_PROPERTY_ID in bacenum.h. It is the property you are \r\n"
|
||||
"writing to. For example, if you were writing to the Present Value\r\n"
|
||||
"property, you would use 85 as the property.\r\n"
|
||||
"\r\n"
|
||||
"priority:\r\n"
|
||||
"This parameter is used for setting the priority of the\r\n"
|
||||
"write. If Priority 0 is given, no priority is sent. The BACnet \r\n"
|
||||
"standard states that the value is written at the lowest \r\n"
|
||||
"priority (16) if the object property supports priorities\r\n"
|
||||
"when no priority is sent.\r\n"
|
||||
"\r\n"
|
||||
"index\r\n"
|
||||
"This integer parameter is the index number of an array.\r\n"
|
||||
"If the property is an array, individual elements can be written\r\n"
|
||||
"to if supported. If this parameter is -1, the index is ignored.\r\n"
|
||||
"\r\n"
|
||||
"tag:\r\n"
|
||||
"Tag is the integer value of the enumeration BACNET_APPLICATION_TAG \r\n"
|
||||
"in bacenum.h. It is the data type of the value that you are\r\n"
|
||||
"writing. For example, if you were writing a REAL value, you would \r\n"
|
||||
"use a tag of 4.\r\n"
|
||||
"Context tags are created using two tags in a row. The context tag\r\n"
|
||||
"is preceded by a C. Ctag tag. C2 4 creates a context 2 tagged REAL.\r\n"
|
||||
"\r\n"
|
||||
"value:\r\n"
|
||||
"The value is an ASCII representation of some type of data that you\r\n"
|
||||
"are writing. It is encoded using the tag information provided. For\r\n"
|
||||
"example, if you were writing a REAL value of 100.0, you would use \r\n"
|
||||
"100.0 as the value.\r\n"
|
||||
"\r\n"
|
||||
"Here is a brief overview of BACnet property and tags:\r\n"
|
||||
"Certain properties are expected to be written with certain \r\n"
|
||||
"application tags, so you probably need to know which ones to use\r\n"
|
||||
"with each property of each object. It is almost safe to say that\r\n"
|
||||
"given a property and an object and a table, the tag could be looked\r\n"
|
||||
"up automatically. There may be a few exceptions to this, such as\r\n"
|
||||
"the Any property type in the schedule object and the Present Value\r\n"
|
||||
"accepting REAL, BOOLEAN, NULL, etc. Perhaps it would be simpler for\r\n"
|
||||
"the demo to use this kind of table - but I also wanted to be able\r\n"
|
||||
"to do negative testing by passing the wrong tag and have the server\r\n"
|
||||
"return a reject message.\r\n"
|
||||
"\r\n"
|
||||
"Example:\r\n"
|
||||
"If you want send a 100 to the Present-Value in the Analog Output\r\n"
|
||||
"at priority 16, you could send the following command:\r\n"
|
||||
"%s 123 1 0 85 4 100\r\n"
|
||||
"You could also send a relinquish command:\r\n"
|
||||
"%s 123 1 0 85 0 0\r\n",
|
||||
filename_remove_path(argv[0]),
|
||||
filename_remove_path(argv[0]));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameters */
|
||||
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
|
||||
Target_Object_Type = strtol(argv[2], NULL, 0);
|
||||
Target_Object_Instance = strtol(argv[3], NULL, 0);
|
||||
Target_Object_Property = strtol(argv[4], NULL, 0);
|
||||
Target_Object_Property_Priority = strtol(argv[5], NULL, 0);
|
||||
Target_Object_Property_Index = strtol(argv[6], NULL, 0);
|
||||
if (Target_Object_Property_Index == -1)
|
||||
Target_Object_Property_Index = BACNET_ARRAY_ALL;
|
||||
if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr, "device-instance=%u - it must be less than %u\r\n",
|
||||
Target_Device_Object_Instance, BACNET_MAX_INSTANCE + 1);
|
||||
return 1;
|
||||
}
|
||||
if (Target_Object_Type > MAX_BACNET_OBJECT_TYPE) {
|
||||
fprintf(stderr, "object-type=%u - it must be less than %u\r\n",
|
||||
Target_Object_Type, MAX_BACNET_OBJECT_TYPE + 1);
|
||||
return 1;
|
||||
}
|
||||
if (Target_Object_Instance > BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr, "object-instance=%u - it must be less than %u\r\n",
|
||||
Target_Object_Instance, BACNET_MAX_INSTANCE + 1);
|
||||
return 1;
|
||||
}
|
||||
if (Target_Object_Property > MAX_BACNET_PROPERTY_ID) {
|
||||
fprintf(stderr, "object-type=%u - it must be less than %u\r\n",
|
||||
Target_Object_Property, MAX_BACNET_PROPERTY_ID + 1);
|
||||
return 1;
|
||||
}
|
||||
args_remaining = (argc - 7);
|
||||
for (i = 0; i < MAX_PROPERTY_VALUES; i++) {
|
||||
tag_value_arg = 7+(i*2);
|
||||
/* special case for context tagged values */
|
||||
if (toupper(argv[tag_value_arg][0]) == 'C') {
|
||||
context_tag = strtol(&argv[tag_value_arg][1], NULL, 0);
|
||||
tag_value_arg++;
|
||||
args_remaining--;
|
||||
Target_Object_Property_Value[i].context_tag = context_tag;
|
||||
Target_Object_Property_Value[i].context_specific = true;
|
||||
} else {
|
||||
Target_Object_Property_Value[i].context_specific = false;
|
||||
}
|
||||
property_tag = strtol(argv[tag_value_arg], NULL, 0);
|
||||
value_string = argv[tag_value_arg+1];
|
||||
args_remaining -= 2;
|
||||
/* printf("tag[%d]=%u value[%d]=%s\r\n",
|
||||
i, property_tag, i, value_string); */
|
||||
if (property_tag >= MAX_BACNET_APPLICATION_TAG) {
|
||||
fprintf(stderr, "tag=%u - it must be less than %u\r\n",
|
||||
property_tag, MAX_BACNET_APPLICATION_TAG);
|
||||
return 1;
|
||||
}
|
||||
status = bacapp_parse_application_data(property_tag,
|
||||
value_string, &Target_Object_Property_Value[i]);
|
||||
if (!status) {
|
||||
/* FIXME: show the expected entry format for the tag */
|
||||
fprintf(stderr, "unable to parse the tag value\r\n");
|
||||
return 1;
|
||||
}
|
||||
Target_Object_Property_Value[i].next = NULL;
|
||||
if (i > 0) {
|
||||
Target_Object_Property_Value[i-1].next =
|
||||
&Target_Object_Property_Value[i];
|
||||
}
|
||||
if (args_remaining <= 0)
|
||||
break;
|
||||
}
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
address_init();
|
||||
Init_Service_Handlers();
|
||||
/* configure standard BACnet/IP port */
|
||||
bip_set_interface("eth0"); /* for linux */
|
||||
bip_set_port(0xBAC0);
|
||||
if (!bip_init())
|
||||
return 1;
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = (Device_APDU_Timeout() / 1000) *
|
||||
Device_Number_Of_APDU_Retries();
|
||||
/* try to bind with the device */
|
||||
Send_WhoIs(Target_Device_Object_Instance,
|
||||
Target_Device_Object_Instance);
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* increment timer - exit if timed out */
|
||||
current_seconds = time(NULL);
|
||||
|
||||
/* returns 0 bytes on timeout */
|
||||
pdu_len = bip_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
|
||||
|
||||
/* process */
|
||||
if (pdu_len) {
|
||||
npdu_handler(&src, &Rx_Buf[0], pdu_len);
|
||||
}
|
||||
/* at least one second has passed */
|
||||
if (current_seconds != last_seconds)
|
||||
tsm_timer_milliseconds(((current_seconds -
|
||||
last_seconds) * 1000));
|
||||
if (Error_Detected)
|
||||
break;
|
||||
/* wait until the device is bound, or timeout and quit */
|
||||
found = address_bind_request(Target_Device_Object_Instance,
|
||||
&max_apdu, &Target_Address);
|
||||
if (found) {
|
||||
if (invoke_id == 0) {
|
||||
invoke_id =
|
||||
Send_Write_Property_Request(
|
||||
Target_Device_Object_Instance, Target_Object_Type,
|
||||
Target_Object_Instance, Target_Object_Property,
|
||||
&Target_Object_Property_Value[0],
|
||||
Target_Object_Property_Priority,
|
||||
Target_Object_Property_Index);
|
||||
} else if (tsm_invoke_id_free(invoke_id))
|
||||
break;
|
||||
else if (tsm_invoke_id_failed(invoke_id)) {
|
||||
fprintf(stderr, "\rError: TSM Timeout!\r\n");
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
/* try again or abort? */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* increment timer - exit if timed out */
|
||||
elapsed_seconds += (current_seconds - last_seconds);
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
printf("\rError: APDU Timeout!\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006-2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* command line tool that sends a BACnet service, and displays the response */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h> /* toupper */
|
||||
#include "bactext.h"
|
||||
#include "iam.h"
|
||||
#include "arf.h"
|
||||
#include "tsm.h"
|
||||
#include "address.h"
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "net.h"
|
||||
#include "datalink.h"
|
||||
#include "whois.h"
|
||||
/* some demo stuff needed */
|
||||
#include "filename.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "txbuf.h"
|
||||
|
||||
/* buffer used for receive */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/* global variables used in this file */
|
||||
static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE;
|
||||
static uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE;
|
||||
static BACNET_OBJECT_TYPE Target_Object_Type = OBJECT_ANALOG_INPUT;
|
||||
static BACNET_PROPERTY_ID Target_Object_Property = PROP_ACKED_TRANSITIONS;
|
||||
/* array index value or BACNET_ARRAY_ALL */
|
||||
static int32_t Target_Object_Property_Index = BACNET_ARRAY_ALL;
|
||||
#define MAX_PROPERTY_VALUES 16
|
||||
static BACNET_APPLICATION_DATA_VALUE
|
||||
Target_Object_Property_Value[MAX_PROPERTY_VALUES];
|
||||
|
||||
/* 0 if not set, 1..16 if set */
|
||||
static uint8_t Target_Object_Property_Priority = 0;
|
||||
|
||||
static BACNET_ADDRESS Target_Address;
|
||||
static bool Error_Detected = false;
|
||||
|
||||
static void MyErrorHandler(BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
/* FIXME: verify src and invoke id */
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("\r\nBACnet Error!\r\n");
|
||||
printf("Error Class: %s\r\n", bactext_error_class_name(error_class));
|
||||
printf("Error Code: %s\r\n", bactext_error_code_name(error_code));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
void MyAbortHandler(BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id, uint8_t abort_reason, bool server)
|
||||
{
|
||||
/* FIXME: verify src and invoke id */
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
(void) server;
|
||||
printf("\r\nBACnet Abort!\r\n");
|
||||
printf("Abort Reason: %s\r\n",
|
||||
bactext_abort_reason_name(abort_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
void MyRejectHandler(BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id, uint8_t reject_reason)
|
||||
{
|
||||
/* FIXME: verify src and invoke id */
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("\r\nBACnet Reject!\r\n");
|
||||
printf("Reject Reason: %s\r\n",
|
||||
bactext_reject_reason_name(reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
void MyWritePropertySimpleAckHandler(BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id)
|
||||
{
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("\r\nWriteProperty Acknowledged!\r\n");
|
||||
}
|
||||
|
||||
static void Init_Service_Handlers(void)
|
||||
{
|
||||
/* we need to handle who-is
|
||||
to support dynamic device binding to us */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
|
||||
handler_who_is);
|
||||
/* handle i-am to support binding to other devices */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM,
|
||||
handler_i_am_bind);
|
||||
/* set the handler for all the services we don't implement
|
||||
It is required to send the proper reject message... */
|
||||
apdu_set_unrecognized_service_handler_handler
|
||||
(handler_unrecognized_service);
|
||||
/* we must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
handler_read_property);
|
||||
/* handle the ack coming back */
|
||||
apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||
MyWritePropertySimpleAckHandler);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||
MyErrorHandler);
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
BACNET_ADDRESS src = { 0 }; /* address where message came from */
|
||||
uint16_t pdu_len = 0;
|
||||
unsigned timeout = 100; /* milliseconds */
|
||||
unsigned max_apdu = 0;
|
||||
time_t elapsed_seconds = 0;
|
||||
time_t last_seconds = 0;
|
||||
time_t current_seconds = 0;
|
||||
time_t timeout_seconds = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool found = false;
|
||||
char *value_string = NULL;
|
||||
bool status = false;
|
||||
int args_remaining = 0, tag_value_arg = 0, i = 0;
|
||||
BACNET_APPLICATION_TAG property_tag;
|
||||
uint8_t context_tag = 0;
|
||||
|
||||
if (argc < 9) {
|
||||
/* note: priority 16 and 0 should produce the same end results... */
|
||||
printf("Usage: %s device-instance object-type object-instance "
|
||||
"property priority index tag value [tag value...]\r\n",
|
||||
filename_remove_path(argv[0]));
|
||||
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
|
||||
printf("device-instance:\r\n"
|
||||
"BACnet Device Object Instance number that you are trying to\r\n"
|
||||
"communicate to. This number will be used to try and bind with\r\n"
|
||||
"the device using Who-Is and I-Am services. For example, if you were\r\n"
|
||||
"writing to Device Object 123, the device-instance would be 123.\r\n"
|
||||
"\r\n"
|
||||
"object-type:\r\n"
|
||||
"The object type is the integer value of the enumeration\r\n"
|
||||
"BACNET_OBJECT_TYPE in bacenum.h. It is the object that you are\r\n"
|
||||
"writing to. For example if you were writing to Analog Output 2, \r\n"
|
||||
"the object-type would be 1.\r\n"
|
||||
"\r\n"
|
||||
"object-instance:\r\n"
|
||||
"This is the object instance number of the object that you are \r\n"
|
||||
"writing to. For example, if you were writing to Analog Output 2, \r\n"
|
||||
"the object-instance would be 2.\r\n"
|
||||
"\r\n"
|
||||
"property:\r\n"
|
||||
"The property is an integer value of the enumeration \r\n"
|
||||
"BACNET_PROPERTY_ID in bacenum.h. It is the property you are \r\n"
|
||||
"writing to. For example, if you were writing to the Present Value\r\n"
|
||||
"property, you would use 85 as the property.\r\n"
|
||||
"\r\n"
|
||||
"priority:\r\n"
|
||||
"This parameter is used for setting the priority of the\r\n"
|
||||
"write. If Priority 0 is given, no priority is sent. The BACnet \r\n"
|
||||
"standard states that the value is written at the lowest \r\n"
|
||||
"priority (16) if the object property supports priorities\r\n"
|
||||
"when no priority is sent.\r\n"
|
||||
"\r\n"
|
||||
"index\r\n"
|
||||
"This integer parameter is the index number of an array.\r\n"
|
||||
"If the property is an array, individual elements can be written\r\n"
|
||||
"to if supported. If this parameter is -1, the index is ignored.\r\n"
|
||||
"\r\n"
|
||||
"tag:\r\n"
|
||||
"Tag is the integer value of the enumeration BACNET_APPLICATION_TAG \r\n"
|
||||
"in bacenum.h. It is the data type of the value that you are\r\n"
|
||||
"writing. For example, if you were writing a REAL value, you would \r\n"
|
||||
"use a tag of 4.\r\n"
|
||||
"Context tags are created using two tags in a row. The context tag\r\n"
|
||||
"is preceded by a C. Ctag tag. C2 4 creates a context 2 tagged REAL.\r\n"
|
||||
"\r\n"
|
||||
"value:\r\n"
|
||||
"The value is an ASCII representation of some type of data that you\r\n"
|
||||
"are writing. It is encoded using the tag information provided. For\r\n"
|
||||
"example, if you were writing a REAL value of 100.0, you would use \r\n"
|
||||
"100.0 as the value.\r\n"
|
||||
"\r\n"
|
||||
"Here is a brief overview of BACnet property and tags:\r\n"
|
||||
"Certain properties are expected to be written with certain \r\n"
|
||||
"application tags, so you probably need to know which ones to use\r\n"
|
||||
"with each property of each object. It is almost safe to say that\r\n"
|
||||
"given a property and an object and a table, the tag could be looked\r\n"
|
||||
"up automatically. There may be a few exceptions to this, such as\r\n"
|
||||
"the Any property type in the schedule object and the Present Value\r\n"
|
||||
"accepting REAL, BOOLEAN, NULL, etc. Perhaps it would be simpler for\r\n"
|
||||
"the demo to use this kind of table - but I also wanted to be able\r\n"
|
||||
"to do negative testing by passing the wrong tag and have the server\r\n"
|
||||
"return a reject message.\r\n"
|
||||
"\r\n"
|
||||
"Example:\r\n"
|
||||
"If you want send a 100 to the Present-Value in the Analog Output\r\n"
|
||||
"at priority 16, you could send the following command:\r\n"
|
||||
"%s 123 1 0 85 4 100\r\n"
|
||||
"You could also send a relinquish command:\r\n"
|
||||
"%s 123 1 0 85 0 0\r\n",
|
||||
filename_remove_path(argv[0]),
|
||||
filename_remove_path(argv[0]));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameters */
|
||||
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
|
||||
Target_Object_Type = strtol(argv[2], NULL, 0);
|
||||
Target_Object_Instance = strtol(argv[3], NULL, 0);
|
||||
Target_Object_Property = strtol(argv[4], NULL, 0);
|
||||
Target_Object_Property_Priority = strtol(argv[5], NULL, 0);
|
||||
Target_Object_Property_Index = strtol(argv[6], NULL, 0);
|
||||
if (Target_Object_Property_Index == -1)
|
||||
Target_Object_Property_Index = BACNET_ARRAY_ALL;
|
||||
if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr, "device-instance=%u - it must be less than %u\r\n",
|
||||
Target_Device_Object_Instance, BACNET_MAX_INSTANCE + 1);
|
||||
return 1;
|
||||
}
|
||||
if (Target_Object_Type > MAX_BACNET_OBJECT_TYPE) {
|
||||
fprintf(stderr, "object-type=%u - it must be less than %u\r\n",
|
||||
Target_Object_Type, MAX_BACNET_OBJECT_TYPE + 1);
|
||||
return 1;
|
||||
}
|
||||
if (Target_Object_Instance > BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr, "object-instance=%u - it must be less than %u\r\n",
|
||||
Target_Object_Instance, BACNET_MAX_INSTANCE + 1);
|
||||
return 1;
|
||||
}
|
||||
if (Target_Object_Property > MAX_BACNET_PROPERTY_ID) {
|
||||
fprintf(stderr, "object-type=%u - it must be less than %u\r\n",
|
||||
Target_Object_Property, MAX_BACNET_PROPERTY_ID + 1);
|
||||
return 1;
|
||||
}
|
||||
args_remaining = (argc - 7);
|
||||
for (i = 0; i < MAX_PROPERTY_VALUES; i++) {
|
||||
tag_value_arg = 7 + (i * 2);
|
||||
/* special case for context tagged values */
|
||||
if (toupper(argv[tag_value_arg][0]) == 'C') {
|
||||
context_tag = strtol(&argv[tag_value_arg][1], NULL, 0);
|
||||
tag_value_arg++;
|
||||
args_remaining--;
|
||||
Target_Object_Property_Value[i].context_tag = context_tag;
|
||||
Target_Object_Property_Value[i].context_specific = true;
|
||||
} else {
|
||||
Target_Object_Property_Value[i].context_specific = false;
|
||||
}
|
||||
property_tag = strtol(argv[tag_value_arg], NULL, 0);
|
||||
value_string = argv[tag_value_arg + 1];
|
||||
args_remaining -= 2;
|
||||
/* printf("tag[%d]=%u value[%d]=%s\r\n",
|
||||
i, property_tag, i, value_string); */
|
||||
if (property_tag >= MAX_BACNET_APPLICATION_TAG) {
|
||||
fprintf(stderr, "tag=%u - it must be less than %u\r\n",
|
||||
property_tag, MAX_BACNET_APPLICATION_TAG);
|
||||
return 1;
|
||||
}
|
||||
status = bacapp_parse_application_data(property_tag,
|
||||
value_string, &Target_Object_Property_Value[i]);
|
||||
if (!status) {
|
||||
/* FIXME: show the expected entry format for the tag */
|
||||
fprintf(stderr, "unable to parse the tag value\r\n");
|
||||
return 1;
|
||||
}
|
||||
Target_Object_Property_Value[i].next = NULL;
|
||||
if (i > 0) {
|
||||
Target_Object_Property_Value[i - 1].next =
|
||||
&Target_Object_Property_Value[i];
|
||||
}
|
||||
if (args_remaining <= 0)
|
||||
break;
|
||||
}
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
address_init();
|
||||
Init_Service_Handlers();
|
||||
/* configure standard BACnet/IP port */
|
||||
bip_set_interface("eth0"); /* for linux */
|
||||
bip_set_port(0xBAC0);
|
||||
if (!bip_init())
|
||||
return 1;
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = (Device_APDU_Timeout() / 1000) *
|
||||
Device_Number_Of_APDU_Retries();
|
||||
/* try to bind with the device */
|
||||
Send_WhoIs(Target_Device_Object_Instance,
|
||||
Target_Device_Object_Instance);
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* increment timer - exit if timed out */
|
||||
current_seconds = time(NULL);
|
||||
|
||||
/* returns 0 bytes on timeout */
|
||||
pdu_len = bip_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
|
||||
|
||||
/* process */
|
||||
if (pdu_len) {
|
||||
npdu_handler(&src, &Rx_Buf[0], pdu_len);
|
||||
}
|
||||
/* at least one second has passed */
|
||||
if (current_seconds != last_seconds)
|
||||
tsm_timer_milliseconds(((current_seconds -
|
||||
last_seconds) * 1000));
|
||||
if (Error_Detected)
|
||||
break;
|
||||
/* wait until the device is bound, or timeout and quit */
|
||||
found = address_bind_request(Target_Device_Object_Instance,
|
||||
&max_apdu, &Target_Address);
|
||||
if (found) {
|
||||
if (invoke_id == 0) {
|
||||
invoke_id =
|
||||
Send_Write_Property_Request
|
||||
(Target_Device_Object_Instance, Target_Object_Type,
|
||||
Target_Object_Instance, Target_Object_Property,
|
||||
&Target_Object_Property_Value[0],
|
||||
Target_Object_Property_Priority,
|
||||
Target_Object_Property_Index);
|
||||
} else if (tsm_invoke_id_free(invoke_id))
|
||||
break;
|
||||
else if (tsm_invoke_id_failed(invoke_id)) {
|
||||
fprintf(stderr, "\rError: TSM Timeout!\r\n");
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
/* try again or abort? */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* increment timer - exit if timed out */
|
||||
elapsed_seconds += (current_seconds - last_seconds);
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
printf("\rError: APDU Timeout!\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user