adjust root folder

This commit is contained in:
Steve Karg
2019-10-08 23:47:53 -05:00
parent b6fc50ddea
commit a42e8f507c
1258 changed files with 26 additions and 214 deletions
+514
View File
@@ -0,0 +1,514 @@
/**************************************************************************
*
* Copyright (C) 2009 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.
*
*********************************************************************/
/* environment variables used for the command line tools */
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "config.h"
#include "bacdef.h"
#include "apdu.h"
#include "datalink.h"
#include "handlers.h"
#include "dlenv.h"
#include "tsm.h"
#if (BACNET_PROTOCOL_REVISION >= 17)
#include "netport.h"
#endif
/** @file dlenv.c Initialize the DataLink configuration. */
#if defined(BACDL_BIP)
/* timer used to renew Foreign Device Registration */
static uint16_t BBMD_Timer_Seconds;
/* BBMD variables */
static long bbmd_timetolive_seconds = 60000;
static long bbmd_port = 0xBAC0;
static long bbmd_address = 0;
static long bbmd_mask = 0xFFFFFFFF;
static int bbmd_result = 0;
static BBMD_TABLE_ENTRY BBMD_Table_Entry;
/* Simple setters for BBMD registration variables. */
/** Sets the IPv4 address for BBMD registration.
* If not set here or provided by Environment variables,
* no BBMD registration will occur.
* @param address - IPv4 address (long) of BBMD to register with,
* in network byte order.
*/
void dlenv_bbmd_address_set(
long address)
{
bbmd_address = address;
}
/** Set the port for BBMD registration.
* Default if not set is 0xBAC0.
* @param port - The port number (provided in network byte order).
*/
void dlenv_bbmd_port_set(
int port)
{
bbmd_port = port;
}
/** Set the Lease Time (Time-to-Live) for BBMD registration.
* Default if not set is 60000 (1000 minutes).
* @param ttl_secs - The Lease Time, in seconds.
*/
void dlenv_bbmd_ttl_set(
int ttl_secs)
{
bbmd_timetolive_seconds = ttl_secs;
}
/** Get the result of the last attempt to register with the indicated BBMD.
* If we sent a foreign registration request, then see if we've received
* a NAK in our BVLC handler.
*
* @return Positive number (of bytes sent) if registration was successful,
* 0 if no registration request was made, or
* -1 if registration attempt failed.
*/
int dlenv_bbmd_result(
void)
{
if ((bbmd_result > 0) &&
(bvlc_get_last_result() == BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK))
return -1;
/* Else, show our send: */
return bbmd_result;
}
#endif
/** Register as a Foreign Device with the designated BBMD.
* @ingroup DataLink
* The BBMD's address, port, and lease time must be provided by
* internal variables or Environment variables.
* If no address for the BBMD is provided, no BBMD registration will occur.
*
* The Environment Variables depend on define of BACDL_BIP:
* - BACNET_BBMD_PORT - 0..65534, defaults to 47808
* - BACNET_BBMD_TIMETOLIVE - 0..65535 seconds, defaults to 60000
* - BACNET_BBMD_ADDRESS - dotted IPv4 address
* @return Positive number (of bytes sent) on success,
* 0 if no registration request is sent, or
* -1 if registration fails.
*/
int dlenv_register_as_foreign_device(
void)
{
int retval = 0;
#if defined(BACDL_BIP)
char *pEnv = NULL;
unsigned a[4] = {0};
char bbmd_env[32] = "";
unsigned entry_number = 0;
int c;
pEnv = getenv("BACNET_BBMD_PORT");
if (pEnv) {
bbmd_port = strtol(pEnv, NULL, 0);
if (bbmd_port > 0xFFFF) {
bbmd_port = 0xBAC0;
}
}
pEnv = getenv("BACNET_BBMD_TIMETOLIVE");
if (pEnv) {
bbmd_timetolive_seconds = strtol(pEnv, NULL, 0);
if (bbmd_timetolive_seconds > 0xFFFF) {
bbmd_timetolive_seconds = 0xFFFF;
}
}
pEnv = getenv("BACNET_BBMD_ADDRESS");
if (pEnv) {
bbmd_address = bip_getaddrbyname(pEnv);
}
if (bbmd_address) {
struct in_addr addr;
addr.s_addr = bbmd_address;
fprintf(stderr, "Registering with BBMD at %s:%ld for %ld seconds\n",
inet_ntoa(addr), bbmd_port, bbmd_timetolive_seconds);
retval =
bvlc_register_with_bbmd(bbmd_address, htons((uint16_t) bbmd_port),
(uint16_t) bbmd_timetolive_seconds);
if (retval < 0)
fprintf(stderr, "FAILED to Register with BBMD at %s \n",
inet_ntoa(addr));
BBMD_Timer_Seconds = (uint16_t) bbmd_timetolive_seconds;
} else {
for (entry_number = 1; entry_number <= 128; entry_number++) {
sprintf(bbmd_env,"BACNET_BDT_ADDR_%u", entry_number);
pEnv = getenv(bbmd_env);
if (pEnv) {
bbmd_address = bip_getaddrbyname(pEnv);
} else if (entry_number == 1) {
/* BDT 1 is self (note: can be overridden) */
bbmd_address = bip_get_addr();
}
if (bbmd_address) {
bbmd_port = 0xBAC0;
sprintf(bbmd_env,"BACNET_BDT_PORT_%u", entry_number);
pEnv = getenv(bbmd_env);
if (pEnv) {
bbmd_port = strtol(pEnv, NULL, 0);
if (bbmd_port > 0xFFFF) {
bbmd_port = 0xBAC0;
}
} else if (entry_number == 1) {
/* BDT 1 is self (note: can be overridden) */
bbmd_port = bip_get_port();
}
bbmd_mask = 0xFFFFFFFF;
sprintf(bbmd_env,"BACNET_BDT_MASK_%u", entry_number);
pEnv = getenv(bbmd_env);
if (pEnv) {
c = sscanf(pEnv, "%3u.%3u.%3u.%3u",
&a[0],&a[1],&a[2],&a[3]);
if (c == 4) {
bbmd_mask =
((a[0]&0xFF)<<24)|((a[1]&0xFF)<<16)|
((a[2]&0xFF)<<8)|(a[3]&0xFF);
}
}
BBMD_Table_Entry.valid = true;
BBMD_Table_Entry.dest_address.s_addr = bbmd_address;
BBMD_Table_Entry.dest_port = bbmd_port;
BBMD_Table_Entry.broadcast_mask.s_addr = bbmd_mask;
bvlc_add_bdt_entry_local(&BBMD_Table_Entry);
}
}
}
bbmd_result = retval;
#endif
return retval;
}
#if (BACNET_PROTOCOL_REVISION >= 17)
#if defined(BACDL_BIP)
/**
* Datalink network port object settings
*/
static void dlenv_network_port_init(void)
{
uint32_t instance = 1;
uint32_t address = 0;
uint32_t broadcast = 0;
uint32_t test_broadcast = 0;
uint32_t mask = 0;
uint16_t port = 0;
uint8_t mac[4+2] = {0};
uint8_t prefix = 0;
Network_Port_Object_Instance_Number_Set(0, instance);
Network_Port_Name_Set(instance, "BACnet/IP Port");
Network_Port_Type_Set(instance, PORT_TYPE_BIP);
port = bip_get_port();
Network_Port_BIP_Port_Set(instance, port);
address = bip_get_addr();
memcpy(&mac[0], &address, 4);
memcpy(&mac[4], &port, 2);
Network_Port_MAC_Address_Set(instance, &mac[0], 6);
broadcast = bip_get_broadcast_addr();
for (prefix = 0; prefix < 32; prefix++) {
mask = htonl((0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF);
test_broadcast = (address & mask) | (~mask);
if (test_broadcast == broadcast) {
break;
}
}
Network_Port_IP_Subnet_Prefix_Set(instance, prefix);
Network_Port_Link_Speed_Set(instance, 0.0);
/* common NP data */
Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED);
Network_Port_Out_Of_Service_Set(instance, false);
Network_Port_Quality_Set(instance, PORT_QUALITY_UNKNOWN);
Network_Port_APDU_Length_Set(instance, MAX_APDU);
Network_Port_Network_Number_Set(instance, 0);
/* last thing - clear pending changes - we don't want to set these
since they are already set */
Network_Port_Changes_Pending_Set(instance, false);
}
#elif defined(BACDL_MSTP)
/**
* Datalink network port object settings
*/
static void dlenv_network_port_init(void)
{
uint32_t instance = 1;
uint8_t mac[1] = {0};
Network_Port_Object_Instance_Number_Set(0, instance);
Network_Port_Name_Set(instance, "MS/TP Port");
Network_Port_Type_Set(instance, PORT_TYPE_MSTP);
Network_Port_MSTP_Max_Master_Set(instance, dlmstp_max_master());
Network_Port_MSTP_Max_Info_Frames_Set(instance, dlmstp_max_info_frames());
Network_Port_Link_Speed_Set(instance, dlmstp_baud_rate());
mac[0] = dlmstp_mac_address();
Network_Port_MAC_Address_Set(instance, &mac[0], 1);
/* common NP data */
Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED);
Network_Port_Out_Of_Service_Set(instance, false);
Network_Port_Quality_Set(instance, PORT_QUALITY_UNKNOWN);
Network_Port_APDU_Length_Set(instance, MAX_APDU);
Network_Port_Network_Number_Set(instance, 0);
/* last thing - clear pending changes - we don't want to set these
since they are already set */
Network_Port_Changes_Pending_Set(instance, false);
}
#elif defined(BACDL_BIP6)
/**
* Datalink network port object settings
*/
static void dlenv_network_port_init(void)
{
uint32_t instance = 1;
uint8_t prefix = 0;
BACNET_ADDRESS addr = {0};
BACNET_IP6_ADDRESS addr6 = {0};
Network_Port_Object_Instance_Number_Set(0, instance);
Network_Port_Name_Set(instance, "BACnet/IPv6 Port");
Network_Port_Type_Set(instance, PORT_TYPE_BIP6);
Network_Port_BIP6_Port_Set(instance, bip6_get_port());
bip6_get_my_address(&addr);
Network_Port_MAC_Address_Set(instance, &addr.mac[0], addr.mac_len);
bip6_get_addr(&addr6);
Network_Port_IPv6_Address_Set(instance, &addr6.address[0]);
bip6_get_broadcast_addr(&addr6);
Network_Port_IPv6_Multicast_Address_Set(instance, &addr6.address[0]);
Network_Port_IPv6_Subnet_Prefix_Set(instance, prefix);
Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED);
Network_Port_Link_Speed_Set(instance, 0.0);
Network_Port_Out_Of_Service_Set(instance, false);
Network_Port_Quality_Set(instance, PORT_QUALITY_UNKNOWN);
Network_Port_APDU_Length_Set(instance, MAX_APDU);
Network_Port_Network_Number_Set(instance, 0);
/* last thing - clear pending changes - we don't want to set these
since they are already set */
Network_Port_Changes_Pending_Set(instance, false);
}
#endif
#else
/**
* Datalink network port object settings
*/
static void dlenv_network_port_init(void)
{
/* do nothing */
}
#endif
/** Datalink maintenance timer
* @ingroup DataLink
*
* Call this function to renew our Foreign Device Registration
* @param elapsed_seconds Number of seconds that have elapsed since last called.
*/
void dlenv_maintenance_timer(
uint16_t elapsed_seconds)
{
#if defined(BACDL_BIP)
if (BBMD_Timer_Seconds) {
if (BBMD_Timer_Seconds <= elapsed_seconds) {
BBMD_Timer_Seconds = 0;
} else {
BBMD_Timer_Seconds -= elapsed_seconds;
}
if (BBMD_Timer_Seconds == 0) {
(void) dlenv_register_as_foreign_device();
/* If that failed (negative), maybe just a network issue.
* If nothing happened (0), may be un/misconfigured.
* Set up to try again later in all cases. */
BBMD_Timer_Seconds = (uint16_t) bbmd_timetolive_seconds;
}
}
#endif
}
/** Initialize the DataLink configuration from Environment variables,
* or else to defaults.
* @ingroup DataLink
* The items configured depend on which BACDL_ the code is built for,
* eg, BACDL_BIP.
*
* For most items, checks first for an environment variable, and, if
* found, uses that to set the item's value. Otherwise, will set
* to a default value.
*
* The Environment Variables, by BACDL_ type, are:
* - BACDL_ALL: (the general-purpose solution)
* - BACNET_DATALINK to set which BACDL_ type we are using.
* - (Any):
* - BACNET_APDU_TIMEOUT - set this value in milliseconds to change
* the APDU timeout. APDU Timeout is how much time a client
* waits for a response from a BACnet device.
* - BACNET_APDU_RETRIES - indicate the maximum number of times that
* an APDU shall be retransmitted.
* - BACNET_IFACE - set this value to dotted IP address (Windows) of
* the interface (see ipconfig command on Windows) for which you
* want to bind. On Linux, set this to the /dev interface
* (i.e. eth0, arc0). Default is eth0 on Linux, and the default
* interface on Windows. Hence, if there is only a single network
* interface on Windows, the applications will choose it, and this
* setting will not be needed.
* - BACDL_BIP: (BACnet/IP)
* - BACNET_IP_PORT - UDP/IP port number (0..65534) used for BACnet/IP
* communications. Default is 47808 (0xBAC0).
* - BACNET_BBMD_PORT - UDP/IP port number (0..65534) used for Foreign
* Device Registration. Defaults to 47808 (0xBAC0).
* - BACNET_BBMD_TIMETOLIVE - number of seconds used in Foreign Device
* Registration (0..65535). Defaults to 60000 seconds.
* - BACNET_BBMD_ADDRESS - dotted IPv4 address of the BBMD or Foreign
* Device Registrar.
* - BACNET_BDT_ADDR_1 - dotted IPv4 address of the BBMD table entry 1..128
* - BACNET_BDT_PORT_1 - UDP port of the BBMD table entry 1..128 (optional)
* - BACNET_BDT_MASK_1 - dotted IPv4 mask of the BBMD table
* entry 1..128 (optional)
* - BACNET_IP_NAT_ADDR - dotted IPv4 address of the public facing router
* - BACDL_MSTP: (BACnet MS/TP)
* - BACNET_MAX_INFO_FRAMES
* - BACNET_MAX_MASTER
* - BACNET_MSTP_BAUD
* - BACNET_MSTP_MAC
* - BACDL_BIP6: (BACnet/IPv6)
* - BACNET_BIP6_PORT - UDP/IP port number (0..65534) used for BACnet/IPv6
* communications. Default is 47808 (0xBAC0).
* - BACNET_BIP6_BROADCAST - FF05::BAC0 or FF02::BAC0 or ...
*/
void dlenv_init(
void)
{
char *pEnv = NULL;
#if defined(BACDL_ALL)
pEnv = getenv("BACNET_DATALINK");
if (pEnv) {
datalink_set(pEnv);
} else {
datalink_set(NULL);
}
#endif
#if defined(BACDL_BIP6)
BACNET_IP6_ADDRESS addr;
pEnv = getenv("BACNET_BIP6_BROADCAST");
if (pEnv) {
bvlc6_address_set(&addr,
(uint16_t) strtol(pEnv, NULL, 0), 0, 0, 0, 0, 0, 0,
BIP6_MULTICAST_GROUP_ID);
bip6_set_broadcast_addr(&addr);
} else {
bvlc6_address_set(&addr,
BIP6_MULTICAST_SITE_LOCAL, 0, 0, 0, 0, 0, 0,
BIP6_MULTICAST_GROUP_ID);
bip6_set_broadcast_addr(&addr);
}
pEnv = getenv("BACNET_BIP6_PORT");
if (pEnv) {
bip6_set_port((uint16_t) strtol(pEnv, NULL, 0));
} else {
bip6_set_port(0xBAC0);
}
#endif
#if defined(BACDL_BIP)
#if defined(BIP_DEBUG)
BIP_Debug = true;
#endif
pEnv = getenv("BACNET_IP_PORT");
if (pEnv) {
bip_set_port(htons((uint16_t) strtol(pEnv, NULL, 0)));
} else {
/* BIP_Port is statically initialized to 0xBAC0,
* so if it is different, then it was programmatically altered,
* and we shouldn't just stomp on it here.
* Unless it is set below 1024, since:
* "The range for well-known ports managed by the IANA is 0-1023."
*/
if (ntohs(bip_get_port()) < 1024)
bip_set_port(htons(0xBAC0));
}
pEnv = getenv("BACNET_IP_NAT_ADDR");
if (pEnv) {
struct in_addr nat_addr;
nat_addr.s_addr = bip_getaddrbyname(pEnv);
if (nat_addr.s_addr) {
bvlc_set_global_address_for_nat(&nat_addr);
}
}
#elif defined(BACDL_MSTP)
pEnv = getenv("BACNET_MAX_INFO_FRAMES");
if (pEnv) {
dlmstp_set_max_info_frames(strtol(pEnv, NULL, 0));
} else {
dlmstp_set_max_info_frames(1);
}
pEnv = getenv("BACNET_MAX_MASTER");
if (pEnv) {
dlmstp_set_max_master(strtol(pEnv, NULL, 0));
} else {
dlmstp_set_max_master(127);
}
pEnv = getenv("BACNET_MSTP_BAUD");
if (pEnv) {
dlmstp_set_baud_rate(strtol(pEnv, NULL, 0));
} else {
dlmstp_set_baud_rate(38400);
}
pEnv = getenv("BACNET_MSTP_MAC");
if (pEnv) {
dlmstp_set_mac_address(strtol(pEnv, NULL, 0));
} else {
dlmstp_set_mac_address(127);
}
#endif
pEnv = getenv("BACNET_APDU_TIMEOUT");
if (pEnv) {
apdu_timeout_set((uint16_t) strtol(pEnv, NULL, 0));
} else {
#if defined(BACDL_MSTP)
apdu_timeout_set(60000);
#endif
}
pEnv = getenv("BACNET_APDU_RETRIES");
if (pEnv) {
apdu_retries_set((uint8_t) strtol(pEnv, NULL, 0));
}
/* === Initialize the Datalink Here === */
if (!datalink_init(getenv("BACNET_IFACE"))) {
exit(1);
}
#if (MAX_TSM_TRANSACTIONS)
pEnv = getenv("BACNET_INVOKE_ID");
if (pEnv) {
tsm_invokeID_set((uint8_t) strtol(pEnv, NULL, 0));
}
#endif
dlenv_network_port_init();
dlenv_register_as_foreign_device();
}
+205
View File
@@ -0,0 +1,205 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "bacerror.h"
#include "bactext.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "alarm_ack.h"
#include "handlers.h"
#include "device.h"
/** @file h_alarm_ack.c Handles Alarm Acknowledgment. */
static alarm_ack_function Alarm_Ack[MAX_BACNET_OBJECT_TYPE];
void handler_alarm_ack_set(
BACNET_OBJECT_TYPE object_type,
alarm_ack_function pFunction)
{
if (object_type < MAX_BACNET_OBJECT_TYPE) {
Alarm_Ack[object_type] = pFunction;
}
}
/** Handler for an Alarm/Event Acknowledgement.
* @ingroup ALMACK
* This handler will be invoked by apdu_handler() if it has been enabled
* by a call to apdu_set_confirmed_handler().
* This handler builds a response packet, which is
* - an Abort if
* - the message is segmented
* - if decoding fails
* - Otherwise, sends a simple ACK
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
void handler_alarm_ack(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
int ack_result = 0;
BACNET_ADDRESS my_address;
BACNET_NPDU_DATA npdu_data;
BACNET_ALARM_ACK_DATA data;
BACNET_ERROR_CODE error_code;
/* 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 (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);
#if PRINT_ENABLED
fprintf(stderr, "Alarm Ack: Segmented message. Sending Abort!\n");
#endif
goto AA_ABORT;
}
len =
alarm_ack_decode_service_request(service_request, service_len, &data);
#if PRINT_ENABLED
if (len <= 0)
fprintf(stderr, "Alarm Ack: Unable to decode Request!\n");
#endif
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);
#if PRINT_ENABLED
fprintf(stderr, "Alarm Ack: Bad Encoding. Sending Abort!\n");
#endif
goto AA_ABORT;
}
#if PRINT_ENABLED
fprintf(stderr,
"Alarm Ack Operation: Received acknowledge for object id (%d, %lu) from %s for process id %lu \n",
data.eventObjectIdentifier.type,
(unsigned long) data.eventObjectIdentifier.instance,
data.ackSource.value, (unsigned long) data.ackProcessIdentifier);
#endif
/* BACnet Testing Observed Incident oi00105
ACK of a non-existent object returned the incorrect error code
Revealed by BACnet Test Client v1.8.16 ( www.bac-test.com/bacnet-test-client-download )
BC 135.1: 9.1.3.3-A
Any discussions can be directed to edward@bac-test.com */
if (!Device_Valid_Object_Id(data.eventObjectIdentifier.type, data.eventObjectIdentifier.instance))
{
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM, ERROR_CLASS_OBJECT, ERROR_CODE_UNKNOWN_OBJECT);
}
else if (Alarm_Ack[data.eventObjectIdentifier.type]) {
ack_result =
Alarm_Ack[data.eventObjectIdentifier.type] (&data, &error_code);
switch (ack_result) {
case 1:
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM);
#if PRINT_ENABLED
fprintf(stderr, "Alarm Acknowledge: " "Sending Simple Ack!\n");
#endif
break;
case -1:
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM, ERROR_CLASS_OBJECT,
error_code);
#if PRINT_ENABLED
fprintf(stderr, "Alarm Acknowledge: error %s!\n",
bactext_error_code_name(error_code));
#endif
break;
default:
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr, "Alarm Acknowledge: abort other!\n");
#endif
break;
}
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM,
ERROR_CLASS_OBJECT, ERROR_CODE_NO_ALARM_CONFIGURED);
#if PRINT_ENABLED
fprintf(stderr, "Alarm Acknowledge: error %s!\n",
bactext_error_code_name(ERROR_CODE_NO_ALARM_CONFIGURED));
#endif
}
AA_ABORT:
pdu_len += len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Alarm Acknowledge: " "Failed to send PDU (%s)!\n",
strerror(errno));
#endif
return;
}
+223
View File
@@ -0,0 +1,223 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacerror.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "arf.h"
/* demo objects */
#if defined(BACFILE)
#include "bacfile.h"
#endif
#include "handlers.h"
/** @file h_arf.c Handles Atomic Read File request. */
/*
from BACnet SSPC-135-2004
14. FILE ACCESS SERVICES
This clause defines the set of services used to access and
manipulate files contained in BACnet devices. The concept of files
is used here as a network-visible representation for a collection
of octets of arbitrary length and meaning. This is an abstract
concept only and does not imply the use of disk, tape or other
mass storage devices in the server devices. These services may
be used to access vendor-defined files as well as specific
files defined in the BACnet protocol standard.
Every file that is accessible by File Access Services shall
have a corresponding File object in the BACnet device. This File
object is used to identify the particular file by name. In addition,
the File object provides access to "header information," such
as the file's total size, creation date, and type. File Access
Services may model files in two ways: as a continuous stream of
octets or as a contiguous sequence of numbered records.
The File Access Services provide atomic read and write operations.
In this context "atomic" means that during the execution
of a read or write operation, no other AtomicReadFile or
AtomicWriteFile operations are allowed for the same file.
Synchronization of these services with internal operations
of the BACnet device is a local matter and is not defined by this
standard.
14.1 AtomicReadFile Service
14.1.5 Service Procedure
The responding BACnet-user shall first verify the validity
of the 'File Identifier' parameter and return a 'Result(-)' response
with the appropriate error class and code if the File object
is unknown, if there is currently another AtomicReadFile or
AtomicWriteFile service in progress, or if the File object is
currently inaccessible for another reason. If the 'File Start
Position' parameter or the 'File Start Record' parameter is
either less than 0 or exceeds the actual file size, then the appropriate
error is returned in a 'Result(-)' response. If not, then the
responding BACnet-user shall read the number of octets specified by
'Requested Octet Count' or the number of records specified by
'Requested Record Count'. If the number of remaining octets or
records is less than the requested amount, then the length of
the 'File Data' returned or 'Returned Record Count' shall indicate
the actual number read. If the returned response contains the
last octet or record of the file, then the 'End Of File' parameter
shall be TRUE, otherwise FALSE.
*/
#if defined(BACFILE)
void handler_atomic_read_file(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_ATOMIC_READ_FILE_DATA data;
int len = 0;
int pdu_len = 0;
bool error = false;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
#if PRINT_ENABLED
fprintf(stderr, "Received Atomic-Read-File Request!\n");
#endif
/* 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 (service_data->segmented_message) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
true);
#if PRINT_ENABLED
fprintf(stderr, "ARF: Segmented Message. Sending Abort!\n");
#endif
goto ARF_ABORT;
}
len = arf_decode_service_request(service_request, service_len, &data);
/* bad decoding - send an abort */
if (len < 0) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr, "Bad Encoding. Sending Abort!\n");
#endif
goto ARF_ABORT;
}
if (data.object_type == OBJECT_FILE) {
if (!bacfile_valid_instance(data.object_instance)) {
error = true;
} else if (data.access == FILE_STREAM_ACCESS) {
if (data.type.stream.requestedOctetCount <
octetstring_capacity(&data.fileData[0])) {
bacfile_read_stream_data(&data);
#if PRINT_ENABLED
fprintf(stderr, "ARF: Stream offset %d, %d octets.\n",
data.type.stream.fileStartPosition,
data.type.stream.requestedOctetCount);
#endif
len =
arf_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, &data);
} else {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
#if PRINT_ENABLED
fprintf(stderr, "Too Big To Send (%d >= %d). Sending Abort!\n",
data.type.stream.requestedOctetCount,
(int)octetstring_capacity(&data.fileData[0]));
#endif
}
} else if (data.access == FILE_RECORD_ACCESS) {
if (data.type.record.fileStartRecord >=
BACNET_READ_FILE_RECORD_COUNT) {
error_class = ERROR_CLASS_SERVICES;
error_code = ERROR_CODE_INVALID_FILE_START_POSITION;
error = true;
} else if (bacfile_read_stream_data(&data)) {
#if PRINT_ENABLED
fprintf(stderr, "ARF: fileStartRecord %d, %u RecordCount.\n",
data.type.record.fileStartRecord,
data.type.record.RecordCount);
#endif
len =
arf_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, &data);
} else {
error = true;
error_class = ERROR_CLASS_OBJECT;
error_code = ERROR_CODE_FILE_ACCESS_DENIED;
}
} else {
error = true;
error_class = ERROR_CLASS_SERVICES;
error_code = ERROR_CODE_INVALID_FILE_ACCESS_METHOD;
#if PRINT_ENABLED
fprintf(stderr, "Record Access Requested. Sending Error!\n");
#endif
}
} else {
error = true;
error_class = ERROR_CLASS_SERVICES;
error_code = ERROR_CODE_INCONSISTENT_OBJECT_TYPE;
}
if (error) {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_ATOMIC_READ_FILE,
error_class, error_code);
}
ARF_ABORT:
pdu_len += len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0) {
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
}
#endif
return;
}
#endif
+79
View File
@@ -0,0 +1,79 @@
/**************************************************************************
*
* Copyright (C) 2005 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 "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "arf.h"
#if defined(BACFILE)
#include "bacfile.h"
#endif
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
/** @file h_arf_a.c Handles Acknowledgment of Atomic Read File response. */
/* We performed an AtomicReadFile Request, */
/* and here is the data from the server */
/* Note: it does not have to be the same file=instance */
/* that someone can read from us. It is common to */
/* use the description as the file name. */
#if defined(BACFILE)
void handler_atomic_read_file_ack(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
{
int len = 0;
BACNET_ATOMIC_READ_FILE_DATA data;
uint32_t instance = 0;
(void) src;
/* get the file instance from the tsm data before freeing it */
instance = bacfile_instance_from_tsm(service_data->invoke_id);
len = arf_ack_decode_service_request(service_request, service_len, &data);
#if PRINT_ENABLED
fprintf(stderr, "Received Read-File Ack!\n");
#endif
if ((len > 0) && (instance <= BACNET_MAX_INSTANCE)) {
/* write the data received to the file specified */
if (data.access == FILE_STREAM_ACCESS) {
bacfile_read_ack_stream_data(instance, &data);
} else if (data.access == FILE_RECORD_ACCESS) {
bacfile_read_ack_record_data(instance, &data);
}
}
}
#endif
+190
View File
@@ -0,0 +1,190 @@
/**************************************************************************
*
* Copyright (C) 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.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacstr.h"
#include "bacerror.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "awf.h"
/* demo objects */
#include "device.h"
#if defined(BACFILE)
#include "bacfile.h"
#endif
#include "handlers.h"
/** @file h_awf.c Handles Atomic Write File request. */
/*
from BACnet SSPC-135-2004
14. FILE ACCESS SERVICES
This clause defines the set of services used to access and
manipulate files contained in BACnet devices. The concept of files
is used here as a network-visible representation for a collection
of octets of arbitrary length and meaning. This is an abstract
concept only and does not imply the use of disk, tape or other
mass storage devices in the server devices. These services may
be used to access vendor-defined files as well as specific
files defined in the BACnet protocol standard.
Every file that is accessible by File Access Services shall
have a corresponding File object in the BACnet device. This File
object is used to identify the particular file by name. In addition,
the File object provides access to "header information," such
as the file's total size, creation date, and type. File Access
Services may model files in two ways: as a continuous stream of
octets or as a contiguous sequence of numbered records.
The File Access Services provide atomic read and write operations.
In this context "atomic" means that during the execution
of a read or write operation, no other AtomicReadFile or
AtomicWriteFile operations are allowed for the same file.
Synchronization of these services with internal operations
of the BACnet device is a local matter and is not defined by this
standard.
*/
#if defined(BACFILE)
void handler_atomic_write_file(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_ATOMIC_WRITE_FILE_DATA data;
int len = 0;
int pdu_len = 0;
bool error = false;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
#if PRINT_ENABLED
fprintf(stderr, "Received AtomicWriteFile Request!\n");
#endif
/* 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 (service_data->segmented_message) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
true);
#if PRINT_ENABLED
fprintf(stderr, "Segmented Message. Sending Abort!\n");
#endif
goto AWF_ABORT;
}
len = awf_decode_service_request(service_request, service_len, &data);
/* bad decoding - send an abort */
if (len < 0) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr, "Bad Encoding. Sending Abort!\n");
#endif
goto AWF_ABORT;
}
if (data.object_type == OBJECT_FILE) {
if (!bacfile_valid_instance(data.object_instance)) {
error = true;
} else if (data.access == FILE_STREAM_ACCESS) {
if (bacfile_write_stream_data(&data)) {
#if PRINT_ENABLED
fprintf(stderr, "AWF: Stream offset %d, %d bytes\n",
data.type.stream.fileStartPosition,
(int)octetstring_length(&data.fileData[0]));
#endif
len =
awf_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, &data);
} else {
error = true;
error_class = ERROR_CLASS_OBJECT;
error_code = ERROR_CODE_FILE_ACCESS_DENIED;
}
} else if (data.access == FILE_RECORD_ACCESS) {
if (bacfile_write_record_data(&data)) {
#if PRINT_ENABLED
fprintf(stderr, "AWF: StartRecord %d, RecordCount %u\n",
data.type.record.fileStartRecord,
data.type.record.returnedRecordCount);
#endif
len =
awf_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, &data);
} else {
error = true;
error_class = ERROR_CLASS_OBJECT;
error_code = ERROR_CODE_FILE_ACCESS_DENIED;
}
} else {
error = true;
error_class = ERROR_CLASS_SERVICES;
error_code = ERROR_CODE_INVALID_FILE_ACCESS_METHOD;
#if PRINT_ENABLED
fprintf(stderr, "Record Access Requested. Sending Error!\n");
#endif
}
} else {
error = true;
error_class = ERROR_CLASS_SERVICES;
error_code = ERROR_CODE_INCONSISTENT_OBJECT_TYPE;
}
if (error) {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_ATOMIC_WRITE_FILE,
error_class, error_code);
}
AWF_ABORT:
pdu_len += len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0) {
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
}
#endif
return;
}
#endif
File diff suppressed because it is too large Load Diff
+163
View File
@@ -0,0 +1,163 @@
/**************************************************************************
*
* Copyright (C) 2011 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
/* special for this module */
#include "cov.h"
#include "bactext.h"
#include "handlers.h"
#ifndef MAX_COV_PROPERTIES
#define MAX_COV_PROPERTIES 2
#endif
/** @file h_ccov.c Handles Confirmed COV Notifications. */
/* */
/** Handler for an Confirmed COV Notification.
* @ingroup DSCOV
* Decodes the received list of Properties to update,
* and print them out with the subscription information.
* @note Nothing is specified in BACnet about what to do with the
* information received from Confirmed COV Notifications.
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
void handler_ccov_notification(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_NPDU_DATA npdu_data;
BACNET_COV_DATA cov_data;
BACNET_PROPERTY_VALUE property_value[MAX_COV_PROPERTIES];
BACNET_PROPERTY_VALUE *pProperty_value = NULL;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_ADDRESS my_address;
/* create linked list to store data if more
than one property value is expected */
bacapp_property_value_list_init(&property_value[0], MAX_COV_PROPERTIES);
cov_data.listOfValues = &property_value[0];
/* 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 PRINT_ENABLED
fprintf(stderr, "CCOV: Received Notification!\n");
#endif
if (service_data->segmented_message) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
true);
#if PRINT_ENABLED
fprintf(stderr, "CCOV: Segmented message. Sending Abort!\n");
#endif
goto CCOV_ABORT;
}
/* decode the service request only */
len =
cov_notify_decode_service_request(service_request, service_len,
&cov_data);
#if PRINT_ENABLED
if (len > 0) {
fprintf(stderr, "CCOV: PID=%u ", cov_data.subscriberProcessIdentifier);
fprintf(stderr, "instance=%u ", cov_data.initiatingDeviceIdentifier);
fprintf(stderr, "%s %u ",
bactext_object_type_name(cov_data.monitoredObjectIdentifier.type),
cov_data.monitoredObjectIdentifier.instance);
fprintf(stderr, "time remaining=%u seconds ", cov_data.timeRemaining);
fprintf(stderr, "\n");
pProperty_value = &property_value[0];
while (pProperty_value) {
fprintf(stderr, "CCOV: ");
if (pProperty_value->propertyIdentifier < 512) {
fprintf(stderr, "%s ",
bactext_property_name
(pProperty_value->propertyIdentifier));
} else {
fprintf(stderr, "proprietary %u ",
pProperty_value->propertyIdentifier);
}
if (pProperty_value->propertyArrayIndex != BACNET_ARRAY_ALL) {
fprintf(stderr, "%u ", pProperty_value->propertyArrayIndex);
}
fprintf(stderr, "\n");
pProperty_value = pProperty_value->next;
}
}
#endif
/* bad decoding or something we didn't understand - send an abort */
if (len <= 0) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr, "CCOV: Bad Encoding. Sending Abort!\n");
#endif
goto CCOV_ABORT;
} else {
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_COV_NOTIFICATION);
#if PRINT_ENABLED
fprintf(stderr, "CCOV: Sending Simple Ack!\n");
#endif
}
CCOV_ABORT:
pdu_len += len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0) {
fprintf(stderr, "CCOV: Failed to send PDU (%s)!\n", strerror(errno));
}
#else
bytes_sent = bytes_sent;
#endif
return;
}
+910
View File
@@ -0,0 +1,910 @@
/**************************************************************************
*
* Copyright (C) 2007-2008 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacerror.h"
#include "bacdcode.h"
#include "bacaddr.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "reject.h"
#include "cov.h"
#include "tsm.h"
#include "dcc.h"
#if PRINT_ENABLED
#include "bactext.h"
#endif
/* demo objects */
#include "device.h"
#include "handlers.h"
#ifndef MAX_COV_PROPERTIES
#define MAX_COV_PROPERTIES 2
#endif
/** @file h_cov.c Handles Change of Value (COV) services. */
typedef struct BACnet_COV_Address {
bool valid:1;
BACNET_ADDRESS dest;
} BACNET_COV_ADDRESS;
/* note: This COV service only monitors the properties
of an object that have been specified in the standard. */
typedef struct BACnet_COV_Subscription_Flags {
bool valid:1;
bool issueConfirmedNotifications:1; /* optional */
bool send_requested:1;
} BACNET_COV_SUBSCRIPTION_FLAGS;
typedef struct BACnet_COV_Subscription {
BACNET_COV_SUBSCRIPTION_FLAGS flag;
uint8_t dest_index;
uint8_t invokeID; /* for confirmed COV */
uint32_t subscriberProcessIdentifier;
uint32_t lifetime; /* optional */
BACNET_OBJECT_ID monitoredObjectIdentifier;
} BACNET_COV_SUBSCRIPTION;
#ifndef MAX_COV_SUBCRIPTIONS
#define MAX_COV_SUBCRIPTIONS 128
#endif
static BACNET_COV_SUBSCRIPTION COV_Subscriptions[MAX_COV_SUBCRIPTIONS];
#ifndef MAX_COV_ADDRESSES
#define MAX_COV_ADDRESSES 16
#endif
static BACNET_COV_ADDRESS COV_Addresses[MAX_COV_ADDRESSES];
/**
* Gets the address from the list of COV addresses
*
* @param index - offset into COV address list where address is stored
* @param dest - address to be filled when found
*
* @return true if valid address, false if not valid or not found
*/
static BACNET_ADDRESS *cov_address_get(
int index)
{
BACNET_ADDRESS *cov_dest = NULL;
if (index < MAX_COV_ADDRESSES) {
if (COV_Addresses[index].valid) {
cov_dest = &COV_Addresses[index].dest;
}
}
return cov_dest;
}
/**
* Removes the address from the list of COV addresses, if it is not
* used by other COV subscriptions
*/
static void cov_address_remove_unused(
void)
{
unsigned index = 0;
unsigned cov_index = 0;
bool found = false;
for (cov_index = 0; cov_index < MAX_COV_ADDRESSES; cov_index++) {
if (COV_Addresses[cov_index].valid) {
found = false;
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
if ((COV_Subscriptions[index].flag.valid) &&
(COV_Subscriptions[index].dest_index == cov_index)) {
found = true;
break;
}
}
if (!found) {
COV_Addresses[cov_index].valid = false;
}
}
}
}
/**
* Adds the address to the list of COV addresses
*
* @param dest - address to be added if there is room in the list
*
* @return index number 0..N, or -1 if unable to add
*/
static int cov_address_add(
BACNET_ADDRESS * dest)
{
int index = -1;
unsigned i = 0;
bool found = false;
bool valid = false;
BACNET_ADDRESS *cov_dest = NULL;
if (dest) {
for (i = 0; i < MAX_COV_ADDRESSES; i++) {
valid = COV_Addresses[i].valid;
if (valid) {
cov_dest = &COV_Addresses[i].dest;
found = bacnet_address_same(dest, cov_dest);
if (found) {
index = i;
break;
}
}
}
if (!found) {
/* find a free place to add a new address */
for (i = 0; i < MAX_COV_ADDRESSES; i++) {
valid = COV_Addresses[i].valid;
if (!valid) {
index = i;
cov_dest = &COV_Addresses[i].dest;
bacnet_address_copy(cov_dest, dest);
COV_Addresses[i].valid = true;
break;
}
}
}
}
return index;
}
/*
BACnetCOVSubscription ::= SEQUENCE {
Recipient [0] BACnetRecipientProcess,
BACnetRecipient ::= CHOICE {
device [0] BACnetObjectIdentifier,
address [1] BACnetAddress
BACnetAddress ::= SEQUENCE {
network-number Unsigned16, -- A value of 0 indicates the local network
mac-address OCTET STRING -- A string of length 0 indicates a broadcast
}
}
BACnetRecipientProcess ::= SEQUENCE {
recipient [0] BACnetRecipient,
processIdentifier [1] Unsigned32
}
MonitoredPropertyReference [1] BACnetObjectPropertyReference,
BACnetObjectPropertyReference ::= SEQUENCE {
objectIdentifier [0] BACnetObjectIdentifier,
propertyIdentifier [1] BACnetPropertyIdentifier,
propertyArrayIndex [2] Unsigned OPTIONAL -- used only with array datatype
-- if omitted with an array the entire array is referenced
}
IssueConfirmedNotifications [2] BOOLEAN,
TimeRemaining [3] Unsigned,
COVIncrement [4] REAL OPTIONAL
*/
static int cov_encode_subscription(
uint8_t * apdu,
int max_apdu,
BACNET_COV_SUBSCRIPTION * cov_subscription)
{
int len = 0;
int apdu_len = 0;
BACNET_OCTET_STRING octet_string;
BACNET_ADDRESS *dest = NULL;
/* FIXME: unused parameter */
max_apdu = max_apdu;
if (!cov_subscription) {
return 0;
}
dest = cov_address_get(cov_subscription->dest_index);
if (!dest) {
return 0;
}
/* Recipient [0] BACnetRecipientProcess - opening */
len = encode_opening_tag(&apdu[apdu_len], 0);
apdu_len += len;
/* recipient [0] BACnetRecipient - opening */
len = encode_opening_tag(&apdu[apdu_len], 0);
apdu_len += len;
/* CHOICE - address [1] BACnetAddress - opening */
len = encode_opening_tag(&apdu[apdu_len], 1);
apdu_len += len;
/* network-number Unsigned16, */
/* -- A value of 0 indicates the local network */
len = encode_application_unsigned(&apdu[apdu_len], dest->net);
apdu_len += len;
/* mac-address OCTET STRING */
/* -- A string of length 0 indicates a broadcast */
if (dest->net) {
octetstring_init(&octet_string, &dest->adr[0], dest->len);
} else {
octetstring_init(&octet_string, &dest->mac[0], dest->mac_len);
}
len = encode_application_octet_string(&apdu[apdu_len], &octet_string);
apdu_len += len;
/* CHOICE - address [1] BACnetAddress - closing */
len = encode_closing_tag(&apdu[apdu_len], 1);
apdu_len += len;
/* recipient [0] BACnetRecipient - closing */
len = encode_closing_tag(&apdu[apdu_len], 0);
apdu_len += len;
/* processIdentifier [1] Unsigned32 */
len =
encode_context_unsigned(&apdu[apdu_len], 1,
cov_subscription->subscriberProcessIdentifier);
apdu_len += len;
/* Recipient [0] BACnetRecipientProcess - closing */
len = encode_closing_tag(&apdu[apdu_len], 0);
apdu_len += len;
/* MonitoredPropertyReference [1] BACnetObjectPropertyReference, */
len = encode_opening_tag(&apdu[apdu_len], 1);
apdu_len += len;
/* objectIdentifier [0] */
len =
encode_context_object_id(&apdu[apdu_len], 0,
cov_subscription->monitoredObjectIdentifier.type,
cov_subscription->monitoredObjectIdentifier.instance);
apdu_len += len;
/* propertyIdentifier [1] */
/* FIXME: we are monitoring 2 properties! How to encode? */
len = encode_context_enumerated(&apdu[apdu_len], 1, PROP_PRESENT_VALUE);
apdu_len += len;
/* MonitoredPropertyReference [1] - closing */
len = encode_closing_tag(&apdu[apdu_len], 1);
apdu_len += len;
/* IssueConfirmedNotifications [2] BOOLEAN, */
len =
encode_context_boolean(&apdu[apdu_len], 2,
cov_subscription->flag.issueConfirmedNotifications);
apdu_len += len;
/* TimeRemaining [3] Unsigned, */
len =
encode_context_unsigned(&apdu[apdu_len], 3,
cov_subscription->lifetime);
apdu_len += len;
return apdu_len;
}
/** Handle a request to list all the COV subscriptions.
* @ingroup DSCOV
* Invoked by a request to read the Device object's PROP_ACTIVE_COV_SUBSCRIPTIONS.
* Loops through the list of COV Subscriptions, and, for each valid one,
* adds its description to the APDU.
* @note This function needs some work to better handle buffer overruns.
* @param apdu [out] Buffer in which the APDU contents are built.
* @param max_apdu [in] Max length of the APDU buffer.
* @return How many bytes were encoded in the buffer, or -2 if the response
* would not fit within the buffer.
*/
int handler_cov_encode_subscriptions(
uint8_t * apdu,
int max_apdu)
{
int len = 0;
int apdu_len = 0;
unsigned index = 0;
if (apdu) {
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
if (COV_Subscriptions[index].flag.valid) {
len =
cov_encode_subscription(&apdu[apdu_len],
max_apdu - apdu_len, &COV_Subscriptions[index]);
apdu_len += len;
/* TODO: too late here to notice that we overran the buffer */
if (apdu_len > max_apdu) {
return -2;
}
}
}
}
return apdu_len;
}
/** Handler to initialize the COV list, clearing and disabling each entry.
* @ingroup DSCOV
*/
void handler_cov_init(
void)
{
unsigned index = 0;
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
COV_Subscriptions[index].flag.valid = false;
COV_Subscriptions[index].dest_index = -1;
COV_Subscriptions[index].subscriberProcessIdentifier = 0;
COV_Subscriptions[index].monitoredObjectIdentifier.type =
OBJECT_ANALOG_INPUT;
COV_Subscriptions[index].monitoredObjectIdentifier.instance = 0;
COV_Subscriptions[index].flag.issueConfirmedNotifications = false;
COV_Subscriptions[index].invokeID = 0;
COV_Subscriptions[index].lifetime = 0;
COV_Subscriptions[index].flag.send_requested = false;
}
for (index = 0; index < MAX_COV_ADDRESSES; index++) {
COV_Addresses[index].valid = false;
}
}
static bool cov_list_subscribe(
BACNET_ADDRESS * src,
BACNET_SUBSCRIBE_COV_DATA * cov_data,
BACNET_ERROR_CLASS * error_class,
BACNET_ERROR_CODE * error_code)
{
bool existing_entry = false;
int index;
int first_invalid_index = -1;
bool found = true;
bool address_match = false;
BACNET_ADDRESS *dest = NULL;
/* unable to subscribe - resources? */
/* unable to cancel subscription - other? */
/* existing? - match Object ID and Process ID and address */
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
if (COV_Subscriptions[index].flag.valid) {
dest = cov_address_get(COV_Subscriptions[index].dest_index);
if (dest) {
address_match = bacnet_address_same(src, dest);
} else {
/* skip address matching - we don't have an address */
address_match = true;
}
if ((COV_Subscriptions[index].monitoredObjectIdentifier.type ==
cov_data->monitoredObjectIdentifier.type) &&
(COV_Subscriptions[index].monitoredObjectIdentifier.instance ==
cov_data->monitoredObjectIdentifier.instance) &&
(COV_Subscriptions[index].subscriberProcessIdentifier ==
cov_data->subscriberProcessIdentifier) && address_match) {
existing_entry = true;
if (cov_data->cancellationRequest) {
COV_Subscriptions[index].flag.valid = false;
COV_Subscriptions[index].dest_index = -1;
cov_address_remove_unused();
} else {
COV_Subscriptions[index].dest_index = cov_address_add(src);
COV_Subscriptions[index].flag.issueConfirmedNotifications =
cov_data->issueConfirmedNotifications;
COV_Subscriptions[index].lifetime = cov_data->lifetime;
COV_Subscriptions[index].flag.send_requested = true;
}
if (COV_Subscriptions[index].invokeID) {
tsm_free_invoke_id(COV_Subscriptions[index].invokeID);
COV_Subscriptions[index].invokeID = 0;
}
break;
}
} else {
if (first_invalid_index < 0) {
first_invalid_index = index;
}
}
}
if (!existing_entry && (first_invalid_index >= 0) &&
(!cov_data->cancellationRequest)) {
index = first_invalid_index;
found = true;
COV_Subscriptions[index].flag.valid = true;
COV_Subscriptions[index].dest_index = cov_address_add(src);
COV_Subscriptions[index].monitoredObjectIdentifier.type =
cov_data->monitoredObjectIdentifier.type;
COV_Subscriptions[index].monitoredObjectIdentifier.instance =
cov_data->monitoredObjectIdentifier.instance;
COV_Subscriptions[index].subscriberProcessIdentifier =
cov_data->subscriberProcessIdentifier;
COV_Subscriptions[index].flag.issueConfirmedNotifications =
cov_data->issueConfirmedNotifications;
COV_Subscriptions[index].invokeID = 0;
COV_Subscriptions[index].lifetime = cov_data->lifetime;
COV_Subscriptions[index].flag.send_requested = true;
} else if (!existing_entry) {
if (first_invalid_index < 0) {
/* Out of resources */
*error_class = ERROR_CLASS_RESOURCES;
*error_code = ERROR_CODE_NO_SPACE_TO_ADD_LIST_ELEMENT;
found = false;
} else {
/* cancellationRequest - valid object not subscribed */
/* From BACnet Standard 135-2010-13.14.2
...Cancellations that are issued for which no matching COV
context can be found shall succeed as if a context had
existed, returning 'Result(+)'. */
found = true;
}
}
return found;
}
static bool cov_send_request(
BACNET_COV_SUBSCRIPTION * cov_subscription,
BACNET_PROPERTY_VALUE * value_list)
{
int len = 0;
int pdu_len = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
int bytes_sent = 0;
uint8_t invoke_id = 0;
bool status = false; /* return value */
BACNET_COV_DATA cov_data;
BACNET_ADDRESS *dest = NULL;
if (!dcc_communication_enabled()) {
return status;
}
#if PRINT_ENABLED
fprintf(stderr, "COVnotification: requested\n");
#endif
if (!cov_subscription) {
return status;
}
dest = cov_address_get(cov_subscription->dest_index);
if (!dest) {
#if PRINT_ENABLED
fprintf(stderr, "COVnotification: dest not found!\n");
#endif
return status;
}
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], dest, &my_address,
&npdu_data);
/* load the COV data structure for outgoing message */
cov_data.subscriberProcessIdentifier =
cov_subscription->subscriberProcessIdentifier;
cov_data.initiatingDeviceIdentifier = Device_Object_Instance_Number();
cov_data.monitoredObjectIdentifier.type =
cov_subscription->monitoredObjectIdentifier.type;
cov_data.monitoredObjectIdentifier.instance =
cov_subscription->monitoredObjectIdentifier.instance;
cov_data.timeRemaining = cov_subscription->lifetime;
cov_data.listOfValues = value_list;
if (cov_subscription->flag.issueConfirmedNotifications) {
npdu_data.data_expecting_reply = true;
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
cov_subscription->invokeID = invoke_id;
len =
ccov_notify_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
sizeof(Handler_Transmit_Buffer) - pdu_len, invoke_id, &cov_data);
} else {
goto COV_FAILED;
}
} else {
len =
ucov_notify_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
sizeof(Handler_Transmit_Buffer) - pdu_len, &cov_data);
}
pdu_len += len;
if (cov_subscription->flag.issueConfirmedNotifications) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, dest, &npdu_data,
&Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
}
bytes_sent =
datalink_send_pdu(dest, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
if (bytes_sent > 0) {
status = true;
#if PRINT_ENABLED
fprintf(stderr, "COVnotification: Sent!\n");
#endif
}
COV_FAILED:
return status;
}
static void cov_lifetime_expiration_handler(
unsigned index,
uint32_t elapsed_seconds,
uint32_t lifetime_seconds)
{
if (index < MAX_COV_SUBCRIPTIONS) {
/* handle lifetime expiration */
if (lifetime_seconds >= elapsed_seconds) {
COV_Subscriptions[index].lifetime -= elapsed_seconds;
#if 0
fprintf(stderr, "COVtimer: subscription[%d].lifetime=%lu\n", index,
(unsigned long) COV_Subscriptions[index].lifetime);
#endif
} else {
COV_Subscriptions[index].lifetime = 0;
}
if (COV_Subscriptions[index].lifetime == 0) {
/* expire the subscription */
#if PRINT_ENABLED
fprintf(stderr, "COVtimer: PID=%u ",
COV_Subscriptions[index].subscriberProcessIdentifier);
fprintf(stderr, "%s %u ",
bactext_object_type_name(COV_Subscriptions[index].
monitoredObjectIdentifier.type),
COV_Subscriptions[index].monitoredObjectIdentifier.instance);
fprintf(stderr, "time remaining=%u seconds ",
COV_Subscriptions[index].lifetime);
fprintf(stderr, "\n");
#endif
COV_Subscriptions[index].flag.valid = false;
COV_Subscriptions[index].dest_index = -1;
cov_address_remove_unused();
if (COV_Subscriptions[index].flag.issueConfirmedNotifications) {
if (COV_Subscriptions[index].invokeID) {
tsm_free_invoke_id(COV_Subscriptions[index].invokeID);
COV_Subscriptions[index].invokeID = 0;
}
}
}
}
}
/** Handler to check the list of subscribed objects for any that have changed
* and so need to have notifications sent.
* @ingroup DSCOV
* This handler will be invoked by the main program every second or so.
* This example only handles Binary Inputs, but can be easily extended to
* support other types.
* For each subscribed object,
* - See if the subscription has timed out
* - Remove it if it has timed out.
* - See if the subscribed object instance has changed
* (eg, check with Binary_Input_Change_Of_Value() )
* - If changed,
* - Clear the COV (eg, Binary_Input_Change_Of_Value_Clear() )
* - Send the notice with cov_send_request()
* - Will be confirmed or unconfirmed, as per the subscription.
*
* @note worst case tasking: MS/TP with the ability to send only
* one notification per task cycle.
*
* @param elapsed_seconds [in] How many seconds have elapsed since last called.
*/
void handler_cov_timer_seconds(
uint32_t elapsed_seconds)
{
unsigned index = 0;
uint32_t lifetime_seconds = 0;
if (elapsed_seconds) {
/* handle the subscription timeouts */
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
if (COV_Subscriptions[index].flag.valid) {
lifetime_seconds = COV_Subscriptions[index].lifetime;
if (lifetime_seconds) {
/* only expire COV with definite lifetimes */
cov_lifetime_expiration_handler(index, elapsed_seconds,
lifetime_seconds);
}
}
}
}
}
bool handler_cov_fsm(
void)
{
static int index = 0;
BACNET_OBJECT_TYPE object_type = MAX_BACNET_OBJECT_TYPE;
uint32_t object_instance = 0;
bool status = false;
bool send = false;
BACNET_PROPERTY_VALUE value_list[MAX_COV_PROPERTIES];
/* states for transmitting */
static enum {
COV_STATE_IDLE = 0,
COV_STATE_MARK,
COV_STATE_CLEAR,
COV_STATE_FREE,
COV_STATE_SEND
} cov_task_state = COV_STATE_IDLE;
switch (cov_task_state) {
case COV_STATE_IDLE:
index = 0;
cov_task_state = COV_STATE_MARK;
break;
case COV_STATE_MARK:
/* mark any subscriptions where the value has changed */
if (COV_Subscriptions[index].flag.valid) {
object_type = (BACNET_OBJECT_TYPE)
COV_Subscriptions[index].monitoredObjectIdentifier.type;
object_instance =
COV_Subscriptions[index].
monitoredObjectIdentifier.instance;
status = Device_COV(object_type, object_instance);
if (status) {
COV_Subscriptions[index].flag.send_requested = true;
#if PRINT_ENABLED
fprintf(stderr, "COVtask: Marking...\n");
#endif
}
}
index++;
if (index >= MAX_COV_SUBCRIPTIONS) {
index = 0;
cov_task_state = COV_STATE_CLEAR;
}
break;
case COV_STATE_CLEAR:
/* clear the COV flag after checking all subscriptions */
if ((COV_Subscriptions[index].flag.valid) &&
(COV_Subscriptions[index].flag.send_requested)) {
object_type = (BACNET_OBJECT_TYPE)
COV_Subscriptions[index].monitoredObjectIdentifier.type;
object_instance =
COV_Subscriptions[index].
monitoredObjectIdentifier.instance;
Device_COV_Clear(object_type, object_instance);
}
index++;
if (index >= MAX_COV_SUBCRIPTIONS) {
index = 0;
cov_task_state = COV_STATE_FREE;
}
break;
case COV_STATE_FREE:
/* confirmed notification house keeping */
if ((COV_Subscriptions[index].flag.valid) &&
(COV_Subscriptions[index].flag.issueConfirmedNotifications) &&
(COV_Subscriptions[index].invokeID)) {
if (tsm_invoke_id_free(COV_Subscriptions[index].invokeID)) {
COV_Subscriptions[index].invokeID = 0;
} else
if (tsm_invoke_id_failed(COV_Subscriptions
[index].invokeID)) {
tsm_free_invoke_id(COV_Subscriptions[index].invokeID);
COV_Subscriptions[index].invokeID = 0;
}
}
index++;
if (index >= MAX_COV_SUBCRIPTIONS) {
index = 0;
cov_task_state = COV_STATE_SEND;
}
break;
case COV_STATE_SEND:
/* send any COVs that are requested */
if ((COV_Subscriptions[index].flag.valid) &&
(COV_Subscriptions[index].flag.send_requested)) {
send = true;
if (COV_Subscriptions[index].flag.issueConfirmedNotifications) {
if (COV_Subscriptions[index].invokeID != 0) {
/* already sending */
send = false;
}
if (!tsm_transaction_available()) {
/* no transactions available - can't send now */
send = false;
}
}
if (send) {
object_type = (BACNET_OBJECT_TYPE)
COV_Subscriptions[index].
monitoredObjectIdentifier.type;
object_instance =
COV_Subscriptions[index].
monitoredObjectIdentifier.instance;
#if PRINT_ENABLED
fprintf(stderr, "COVtask: Sending...\n");
#endif
/* configure the linked list for the two properties */
bacapp_property_value_list_init(&value_list[0],
MAX_COV_PROPERTIES);
status = Device_Encode_Value_List(object_type,
object_instance, &value_list[0]);
if (status) {
status =
cov_send_request(&COV_Subscriptions[index],
&value_list[0]);
}
if (status) {
COV_Subscriptions[index].flag.send_requested = false;
}
}
}
index++;
if (index >= MAX_COV_SUBCRIPTIONS) {
index = 0;
cov_task_state = COV_STATE_IDLE;
}
break;
default:
index = 0;
cov_task_state = COV_STATE_IDLE;
break;
}
return (cov_task_state == COV_STATE_IDLE);
}
void handler_cov_task(
void)
{
handler_cov_fsm();
}
static bool cov_subscribe(
BACNET_ADDRESS * src,
BACNET_SUBSCRIBE_COV_DATA * cov_data,
BACNET_ERROR_CLASS * error_class,
BACNET_ERROR_CODE * error_code)
{
bool status = false; /* return value */
BACNET_OBJECT_TYPE object_type = MAX_BACNET_OBJECT_TYPE;
uint32_t object_instance = 0;
object_type =
(BACNET_OBJECT_TYPE) cov_data->monitoredObjectIdentifier.type;
object_instance = cov_data->monitoredObjectIdentifier.instance;
status = Device_Valid_Object_Id(object_type, object_instance);
if (status) {
status = Device_Value_List_Supported(object_type);
if (status) {
status =
cov_list_subscribe(src, cov_data, error_class, error_code);
} else {
*error_class = ERROR_CLASS_OBJECT;
*error_code = ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED;
}
} else {
*error_class = ERROR_CLASS_OBJECT;
*error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return status;
}
/** Handler for a COV Subscribe Service request.
* @ingroup DSCOV
* This handler will be invoked by apdu_handler() if it has been enabled
* by a call to apdu_set_confirmed_handler().
* This handler builds a response packet, which is
* - an Abort if
* - the message is segmented
* - if decoding fails
* - an ACK, if cov_subscribe() succeeds
* - an Error if cov_subscribe() fails
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
void handler_cov_subscribe(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_SUBSCRIBE_COV_DATA cov_data;
int len = 0;
int pdu_len = 0;
int npdu_len = 0;
int apdu_len = 0;
BACNET_NPDU_DATA npdu_data;
bool success = false;
int bytes_sent = 0;
BACNET_ADDRESS my_address;
bool error = false;
/* initialize a common abort code */
cov_data.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
npdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address,
&npdu_data);
if (service_data->segmented_message) {
/* we don't support segmentation - send an abort */
len = BACNET_STATUS_ABORT;
#if PRINT_ENABLED
fprintf(stderr, "SubscribeCOV: Segmented message. Sending Abort!\n");
#endif
error = true;
goto COV_ABORT;
}
len =
cov_subscribe_decode_service_request(service_request, service_len,
&cov_data);
#if PRINT_ENABLED
if (len <= 0)
fprintf(stderr, "SubscribeCOV: Unable to decode Request!\n");
#endif
if (len < 0) {
error = true;
goto COV_ABORT;
}
cov_data.error_class = ERROR_CLASS_OBJECT;
cov_data.error_code = ERROR_CODE_UNKNOWN_OBJECT;
success =
cov_subscribe(src, &cov_data, &cov_data.error_class,
&cov_data.error_code);
if (success) {
apdu_len =
encode_simple_ack(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_SUBSCRIBE_COV);
#if PRINT_ENABLED
fprintf(stderr, "SubscribeCOV: Sending Simple Ack!\n");
#endif
} else {
len = BACNET_STATUS_ERROR;
error = true;
#if PRINT_ENABLED
fprintf(stderr, "SubscribeCOV: Sending Error!\n");
#endif
}
COV_ABORT:
if (error) {
if (len == BACNET_STATUS_ABORT) {
apdu_len =
abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
abort_convert_error_code(cov_data.error_code), true);
#if PRINT_ENABLED
fprintf(stderr, "SubscribeCOV: Sending Abort!\n");
#endif
} else if (len == BACNET_STATUS_ERROR) {
apdu_len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_SUBSCRIBE_COV,
cov_data.error_class, cov_data.error_code);
#if PRINT_ENABLED
fprintf(stderr, "SubscribeCOV: Sending Error!\n");
#endif
} else if (len == BACNET_STATUS_REJECT) {
apdu_len =
reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
reject_convert_error_code(cov_data.error_code));
#if PRINT_ENABLED
fprintf(stderr, "SubscribeCOV: Sending Reject!\n");
#endif
}
}
pdu_len = npdu_len + apdu_len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "SubscribeCOV: Failed to send PDU (%s)!\n",
strerror(errno));
#endif
}
return;
}
+212
View File
@@ -0,0 +1,212 @@
/**************************************************************************
*
* 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 <stdio.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 "reject.h"
#include "dcc.h"
#include "handlers.h"
#include "device.h"
/** @file h_dcc.c Handles Device Communication Control request. */
static char My_Password[32] = "filister";
/** Sets (non-volatile hold) the password to be used for DCC requests.
* @param new_password [in] The new DCC password, of up to 31 characters.
*/
void handler_dcc_password_set(
char *new_password)
{
size_t i = 0; /* loop counter */
if (new_password) {
for (i = 0; i < (sizeof(My_Password) - 1); i++) {
My_Password[i] = new_password[i];
My_Password[i + 1] = 0;
if (new_password[i] == 0) {
break;
}
}
} else {
for (i = 0; i < sizeof(My_Password); i++) {
My_Password[i] = 0;
}
}
}
/** Gets (non-volatile hold) the password to be used for DCC requests.
* @return DCC password
*/
char *handler_dcc_password(void)
{
return My_Password;
}
/** Handler for a Device Communication Control (DCC) request.
* @ingroup DMDCC
* This handler will be invoked by apdu_handler() if it has been enabled
* by a call to apdu_set_confirmed_handler().
* This handler builds a response packet, which is
* - an Abort if
* - the message is segmented
* - if decoding fails
* - if not a known DCC state
* - an Error if the DCC password is incorrect
* - else tries to send a simple ACK for the DCC on success,
* and sets the DCC state requested.
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
void handler_device_communication_control(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
uint16_t timeDuration = 0;
BACNET_COMMUNICATION_ENABLE_DISABLE state = COMMUNICATION_ENABLE;
BACNET_CHARACTER_STRING password;
int len = 0;
int pdu_len = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
/* encode the NPDU portion of the reply 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 PRINT_ENABLED
fprintf(stderr, "DeviceCommunicationControl!\n");
#endif
if (service_data->segmented_message) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
true);
#if PRINT_ENABLED
fprintf(stderr,
"DeviceCommunicationControl: "
"Sending Abort - segmented message.\n");
#endif
goto DCC_ABORT;
}
/* decode the service request only */
len =
dcc_decode_service_request(service_request, service_len, &timeDuration,
&state, &password);
#if PRINT_ENABLED
if (len > 0)
fprintf(stderr,
"DeviceCommunicationControl: " "timeout=%u state=%u password=%s\n",
(unsigned) timeDuration, (unsigned) state,
characterstring_value(&password));
#endif
/* bad decoding or something we didn't understand - send an abort */
if (len < 0) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr,
"DeviceCommunicationControl: "
"Sending Abort - could not decode.\n");
#endif
goto DCC_ABORT;
}
if (state >= MAX_BACNET_COMMUNICATION_ENABLE_DISABLE) {
len =
reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, REJECT_REASON_UNDEFINED_ENUMERATION);
#if PRINT_ENABLED
fprintf(stderr,
"DeviceCommunicationControl: "
"Sending Reject - undefined enumeration\n");
#endif
} else {
#if BAC_ROUTING
/* Check to see if the current Device supports this service. */
len =
Routed_Device_Service_Approval
(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, (int) state,
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id);
if (len > 0)
goto DCC_ABORT;
#endif
if (characterstring_ansi_same(&password, My_Password)) {
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL);
#if PRINT_ENABLED
fprintf(stderr,
"DeviceCommunicationControl: " "Sending Simple Ack!\n");
#endif
dcc_set_status_duration(state, timeDuration);
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
ERROR_CLASS_SECURITY, ERROR_CODE_PASSWORD_FAILURE);
#if PRINT_ENABLED
fprintf(stderr,
"DeviceCommunicationControl: "
"Sending Error - password failure.\n");
#endif
}
}
DCC_ABORT:
pdu_len += len;
len =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
if (len <= 0) {
#if PRINT_ENABLED
fprintf(stderr,
"DeviceCommunicationControl: " "Failed to send PDU (%s)!\n",
strerror(errno));
#endif
}
return;
}
+81
View File
@@ -0,0 +1,81 @@
/**
* @file
* @author Daniel Blazevic <daniel.blazevic@gmail.com>
* @date 2013
* @brief GetAlarmSummary ACK service handling
*
* @section LICENSE
*
* Copyright (C) 2013 Daniel Blazevic <daniel.blazevic@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @section DESCRIPTION
*
* The GetAlarmSummary ACK service handler is used by a client BACnet-user to
* obtain a summary of "active alarms." The term "active alarm" refers to
* BACnet standard objects that have an Event_State property whose value is
* not equal to NORMAL and a Notify_Type property whose value is ALARM.
*/
#include <assert.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 "handlers.h"
#include "get_alarm_sum.h"
/** Example function to handle a GetAlarmSummary ACK.
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
void get_alarm_summary_ack_handler(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
{
uint16_t apdu_len = 0;
uint16_t len = 0;
BACNET_GET_ALARM_SUMMARY_DATA data;
while (service_len - len) {
apdu_len =
get_alarm_summary_ack_decode_apdu_data(&service_request[len],
service_len - len, &data);
len += apdu_len;
if (apdu_len > 0) {
/* FIXME: Add code to process data */
} else {
break;
}
}
}
+162
View File
@@ -0,0 +1,162 @@
/**************************************************************************
*
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.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 "handlers.h"
/** @file h_alarm_sum.c Handles Get Alarm Summary request. */
static get_alarm_summary_function Get_Alarm_Summary[MAX_BACNET_OBJECT_TYPE];
void handler_get_alarm_summary_set(
BACNET_OBJECT_TYPE object_type,
get_alarm_summary_function pFunction)
{
if (object_type < MAX_BACNET_OBJECT_TYPE) {
Get_Alarm_Summary[object_type] = pFunction;
}
}
void handler_get_alarm_summary(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
int len = 0;
int pdu_len = 0;
int apdu_len = 0;
int bytes_sent = 0;
int alarm_value = 0;
unsigned i = 0;
unsigned j = 0;
bool error = false;
BACNET_ADDRESS my_address;
BACNET_NPDU_DATA npdu_data;
BACNET_GET_ALARM_SUMMARY_DATA getalarm_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 (service_data->segmented_message) {
/* we don't support segmentation - send an abort */
apdu_len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
true);
#if PRINT_ENABLED
fprintf(stderr,
"GetAlarmSummary: Segmented message. Sending Abort!\n");
#endif
goto GET_ALARM_SUMMARY_ABORT;
}
/* init header */
apdu_len =
get_alarm_summary_ack_encode_apdu_init(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id);
for (i = 0; i < MAX_BACNET_OBJECT_TYPE; i++) {
if (Get_Alarm_Summary[i]) {
for (j = 0; j < 0xffff; j++) {
alarm_value = Get_Alarm_Summary[i] (j, &getalarm_data);
if (alarm_value > 0) {
len =
get_alarm_summary_ack_encode_apdu_data
(&Handler_Transmit_Buffer[pdu_len + apdu_len],
service_data->max_resp - apdu_len, &getalarm_data);
if (len <= 0) {
error = true;
goto GET_ALARM_SUMMARY_ERROR;
} else
apdu_len += len;
} else if (alarm_value < 0) {
break;
}
}
}
}
#if PRINT_ENABLED
fprintf(stderr, "GetAlarmSummary: Sending response!\n");
#endif
GET_ALARM_SUMMARY_ERROR:
if (error) {
if (len == BACNET_STATUS_ABORT) {
/* BACnet APDU too small to fit data, so proper response is Abort */
apdu_len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
#if PRINT_ENABLED
fprintf(stderr,
"GetAlarmSummary: Reply too big to fit into APDU!\n");
#endif
} else {
apdu_len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_GET_ALARM_SUMMARY,
ERROR_CLASS_PROPERTY, ERROR_CODE_OTHER);
#if PRINT_ENABLED
fprintf(stderr, "GetAlarmSummary: Sending Error!\n");
#endif
}
}
GET_ALARM_SUMMARY_ABORT:
pdu_len += apdu_len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0) {
/*fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno)); */
}
#else
bytes_sent = bytes_sent;
#endif
return;
}
+245
View File
@@ -0,0 +1,245 @@
/**************************************************************************
*
* Copyright (C) 2009 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 <stdio.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 "event.h"
#include "getevent.h"
#include "handlers.h"
/** @file h_getevent.c Handles Get Event Information request. */
static get_event_info_function Get_Event_Info[MAX_BACNET_OBJECT_TYPE];
/** print eventState
*/
void ge_ack_print_data(
BACNET_GET_EVENT_INFORMATION_DATA * data,
uint32_t device_id)
{
BACNET_GET_EVENT_INFORMATION_DATA *act_data = data;
const char* state_strs[] = {"NO", "FA", "ON", "HL", "LL"};
printf("DeviceID\tType\tInstance\teventState\n");
printf("--------------- ------- --------------- ---------------\n");
int count = 0;
while (act_data) {
printf("%u\t\t%u\t%u\t\t%s\n",
device_id,
act_data->objectIdentifier.type,
act_data->objectIdentifier.instance,
state_strs[data->eventState]
);
act_data = act_data->next;
count++;
}
printf("\n%u\t Total\n",count);
}
void handler_get_event_information_set(
BACNET_OBJECT_TYPE object_type,
get_event_info_function pFunction)
{
if (object_type < MAX_BACNET_OBJECT_TYPE) {
Get_Event_Info[object_type] = pFunction;
}
}
void handler_get_event_information(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
int len = 0;
int pdu_len = 0;
int apdu_len = 0;
BACNET_NPDU_DATA npdu_data;
bool error = false;
bool more_events = 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;
BACNET_OBJECT_ID object_id;
unsigned i = 0, j = 0; /* counter */
BACNET_GET_EVENT_INFORMATION_DATA getevent_data;
int valid_event = 0;
/* initialize type of 'Last Received Object Identifier' using max value */
object_id.type = MAX_BACNET_OBJECT_TYPE;
/* 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 (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);
#if PRINT_ENABLED
fprintf(stderr,
"GetEventInformation: " "Segmented message. Sending Abort!\n");
#endif
goto GET_EVENT_ABORT;
}
len =
getevent_decode_service_request(service_request, service_len,
&object_id);
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);
#if PRINT_ENABLED
fprintf(stderr,
"GetEventInformation: Bad Encoding. Sending Abort!\n");
#endif
goto GET_EVENT_ABORT;
}
len =
getevent_ack_encode_apdu_init(&Handler_Transmit_Buffer[pdu_len],
sizeof(Handler_Transmit_Buffer) - pdu_len, service_data->invoke_id);
if (len <= 0) {
error = true;
goto GET_EVENT_ERROR;
}
pdu_len += len;
apdu_len = len;
for (i = 0; i < MAX_BACNET_OBJECT_TYPE; i++) {
if (Get_Event_Info[i]) {
for (j = 0; j < 0xffff; j++) {
valid_event = Get_Event_Info[i] (j, &getevent_data);
if (valid_event > 0) {
/* encode GetEvent_data only when type of object_id has max value */
if (object_id.type != MAX_BACNET_OBJECT_TYPE) {
if ((object_id.type ==
getevent_data.objectIdentifier.type) &&
(object_id.instance ==
getevent_data.objectIdentifier.instance)) {
/* found 'Last Received Object Identifier'
so should set type of object_id to max value */
object_id.type = MAX_BACNET_OBJECT_TYPE;
}
continue;
}
getevent_data.next = NULL;
len =
getevent_ack_encode_apdu_data(&Handler_Transmit_Buffer
[pdu_len], sizeof(Handler_Transmit_Buffer) - pdu_len,
&getevent_data);
if (len <= 0) {
error = true;
goto GET_EVENT_ERROR;
}
apdu_len += len;
if ((apdu_len >= service_data->max_resp - 2) ||
(apdu_len >= MAX_APDU - 2)) {
/* Device must be able to fit minimum
one event information.
Length of one event informations needs
more than 50 octets. */
if ((service_data->max_resp < 128) ||
(MAX_APDU < 128)) {
len = BACNET_STATUS_ABORT;
error = true;
goto GET_EVENT_ERROR;
} else {
more_events = true;
}
break;
} else {
pdu_len += len;
}
} else if (valid_event < 0) {
break;
}
}
}
}
len =
getevent_ack_encode_apdu_end(&Handler_Transmit_Buffer[pdu_len],
sizeof(Handler_Transmit_Buffer) - pdu_len, more_events);
if (len <= 0) {
error = true;
goto GET_EVENT_ERROR;
}
#if PRINT_ENABLED
fprintf(stderr, "Got a GetEventInformation request: Sending Ack!\n");
#endif
GET_EVENT_ERROR:
if (error) {
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address,
&npdu_data);
if (len == -2) {
/* BACnet APDU too small to fit data, so proper response is Abort */
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
#if PRINT_ENABLED
fprintf(stderr,
"GetEventInformation: " "Reply too big to fit into APDU!\n");
#endif
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY,
error_class, error_code);
#if PRINT_ENABLED
fprintf(stderr, "GetEventInformation: Sending Error!\n");
#endif
}
}
GET_EVENT_ABORT:
pdu_len += len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
#endif
return;
}
+87
View File
@@ -0,0 +1,87 @@
/**
* @file
* @author Daniel Blazevic <daniel.blazevic@gmail.com>
* @date 2014
* @brief GetEvent ACK service handling
*
* @section LICENSE
*
* Copyright (C) 2014 Daniel Blazevic <daniel.blazevic@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @section DESCRIPTION
*
* The GetEventInformation service ACK service handler is used by a client
* BACnet-user to obtain a summary of all "active event states". The term
* "active event states" refers to all event-initiating objects that have an
* Event_State property whose value is not equal to NORMAL, or have an
* Acked_Transitions property, which has at least one of the bits
* (TO-OFFNORMAL, TO-FAULT, TONORMAL) set to FALSE.
*/
#include <assert.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 "handlers.h"
#include "getevent.h"
/* 40 = min size of get event data in APDU */
#define MAX_NUMBER_OF_EVENTS ((MAX_APDU / 40) + 1)
/** Example function to handle a GetEvent ACK.
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_ACK_DATA information
* decoded from the APDU header of this message.
*/
void get_event_ack_handler(
uint8_t *service_request,
uint16_t service_len,
BACNET_ADDRESS *src,
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data)
{
uint8_t i = 0;
uint16_t apdu_len = 0;
bool more_events = false;
/* initialize array big enough to accommodate
multiple get event data in APDU */
BACNET_GET_EVENT_INFORMATION_DATA get_event_data[MAX_NUMBER_OF_EVENTS];
for (i = 1; i < MAX_NUMBER_OF_EVENTS; i++) {
/* Create linked list */
get_event_data[i - 1].next = &get_event_data[i];
}
apdu_len =
getevent_ack_decode_service_request(&service_request[0],
service_len, &get_event_data[0], &more_events);
if (apdu_len > 0) {
/* FIXME: Add code to process get_event_data */
}
}
+108
View File
@@ -0,0 +1,108 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <stdio.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "iam.h"
#include "address.h"
#include "handlers.h"
/** @file h_iam.c Handles I-Am requests. */
/** Handler for I-Am responses.
* Will add the responder to our cache, or update its binding.
* @ingroup DMDDB
* @param service_request [in] The received message to be handled.
* @param service_len [in] Length of the service_request message.
* @param src [in] The BACNET_ADDRESS of the message's source.
*/
void handler_i_am_add(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
int len = 0;
uint32_t device_id = 0;
unsigned max_apdu = 0;
int segmentation = 0;
uint16_t vendor_id = 0;
(void) service_len;
len =
iam_decode_service_request(service_request, &device_id, &max_apdu,
&segmentation, &vendor_id);
#if PRINT_ENABLED
fprintf(stderr, "Received I-Am Request");
#endif
if (len != -1) {
#if PRINT_ENABLED
fprintf(stderr, " from %lu, MAC = %d.%d.%d.%d.%d.%d\n",
(unsigned long) device_id, src->mac[0], src->mac[1], src->mac[2],
src->mac[3], src->mac[4], src->mac[5]);
#endif
address_add(device_id, max_apdu, src);
} else {
#if PRINT_ENABLED
fprintf(stderr, ", but unable to decode it.\n");
#endif
}
return;
}
/** Handler for I-Am responses (older binding-update-only version).
* Will update the responder's binding, but if already in our cache.
* @note This handler is deprecated, in favor of handler_i_am_add().
*
* @param service_request [in] The received message to be handled.
* @param service_len [in] Length of the service_request message.
* @param src [in] The BACNET_ADDRESS of the message's source.
*/
void handler_i_am_bind(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
int len = 0;
uint32_t device_id = 0;
unsigned max_apdu = 0;
int segmentation = 0;
uint16_t vendor_id = 0;
(void) service_len;
len =
iam_decode_service_request(service_request, &device_id, &max_apdu,
&segmentation, &vendor_id);
if (len > 0) {
/* only add address if requested to bind */
address_add_binding(device_id, max_apdu, src);
}
return;
}
+70
View File
@@ -0,0 +1,70 @@
/**************************************************************************
*
* 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 <stdio.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "bactext.h"
#include "ihave.h"
#include "handlers.h"
/** @file h_ihave.c Handles incoming I-Have messages. */
/** Simple Handler for I-Have responses (just validates response).
* @ingroup DMDOB
* @param service_request [in] The received message to be handled.
* @param service_len [in] Length of the service_request message.
* @param src [in] The BACNET_ADDRESS of the message's source.
*/
void handler_i_have(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
int len = 0;
BACNET_I_HAVE_DATA data;
(void) service_len;
(void) src;
len = ihave_decode_service_request(service_request, service_len, &data);
if (len != -1) {
#if PRINT_ENABLED
fprintf(stderr, "I-Have: %s %lu from %s %lu!\r\n",
bactext_object_type_name(data.object_id.type),
(unsigned long) data.object_id.instance,
bactext_object_type_name(data.device_id.type),
(unsigned long) data.device_id.instance);
#endif
} else {
#if PRINT_ENABLED
fprintf(stderr, "I-Have: received, but unable to decode!\n");
#endif
}
return;
}
+119
View File
@@ -0,0 +1,119 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "lso.h"
#include "handlers.h"
#include "device.h"
/** @file h_lso.c Handles BACnet Life Safey Operation messages. */
void handler_lso(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_LSO_DATA data;
int len = 0;
int pdu_len = 0;
BACNET_NPDU_DATA npdu_data;
int bytes_sent = 0;
BACNET_ADDRESS my_address;
/* 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 (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);
#if PRINT_ENABLED
fprintf(stderr, "LSO: Segmented message. Sending Abort!\n");
#endif
goto LSO_ABORT;
}
len = lso_decode_service_request(service_request, service_len, &data);
#if PRINT_ENABLED
if (len <= 0)
fprintf(stderr, "LSO: Unable to decode Request!\n");
#endif
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);
#if PRINT_ENABLED
fprintf(stderr, "LSO: Bad Encoding. Sending Abort!\n");
#endif
goto LSO_ABORT;
}
/*
** Process Life Safety Operation Here
*/
#if PRINT_ENABLED
fprintf(stderr,
"Life Safety Operation: Received operation %d from process id %lu for object %lu\n",
data.operation, (unsigned long) data.processId,
(unsigned long) data.targetObject.instance);
#endif
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION);
#if PRINT_ENABLED
fprintf(stderr, "Life Safety Operation: " "Sending Simple Ack!\n");
#endif
LSO_ABORT:
pdu_len += len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Life Safety Operation: " "Failed to send PDU (%s)!\n",
strerror(errno));
#endif
return;
}
+112
View File
@@ -0,0 +1,112 @@
/**************************************************************************
*
* Copyright (C) 2008 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 <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"
#include "handlers.h"
#include "client.h"
#if PRINT_ENABLED
#include <stdio.h>
#endif
/** @file h_npdu.c Handles messages at the NPDU level of the BACnet stack. */
/** Handler for the NPDU portion of a received packet.
* Aside from error-checking, if the NPDU doesn't contain routing info,
* this handler doesn't do much besides stepping over the NPDU header
* and passing the remaining bytes to the apdu_handler.
* @note The routing (except src) and NCPI information, including
* npdu_data->data_expecting_reply, are discarded.
* @see routing_npdu_handler
*
* @ingroup MISCHNDLR
*
* @param src [out] Returned with routing source information if the NPDU
* has any and if this points to non-null storage for it.
* If src->net and src->len are 0 on return, there is no
* routing source information.
* This src describes the original source of the message when
* it had to be routed to reach this BACnet Device, and this
* is passed down into the apdu_handler; however, I don't
* think this project's code has any use for the src info
* on return from this handler, since the response has
* already been sent via the apdu_handler.
* @param pdu [in] Buffer containing the NPDU and APDU of the received packet.
* @param pdu_len [in] The size of the received message in the pdu[] buffer.
*/
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 };
/* only handle the version that we know how to handle */
if (pdu[0] == BACNET_PROTOCOL_VERSION) {
apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data);
if (npdu_data.network_layer_message) {
/*FIXME: network layer message received! Handle it! */
#if PRINT_ENABLED
fprintf(stderr, "NPDU: Network Layer Message discarded!\n");
#endif
} else if ((apdu_offset > 0) && (apdu_offset <= pdu_len)) {
if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) {
/* only handle the version that we know how to handle */
/* and we are not a router, so ignore messages with
routing information cause they are not for us */
if ((dest.net == BACNET_BROADCAST_NETWORK) &&
((pdu[apdu_offset] & 0xF0) ==
PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) {
/* hack for 5.4.5.1 - IDLE */
/* ConfirmedBroadcastReceived */
/* then enter IDLE - ignore the PDU */
} else {
apdu_handler(src, &pdu[apdu_offset],
(uint16_t) (pdu_len - apdu_offset));
}
} else {
#if PRINT_ENABLED
printf("NPDU: DNET=%u. Discarded!\n", (unsigned) dest.net);
#endif
}
}
} else {
#if PRINT_ENABLED
printf("NPDU: BACnet Protocol Version=%u. Discarded!\n",
(unsigned) pdu[0]);
#endif
}
return;
}
+312
View File
@@ -0,0 +1,312 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
/*#include "arf.h" */
/* demo objects */
#if defined(BACFILE)
#include "bacfile.h"
#endif
#include "mydata.h"
#include "ptransfer.h"
#include "handlers.h"
/** @file h_pt.c Handles Confirmed Private Transfer requests. */
#define MYMAXSTR 32
#define MYMAXBLOCK 8
DATABLOCK MyData[MYMAXBLOCK];
uint8_t IOBufferPT[MAX_APDU]; /* Buffer for building response in */
static void ProcessPT(
BACNET_PRIVATE_TRANSFER_DATA * data)
{
int iLen; /* Index to current location in data */
char cBlockNumber;
uint32_t ulTemp;
int tag_len;
uint8_t tag_number;
uint32_t len_value_type;
BACNET_CHARACTER_STRING bsTemp;
iLen = 0;
/* Decode the block number */
tag_len =
decode_tag_number_and_value(&data->serviceParameters[iLen],
&tag_number, &len_value_type);
iLen += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
/* Bail out early if wrong type */
/* and signal unexpected error */
data->serviceParametersLen = 0;
return;
}
iLen +=
decode_unsigned(&data->serviceParameters[iLen], len_value_type,
&ulTemp);
cBlockNumber = (char) ulTemp;
if (cBlockNumber < MY_MAX_BLOCK) {
if (data->serviceNumber == MY_SVC_READ) {
/* Read Response is an unsigned int with
0 for success or a non 0 error code
For a successful read the 0 success
code is followed by the block number
and then the block contents which
consist of 2 unsigned ints (in 0 to 255
range as they are really chars) a single
precision real and a string which
will be up to 32 chars + a nul */
iLen = 0;
/* Signal success */
iLen += encode_application_unsigned(&IOBufferPT[iLen], MY_ERR_OK);
/* Followed by the block number */
iLen +=
encode_application_unsigned(&IOBufferPT[iLen], cBlockNumber);
/* And Then the block contents */
iLen +=
encode_application_unsigned(&IOBufferPT[iLen],
MyData[(int8_t) cBlockNumber].cMyByte1);
iLen +=
encode_application_unsigned(&IOBufferPT[iLen],
MyData[(int8_t) cBlockNumber].cMyByte2);
iLen +=
encode_application_real(&IOBufferPT[iLen],
MyData[(int8_t) cBlockNumber].fMyReal);
characterstring_init_ansi(&bsTemp,
(char *) MyData[(int8_t) cBlockNumber].sMyString);
iLen +=
encode_application_character_string(&IOBufferPT[iLen],
&bsTemp);
} else {
/* Write operation */
/* Write block consists of the block number
followed by the block contents as
described above for the read operation.
The returned result is an unsigned
response which is 0 for success and
a non 0 error code otherwise. */
tag_len =
decode_tag_number_and_value(&data->serviceParameters[iLen],
&tag_number, &len_value_type);
iLen += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
data->serviceParametersLen = 0;
return;
}
iLen +=
decode_unsigned(&data->serviceParameters[iLen], len_value_type,
&ulTemp);
MyData[(int8_t) cBlockNumber].cMyByte1 = (char) ulTemp;
tag_len =
decode_tag_number_and_value(&data->serviceParameters[iLen],
&tag_number, &len_value_type);
iLen += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
data->serviceParametersLen = 0;
return;
}
iLen +=
decode_unsigned(&data->serviceParameters[iLen], len_value_type,
&ulTemp);
MyData[(int8_t) cBlockNumber].cMyByte2 = (char) ulTemp;
tag_len =
decode_tag_number_and_value(&data->serviceParameters[iLen],
&tag_number, &len_value_type);
iLen += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_REAL) {
data->serviceParametersLen = 0;
return;
}
iLen +=
decode_real(&data->serviceParameters[iLen],
&MyData[(int8_t) cBlockNumber].fMyReal);
tag_len =
decode_tag_number_and_value(&data->serviceParameters[iLen],
&tag_number, &len_value_type);
iLen += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_CHARACTER_STRING) {
data->serviceParametersLen = 0;
return;
}
decode_character_string(&data->serviceParameters[iLen],
len_value_type, &bsTemp);
/* Only copy as much as we can accept */
strncpy((char *) MyData[(int8_t) cBlockNumber].sMyString,
characterstring_value(&bsTemp), MY_MAX_STR);
/* Make sure it is nul terminated */
MyData[(int8_t) cBlockNumber].sMyString[MY_MAX_STR] = '\0';
/* Signal success */
iLen = encode_application_unsigned(&IOBufferPT[0], MY_ERR_OK);
}
} else {
/* Signal bad index */
iLen = encode_application_unsigned(&IOBufferPT[0], MY_ERR_BAD_INDEX);
}
data->serviceParametersLen = iLen;
data->serviceParameters = IOBufferPT;
}
/*
* This is called when we receive a private transfer packet.
* We parse the data, send the private part for processing and then send the
* response which the application generates.
* If there are any BACnet level errors we send an error response from here.
* If there are any application level errors they will be packeged up in the
* response block which we send back to the originator of the request.
*
*/
void handler_conf_private_trans(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_PRIVATE_TRANSFER_DATA data;
int len;
int pdu_len;
bool error;
int bytes_sent;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
BACNET_ERROR_CLASS error_class;
BACNET_ERROR_CODE error_code;
len = 0;
pdu_len = 0;
error = false;
bytes_sent = 0;
error_class = ERROR_CLASS_OBJECT;
error_code = ERROR_CODE_UNKNOWN_OBJECT;
#if PRINT_ENABLED
fprintf(stderr, "Received Confirmed Private Transfer Request!\n");
#endif
/* encode the NPDU portion of the response packet as it will be needed */
/* no matter what the outcome. */
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 (service_data->segmented_message) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
true);
#if PRINT_ENABLED
fprintf(stderr, "CPT: Segmented Message. Sending Abort!\n");
#endif
goto CPT_ABORT;
}
len =
ptransfer_decode_service_request(service_request, service_len, &data);
/* bad decoding - send an abort */
if (len < 0) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr, "CPT: Bad Encoding. Sending Abort!\n");
#endif
goto CPT_ABORT;
}
/* Simple example with service number of 0 for
read block and 1 for write block
We also only support our own vendor ID.
In theory we could support others
for compatability purposes but these
interfaces are rarely documented... */
if ((data.vendorID == BACNET_VENDOR_ID) &&
(data.serviceNumber <= MY_SVC_WRITE)) {
/* We only try to understand our own IDs and service numbers */
/* Will either return a result block or an app level status block */
ProcessPT(&data);
if (data.serviceParametersLen == 0) {
/* No respopnse means fatal error */
error = true;
error_class = ERROR_CLASS_SERVICES;
error_code = ERROR_CODE_OTHER;
#if PRINT_ENABLED
fprintf(stderr, "CPT: Error servicing request!\n");
#endif
}
len =
ptransfer_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, &data);
} else { /* Not our vendor ID or bad service parameter */
error = true;
error_class = ERROR_CLASS_SERVICES;
error_code = ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED;
#if PRINT_ENABLED
fprintf(stderr, "CPT: Not our Vendor ID or invalid service code!\n");
#endif
}
if (error) {
len =
ptransfer_error_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, error_class, error_code, &data);
}
CPT_ABORT:
pdu_len += len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0) {
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
}
#endif
return;
}
+240
View File
@@ -0,0 +1,240 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
/*#include "arf.h" */
/* demo objects */
#include "ptransfer.h"
#include "mydata.h"
#if defined(BACFILE)
#include "bacfile.h"
#endif
#include "handlers.h"
/** @file h_pt_a.c Handles Confirmed Private Transfer Acknowledgment. */
extern uint8_t IOBufferPT[300]; /* Somewhere to build the encoded result block for Private Transfers */
static void DecodeBlock(
char cBlockNum,
uint8_t * pData)
{
int iLen;
uint32_t ulTemp;
int tag_len;
uint8_t tag_number;
uint32_t len_value_type;
BACNET_CHARACTER_STRING bsName;
DATABLOCK Response;
iLen = 0;
if (cBlockNum >= MY_MAX_BLOCK)
return;
tag_len =
decode_tag_number_and_value(&pData[iLen], &tag_number,
&len_value_type);
iLen += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
return;
iLen += decode_unsigned(&pData[iLen], len_value_type, &ulTemp);
Response.cMyByte1 = (char) ulTemp;
tag_len =
decode_tag_number_and_value(&pData[iLen], &tag_number,
&len_value_type);
iLen += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
return;
iLen += decode_unsigned(&pData[iLen], len_value_type, &ulTemp);
Response.cMyByte2 = (char) ulTemp;
tag_len =
decode_tag_number_and_value(&pData[iLen], &tag_number,
&len_value_type);
iLen += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_REAL)
return;
iLen += decode_real(&pData[iLen], &Response.fMyReal);
tag_len =
decode_tag_number_and_value(&pData[iLen], &tag_number,
&len_value_type);
iLen += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_CHARACTER_STRING)
return;
iLen += decode_character_string(&pData[iLen], len_value_type, &bsName);
strncpy((char *) Response.sMyString, characterstring_value(&bsName),
MY_MAX_STR);
Response.sMyString[MY_MAX_STR] = '\0'; /* Make sure it is nul terminated */
printf("Private Transfer Read Block Response\n");
printf("Data Block: %d\n", (int) cBlockNum);
printf(" First Byte : %d\n", (int) Response.cMyByte1);
printf(" Second Byte : %d\n", (int) Response.cMyByte2);
printf(" Real : %f\n", Response.fMyReal);
printf(" String : %s\n\n", Response.sMyString);
}
static void ProcessPTA(
BACNET_PRIVATE_TRANSFER_DATA * data)
{
int iLen; /* Index to current location in data */
uint32_t uiErrorCode;
char cBlockNumber;
uint32_t ulTemp;
int tag_len;
uint8_t tag_number;
uint32_t len_value_type;
iLen = 0;
/* Error code is returned for read and write operations */
tag_len =
decode_tag_number_and_value(&data->serviceParameters[iLen],
&tag_number, &len_value_type);
iLen += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
#if PRINT_ENABLED
printf("CPTA: Bad Encoding!\n");
#endif
return;
}
iLen +=
decode_unsigned(&data->serviceParameters[iLen], len_value_type,
&uiErrorCode);
if (data->serviceNumber == MY_SVC_READ) { /* Read I/O block so should be full block of data or error */
/* Decode the error type and if necessary block number and then fetch the info */
if (uiErrorCode == MY_ERR_OK) {
/* Block Number */
tag_len =
decode_tag_number_and_value(&data->serviceParameters[iLen],
&tag_number, &len_value_type);
iLen += tag_len;
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
#if PRINT_ENABLED
printf("CPTA: Bad Encoding!\n");
#endif
return;
}
iLen +=
decode_unsigned(&data->serviceParameters[iLen], len_value_type,
&ulTemp);
cBlockNumber = (char) ulTemp;
DecodeBlock(cBlockNumber, &data->serviceParameters[iLen]);
} else { /* Read error */
printf
("Private Transfer read operation returned error code: %lu\n",
(unsigned long) uiErrorCode);
return;
}
} else { /* Write I/O block - should just be an OK type message */
printf("Private Transfer write operation returned error code: %lu\n",
(unsigned long) uiErrorCode);
}
}
/*
* This is called when we receive a private transfer packet ack.
* We parse the response which the remote application generated
* and decide what to do next...
*/
void handler_conf_private_trans_ack(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
{
BACNET_PRIVATE_TRANSFER_DATA data;
int len;
/*
* Note:
* We currently don't look at the source address and service data
* but we probably should to verify that the ack is oneit is what
* we were expecting. But this is just to silence some compiler
* warnings from Borland.
*/
src = src;
service_data = service_data;
len = 0;
#if PRINT_ENABLED
printf("Received Confirmed Private Transfer Ack!\n");
#endif
len = ptransfer_decode_service_request(service_request, service_len, &data); /* Same decode for ack as for service request! */
if (len < 0) {
#if PRINT_ENABLED
printf("cpta: Bad Encoding!\n");
#endif
}
ProcessPTA(&data); /* See what to do with the response */
return;
}
#if 0
void PTErrorHandler(
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("BACnet Error: %s: %s\r\n",
bactext_error_class_name((int) error_class),
bactext_error_code_name((int) error_code));
Error_Detected = true;
}
#endif
+173
View File
@@ -0,0 +1,173 @@
/**************************************************************************
*
* 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 <stdio.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 "reject.h"
#include "rd.h"
/* custom handling in device object */
#include "device.h"
#include "handlers.h"
/** @file h_rd.c Handles Reinitialize Device requests. */
/** Handler for a Reinitialize Device (RD) request.
* @ingroup DMRD
* This handler will be invoked by apdu_handler() if it has been enabled
* by a call to apdu_set_confirmed_handler().
* This handler builds a response packet, which is
* - an Abort if
* - the message is segmented
* - if decoding fails
* - an Error if
* - the RD password is incorrect
* - the Reinitialize Device operation fails
* - a Reject if the request state is invalid
* - else tries to send a simple ACK for the RD on success.
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
void handler_reinitialize_device(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_REINITIALIZE_DEVICE_DATA rd_data;
int len = 0;
int pdu_len = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
/* 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 PRINT_ENABLED
fprintf(stderr, "ReinitializeDevice!\n");
#endif
if (service_data->segmented_message) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
true);
#if PRINT_ENABLED
fprintf(stderr,
"ReinitializeDevice: Sending Abort - segmented message.\n");
#endif
goto RD_ABORT;
}
/* decode the service request only */
len =
rd_decode_service_request(service_request, service_len, &rd_data.state,
&rd_data.password);
#if PRINT_ENABLED
if (len > 0) {
fprintf(stderr, "ReinitializeDevice: state=%u password=%s\n",
(unsigned) rd_data.state,
characterstring_value(&rd_data.password));
} else {
fprintf(stderr, "ReinitializeDevice: Unable to decode request!\n");
}
#endif
/* bad decoding or something we didn't understand - send an abort */
if (len < 0) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr,
"ReinitializeDevice: Sending Abort - could not decode.\n");
#endif
goto RD_ABORT;
}
/* check the data from the request */
if (rd_data.state >= BACNET_REINIT_MAX) {
len =
reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, REJECT_REASON_UNDEFINED_ENUMERATION);
#if PRINT_ENABLED
fprintf(stderr,
"ReinitializeDevice: Sending Reject - undefined enumeration\n");
#endif
} else {
#if BAC_ROUTING
/* Check to see if the current Device supports this service. */
len =
Routed_Device_Service_Approval
(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, (int) rd_data.state,
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id);
if (len > 0)
goto RD_ABORT;
#endif
if (Device_Reinitialize(&rd_data)) {
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_REINITIALIZE_DEVICE);
#if PRINT_ENABLED
fprintf(stderr, "ReinitializeDevice: Sending Simple Ack!\n");
#endif
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_REINITIALIZE_DEVICE,
rd_data.error_class, rd_data.error_code);
#if PRINT_ENABLED
fprintf(stderr, "ReinitializeDevice: Sending Error.\n");
#endif
}
}
RD_ABORT:
pdu_len += len;
len =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
if (len <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "ReinitializeDevice: Failed to send PDU (%s)!\n",
strerror(errno));
#endif
}
return;
}
+306
View File
@@ -0,0 +1,306 @@
/**************************************************************************
*
* Copyright (C) 2010 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.
*
*********************************************************************/
/* Acknowledging the contribution of code and ideas used here that
* came from Paul Chapman's vmac demo project. */
#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"
#include "handlers.h"
#include "device.h"
#include "client.h"
#include "bactext.h"
#include "debug.h"
#if PRINT_ENABLED
#include <stdio.h>
#endif
#if defined(BACDL_BIP)
#include "bvlc.h"
#endif
/** @file h_routed_npdu.c Handles messages at the NPDU level of the BACnet stack,
* including routing and network control messages. */
/** Handler to manage the Network Layer Control Messages received in a packet.
* This handler is called if the NCPI bit 7 indicates that this packet is a
* network layer message and there is no further DNET to pass it to.
* The NCPI has already been decoded into the npdu_data structure.
* @ingroup MISCHNDLR
*
* @param src [in] The routing source information, if any.
* If src->net and src->len are 0, there is no
* routing source information.
* @param DNET_list [in] List of our reachable downstream BACnet Network numbers.
* Normally just one valid entry; terminated with a -1 value.
* @param npdu_data [in] Contains a filled-out structure with information
* decoded from the NCPI and other NPDU bytes.
* @param npdu [in] Buffer containing the rest of the NPDU, following the
* bytes that have already been decoded.
* @param npdu_len [in] The length of the remaining NPDU message in npdu[].
*/
static void network_control_handler(
BACNET_ADDRESS * src,
int *DNET_list,
BACNET_NPDU_DATA * npdu_data,
uint8_t * npdu,
uint16_t npdu_len)
{
uint16_t npdu_offset = 0;
uint16_t dnet = 0;
uint16_t len = 0;
switch (npdu_data->network_message_type) {
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
/* Send I-am-router-to-network with our one-network list if
* our specific network is requested, or no specific
* network is requested. Silently drop other DNET requests.
*/
if (npdu_len >= 2) {
uint16_t network;
len += decode_unsigned16(&npdu[len], &network);
if (network == DNET_list[0]) {
Send_I_Am_Router_To_Network(DNET_list);
}
} else {
Send_I_Am_Router_To_Network(DNET_list);
}
break;
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
/* Per the standard, we are supposed to process this message and
* add its DNETs to our routing table.
* However, since we only have one upstream port that these
* messages can come from and replies go to, it doesn't seem
* to provide us any value to do this; when we need to send to
* some remote device, we will start by pushing it out the
* upstream port and let the attached router(s) take it from there.
* Consequently, we'll do nothing interesting here.
* -- Unless we act upon NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK
* later for congestion control - then it could matter.
*/
debug_printf("%s for Networks: ",
bactext_network_layer_msg_name
(NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK));
while (npdu_len >= 2) {
len = decode_unsigned16(&npdu[npdu_offset], &dnet);
debug_printf("%hu", dnet);
npdu_len -= len;
npdu_offset += len;
if (npdu_len >= 2) {
debug_printf(", ");
}
}
debug_printf("\n");
break;
case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
/* Do nothing, same as previous case. */
break;
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK:
if (npdu_len >= 3) {
decode_unsigned16(&npdu[1], &dnet);
debug_printf("Received %s for Network: ",
bactext_network_layer_msg_name
(NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK));
debug_printf("%hu, Reason code: %d \n", dnet, npdu[0]);
}
break;
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
/* Do nothing - don't support upstream traffic congestion control */
break;
case NETWORK_MESSAGE_INIT_RT_TABLE:
/* If sent with Number of Ports == 0, we respond with
* NETWORK_MESSAGE_INIT_RT_TABLE_ACK and a list of all our
* reachable networks.
*/
if (npdu_len > 0) {
/* If Number of Ports is 0, broadcast our "full" table */
if (npdu[0] == 0)
Send_Initialize_Routing_Table_Ack(NULL, DNET_list);
else {
/* If they sent us a list, just politely ACK it
* with no routing list of our own. But we don't DO
* anything with the info, either.
*/
int listTerminator = -1;
Send_Initialize_Routing_Table_Ack(src, &listTerminator);
}
break;
}
/* Else, fall through to do nothing. */
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
/* Do nothing with the routing table info, since don't support
* upstream traffic congestion control */
break;
case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK:
case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK:
/* Do nothing - don't support PTP half-router control */
break;
default:
/* An unrecognized message is bad; send an error response. */
Send_Reject_Message_To_Network(src,
NETWORK_REJECT_UNKNOWN_MESSAGE_TYPE, DNET_list[0]);
/* Sending our DNET doesn't make a lot of sense, does it? */
break;
}
}
/** An APDU pre-handler that makes sure that the subsequent APDU handler call
* operates on the right Device Object(s), as addressed by the destination
* (routing) information.
*
* @note Even when the destination is "routed" to our virtual BACnet network,
* the src information does not need to change to reflect that (as it normally
* would for a routed message) because the reply will be sent from the level
* of the gateway Device.
*
* @param src [in] The BACNET_ADDRESS of the message's source.
* @param dest [in] The BACNET_ADDRESS of the message's destination.
* @param DNET_list [in] List of our reachable downstream BACnet Network numbers.
* Normally just one valid entry; terminated with a -1 value.
* @param apdu [in] The apdu portion of the request, to be processed.
* @param apdu_len [in] The total (remaining) length of the apdu.
*/
static void routed_apdu_handler(
BACNET_ADDRESS * src,
BACNET_ADDRESS * dest,
int *DNET_list,
uint8_t * apdu,
uint16_t apdu_len)
{
int cursor = 0; /* Starting hint */
bool bGotOne = false;
if (!Routed_Device_Is_Valid_Network(dest->net, DNET_list)) {
/* We don't know how to reach this one.
* The protocol doesn't specifically state this, but if this message
* was broadcast to us, we should assume "someone else" is handling
* it and not get involved (ie, send a Reject-Message).
* Since we can't reach other routers that src couldn't already reach,
* we don't try the standard path of asking Who-Is-Router-to-Network. */
#if defined(BACDL_BIP)
/* If wasn't unicast to us, must have been one of the bcast types.
* Drop it. */
if (bvlc_get_function_code() != BVLC_ORIGINAL_UNICAST_NPDU)
return;
#endif
/* Upper level handlers knew that this was sent as a bcast,
* but our only other way to guess at that here is if the dest->adr
* is absent, then we know this is some sort of bcast.
*/
if (dest->len > 0) {
Send_Reject_Message_To_Network(src, NETWORK_REJECT_NO_ROUTE,
dest->net);
} /* else, silently drop it */
return;
}
while (Routed_Device_GetNext(dest, DNET_list, &cursor)) {
apdu_handler(src, apdu, apdu_len);
bGotOne = true;
if (cursor < 0) /* If no more matches, */
break; /* We don't need to keep looking */
}
if (!bGotOne) {
/* Just silently drop this packet. */
}
}
/** Handler for the NPDU portion of a received packet, which may have routing.
* This is a fuller handler than the regular npdu_handler, as it manages
* - Decoding of the NCPI byte
* - Further processing by network_control_handler() if this is a network
* layer message.
* - Further processing by routed_apdu_handler() if it contains an APDU
* - Normally (no routing) by apdu_handler()
* - With Routing (a further destination was indicated) by the decoded
* destination.
* - Errors in decoding.
* @note The npdu_data->data_expecting_reply status is discarded.
* @see npdu_handler
* @ingroup NMRC
*
* @param src [out] Returned with routing source information if the NPDU
* has any and if this points to non-null storage for it.
* If src->net and src->len are 0 on return, there is no
* routing source information.
* This src describes the original source of the message when
* it had to be routed to reach this BACnet Device, and this
* is passed down into the apdu_handler; however, I don't
* think this project's code has any use for the src info
* on return from this handler, since the response has
* already been sent via the apdu_handler.
* @param DNET_list [in] List of our reachable downstream BACnet Network numbers.
* Normally just one valid entry; terminated with a -1 value.
* @param pdu [in] Buffer containing the NPDU and APDU of the received packet.
* @param pdu_len [in] The size of the received message in the pdu[] buffer.
*/
void routing_npdu_handler(
BACNET_ADDRESS * src,
int *DNET_list,
uint8_t * pdu,
uint16_t pdu_len)
{
int apdu_offset = 0;
BACNET_ADDRESS dest = { 0 };
BACNET_NPDU_DATA npdu_data = { 0 };
/* only handle the version that we know how to handle */
if (pdu[0] == BACNET_PROTOCOL_VERSION) {
apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data);
if (apdu_offset <= 0) {
debug_printf("NPDU: Decoding failed; Discarded!\n");
} else if (npdu_data.network_layer_message) {
if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) {
network_control_handler(src, DNET_list, &npdu_data,
&pdu[apdu_offset], (uint16_t) (pdu_len - apdu_offset));
} else {
/* The DNET is set, but we don't support downstream routers,
* so we just silently drop this network layer message,
* since only routers can handle it (even if for our DNET) */
}
} else if (apdu_offset <= pdu_len) {
if ((dest.net == 0) || (npdu_data.hop_count > 1))
routed_apdu_handler(src, &dest, DNET_list, &pdu[apdu_offset],
(uint16_t) (pdu_len - apdu_offset));
/* Else, hop_count bottomed out and we discard this one. */
}
} else {
/* Should we send NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK? */
debug_printf
("NPDU: Unsupported BACnet Protocol Version=%u. Discarded!\n",
(unsigned) pdu[0]);
}
return;
}
+203
View File
@@ -0,0 +1,203 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "bacerror.h"
#include "bacdevobjpropref.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "reject.h"
#include "rp.h"
/* device object has custom handler for all objects */
#include "device.h"
#include "handlers.h"
/** @file h_rp.c Handles Read Property requests. */
/** Handler for a ReadProperty Service request.
* @ingroup DSRP
* This handler will be invoked by apdu_handler() if it has been enabled
* by a call to apdu_set_confirmed_handler().
* This handler builds a response packet, which is
* - an Abort if
* - the message is segmented
* - if decoding fails
* - if the response would be too large
* - the result from Device_Read_Property(), if it succeeds
* - an Error if Device_Read_Property() fails
* or there isn't enough room in the APDU to fit the data.
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
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 rpdata;
int len = 0;
int pdu_len = 0;
int apdu_len = -1;
int npdu_len = -1;
BACNET_NPDU_DATA npdu_data;
bool error = true; /* assume that there is an error */
int bytes_sent = 0;
BACNET_ADDRESS my_address;
/* configure default error code as an abort since it is common */
rpdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
npdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address,
&npdu_data);
if (service_data->segmented_message) {
/* we don't support segmentation - send an abort */
len = BACNET_STATUS_ABORT;
#if PRINT_ENABLED
fprintf(stderr, "RP: Segmented message. Sending Abort!\n");
#endif
goto RP_FAILURE;
}
len = rp_decode_service_request(service_request, service_len, &rpdata);
#if PRINT_ENABLED
if (len <= 0) {
fprintf(stderr, "RP: Unable to decode Request!\n");
}
#endif
if (len < 0) {
/* bad decoding - skip to error/reject/abort handling */
error = true;
#if PRINT_ENABLED
fprintf(stderr, "RP: Bad Encoding.\n");
#endif
goto RP_FAILURE;
}
/* Test for case of indefinite Device object instance */
if ((rpdata.object_type == OBJECT_DEVICE) &&
(rpdata.object_instance == BACNET_MAX_INSTANCE)) {
rpdata.object_instance = Device_Object_Instance_Number();
}
apdu_len =
rp_ack_encode_apdu_init(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id, &rpdata);
/* configure our storage */
rpdata.application_data = &Handler_Transmit_Buffer[npdu_len + apdu_len];
rpdata.application_data_len =
sizeof(Handler_Transmit_Buffer) - (npdu_len + apdu_len);
len = Device_Read_Property(&rpdata);
if (len >= 0) {
apdu_len += len;
len =
rp_ack_encode_apdu_object_property_end(&Handler_Transmit_Buffer
[npdu_len + apdu_len]);
apdu_len += len;
if (apdu_len > service_data->max_resp) {
/* too big for the sender - send an abort
* Setting of error code needed here as read property processing may
* have overriden the default set at start */
rpdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
len = BACNET_STATUS_ABORT;
#if PRINT_ENABLED
fprintf(stderr, "RP: Message too large.\n");
#endif
} else {
#if PRINT_ENABLED
fprintf(stderr, "RP: Sending Ack!\n");
#endif
error = false;
}
} else {
#if PRINT_ENABLED
fprintf(stderr, "RP: Device_Read_Property: ");
if (len == BACNET_STATUS_ABORT) {
fprintf(stderr, "Abort!\n");
} else if (len == BACNET_STATUS_ERROR) {
fprintf(stderr, "Error!\n");
} else if (len == BACNET_STATUS_REJECT) {
fprintf(stderr, "Reject!\n");
} else {
fprintf(stderr, "Unknown Len=%d\n", len);
}
#endif
}
RP_FAILURE:
if (error) {
if (len == BACNET_STATUS_ABORT) {
apdu_len =
abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
abort_convert_error_code(rpdata.error_code), true);
#if PRINT_ENABLED
fprintf(stderr, "RP: Sending Abort!\n");
#endif
} else if (len == BACNET_STATUS_ERROR) {
apdu_len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY,
rpdata.error_class, rpdata.error_code);
#if PRINT_ENABLED
fprintf(stderr, "RP: Sending Error!\n");
#endif
} else if (len == BACNET_STATUS_REJECT) {
apdu_len =
reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
reject_convert_error_code(rpdata.error_code));
#if PRINT_ENABLED
fprintf(stderr, "RP: Sending Reject!\n");
#endif
}
}
pdu_len = npdu_len + apdu_len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
#endif
}
return;
}
+241
View File
@@ -0,0 +1,241 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <stdlib.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "bactext.h"
#include "rp.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
/** @file h_rp_a.c Handles Read Property Acknowledgments. */
/** For debugging...
* @param [in] data portion of the ACK
*/
void rp_ack_print_data(
BACNET_READ_PROPERTY_DATA * data)
{
BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */
BACNET_APPLICATION_DATA_VALUE value; /* for decode value data */
int len = 0;
uint8_t *application_data;
int application_data_len;
bool first_value = true;
bool print_brace = false;
if (data) {
application_data = data->application_data;
application_data_len = data->application_data_len;
/* FIXME: what if application_data_len is bigger than 255? */
/* value? need to loop until all of the len is gone... */
for (;;) {
len =
bacapp_decode_application_data(application_data,
(uint8_t) application_data_len, &value);
if (first_value && (len < application_data_len)) {
first_value = false;
#if PRINT_ENABLED
fprintf(stdout, "{");
#endif
print_brace = true;
}
object_value.object_type = data->object_type;
object_value.object_instance = data->object_instance;
object_value.object_property = data->object_property;
object_value.array_index = data->array_index;
object_value.value = &value;
bacapp_print_value(stdout, &object_value);
if (len > 0) {
if (len < application_data_len) {
application_data += len;
application_data_len -= len;
/* there's more! */
#if PRINT_ENABLED
fprintf(stdout, ",");
#endif
} else {
break;
}
} else {
break;
}
}
#if PRINT_ENABLED
if (print_brace)
fprintf(stdout, "}");
fprintf(stdout, "\r\n");
#endif
}
}
/** Handler for a ReadProperty ACK.
* @ingroup DSRP
* Doesn't actually do anything, except, for debugging, to
* print out the ACK message.
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
void handler_read_property_ack(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
{
int len = 0;
BACNET_READ_PROPERTY_DATA data;
(void) src;
(void) service_data; /* we could use these... */
len = rp_ack_decode_service_request(service_request, service_len, &data);
#if 0
fprintf(stderr, "Received Read-Property Ack!\n");
#endif
if (len > 0)
rp_ack_print_data(&data);
}
/** Decode the received RP data into a linked list of the results, with the
* same data structure used by RPM ACK replies.
* This function is provided to provide common handling for RP and RPM data,
* and fully decodes the value(s) portion of the data for one property.
* @ingroup DSRP
* @see rp_ack_decode_service_request(), rpm_ack_decode_service_request()
*
* @param apdu [in] The received apdu data.
* @param apdu_len [in] Total length of the apdu.
* @param read_access_data [out] Pointer to the head of the linked list
* where the RP data is to be stored.
* @return Number of decoded bytes (could be less than apdu_len),
* or -1 on decoding error.
*/
int rp_ack_fully_decode_service_request(
uint8_t * apdu,
int apdu_len,
BACNET_READ_ACCESS_DATA * read_access_data)
{
int decoded_len = 0; /* return value */
BACNET_READ_PROPERTY_DATA rp1data;
BACNET_PROPERTY_REFERENCE *rp1_property; /* single property */
BACNET_APPLICATION_DATA_VALUE *value, *old_value;
uint8_t *vdata;
int vlen, len;
decoded_len = rp_ack_decode_service_request(apdu, apdu_len, &rp1data);
if (decoded_len > 0) {
/* Then we have to transfer to the BACNET_READ_ACCESS_DATA structure
* and decode the value(s) portion
*/
read_access_data->object_type = rp1data.object_type;
read_access_data->object_instance = rp1data.object_instance;
rp1_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE));
read_access_data->listOfProperties = rp1_property;
if (rp1_property == NULL) {
/* can't proceed if calloc failed. */
return BACNET_STATUS_ERROR;
}
rp1_property->propertyIdentifier = rp1data.object_property;
rp1_property->propertyArrayIndex = rp1data.array_index;
/* Is there no Error case possible here, as there is when decoding RPM? */
/* rp1_property->error.error_class = ?? */
/* rp_ack_decode_service_request() processing already removed the
* Opening and Closing '3' Tags.
* note: if this is an array, there will be
more than one element to decode */
vdata = rp1data.application_data;
vlen = rp1data.application_data_len;
value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE));
rp1_property->value = value;
old_value = value;
while (value && vdata && (vlen > 0)) {
if (IS_CONTEXT_SPECIFIC(*vdata)) {
len =
bacapp_decode_context_data(vdata, vlen, value,
rp1_property->propertyIdentifier);
} else {
len = bacapp_decode_application_data(vdata, vlen, value);
}
if (len < 0) {
/* unable to decode the data */
while (value) {
/* free the linked list of values */
old_value = value;
value = value->next;
free(old_value);
}
free(rp1_property);
read_access_data->listOfProperties = NULL;
return len;
}
decoded_len += len;
vlen -= len;
vdata += len;
/* If unexpected closing tag here: */
if (vlen && decode_is_closing_tag_number(vdata, 3)) {
decoded_len++;
vlen--;
vdata++;
break;
} else {
if (len == 0) {
/* nothing decoded and no closing tag, so malformed */
while (value) {
/* free the linked list of values */
old_value = value;
value = value->next;
free(old_value);
}
free(rp1_property);
read_access_data->listOfProperties = NULL;
return BACNET_STATUS_ERROR;
}
if (vlen > 0) {
/* If more values */
old_value = value;
value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE));
old_value->next = value;
}
}
}
}
return decoded_len;
}
+452
View File
@@ -0,0 +1,452 @@
/**************************************************************************
*
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
* Inspired by John Stachler <John.Stachler@lennoxind.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "memcopy.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "reject.h"
#include "bacerror.h"
#include "rpm.h"
#include "handlers.h"
/* device object has custom handler for all objects */
#include "device.h"
/** @file h_rpm.c Handles Read Property Multiple requests. */
static uint8_t Temp_Buf[MAX_APDU] = { 0 };
static BACNET_PROPERTY_ID RPM_Object_Property(
struct special_property_list_t *pPropertyList,
BACNET_PROPERTY_ID special_property,
unsigned index)
{
int property = -1; /* return value */
unsigned required, optional, proprietary;
required = pPropertyList->Required.count;
optional = pPropertyList->Optional.count;
proprietary = pPropertyList->Proprietary.count;
if (special_property == PROP_ALL) {
if (index < required) {
property = pPropertyList->Required.pList[index];
} else if (index < (required + optional)) {
index -= required;
property = pPropertyList->Optional.pList[index];
} else if (index < (required + optional + proprietary)) {
index -= (required + optional);
property = pPropertyList->Proprietary.pList[index];
}
} else if (special_property == PROP_REQUIRED) {
if (index < required) {
property = pPropertyList->Required.pList[index];
}
} else if (special_property == PROP_OPTIONAL) {
if (index < optional) {
property = pPropertyList->Optional.pList[index];
}
}
return (BACNET_PROPERTY_ID) property;
}
static unsigned RPM_Object_Property_Count(
struct special_property_list_t *pPropertyList,
BACNET_PROPERTY_ID special_property)
{
unsigned count = 0; /* return value */
if (special_property == PROP_ALL) {
count =
pPropertyList->Required.count + pPropertyList->Optional.count +
pPropertyList->Proprietary.count;
} else if (special_property == PROP_REQUIRED) {
count = pPropertyList->Required.count;
} else if (special_property == PROP_OPTIONAL) {
count = pPropertyList->Optional.count;
}
return count;
}
/** Encode the RPM property returning the length of the encoding,
or 0 if there is no room to fit the encoding. */
static int RPM_Encode_Property(
uint8_t * apdu,
uint16_t offset,
uint16_t max_apdu,
BACNET_RPM_DATA * rpmdata)
{
int len = 0;
size_t copy_len = 0;
int apdu_len = 0;
BACNET_READ_PROPERTY_DATA rpdata;
len =
rpm_ack_encode_apdu_object_property(&Temp_Buf[0],
rpmdata->object_property, rpmdata->array_index);
copy_len = memcopy(&apdu[0], &Temp_Buf[0], offset, len, max_apdu);
if (copy_len == 0) {
rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
return BACNET_STATUS_ABORT;
}
apdu_len += len;
len = 0;
rpdata.error_class = ERROR_CLASS_OBJECT;
rpdata.error_code = ERROR_CODE_UNKNOWN_OBJECT;
rpdata.object_type = rpmdata->object_type;
rpdata.object_instance = rpmdata->object_instance;
rpdata.object_property = rpmdata->object_property;
rpdata.array_index = rpmdata->array_index;
rpdata.application_data = &Temp_Buf[0];
rpdata.application_data_len = sizeof(Temp_Buf);
len = Device_Read_Property(&rpdata);
if (len < 0) {
if ((len == BACNET_STATUS_ABORT) || (len == BACNET_STATUS_REJECT)) {
rpmdata->error_code = rpdata.error_code;
/* pass along aborts and rejects for now */
return len; /* Ie, Abort */
}
/* error was returned - encode that for the response */
len =
rpm_ack_encode_apdu_object_property_error(&Temp_Buf[0],
rpdata.error_class, rpdata.error_code);
copy_len =
memcopy(&apdu[0], &Temp_Buf[0], offset + apdu_len, len, max_apdu);
if (copy_len == 0) {
rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
return BACNET_STATUS_ABORT;
}
} else if ((offset + apdu_len + 1 + len + 1) < max_apdu) {
/* enough room to fit the property value and tags */
len =
rpm_ack_encode_apdu_object_property_value(&apdu[offset + apdu_len],
&Temp_Buf[0], len);
} else {
/* not enough room - abort! */
rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
return BACNET_STATUS_ABORT;
}
apdu_len += len;
return apdu_len;
}
/** Handler for a ReadPropertyMultiple Service request.
* @ingroup DSRPM
* This handler will be invoked by apdu_handler() if it has been enabled
* by a call to apdu_set_confirmed_handler().
* This handler builds a response packet, which is
* - an Abort if
* - the message is segmented
* - if decoding fails
* - if the response would be too large
* - the result from each included read request, if it succeeds
* - an Error if processing fails for all, or individual errors if only some fail,
* or there isn't enough room in the APDU to fit the data.
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
void handler_read_property_multiple(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
int len = 0;
uint16_t copy_len = 0;
uint16_t decode_len = 0;
int pdu_len = 0;
BACNET_NPDU_DATA npdu_data;
int bytes_sent;
BACNET_ADDRESS my_address;
BACNET_RPM_DATA rpmdata;
int apdu_len = 0;
int npdu_len = 0;
int error = 0;
/* jps_debug - see if we are utilizing all the buffer */
/* memset(&Handler_Transmit_Buffer[0], 0xff, sizeof(Handler_Transmit_Buffer)); */
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
npdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address,
&npdu_data);
if (service_data->segmented_message) {
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
#if PRINT_ENABLED
fprintf(stderr, "RPM: Segmented message. Sending Abort!\r\n");
#endif
goto RPM_FAILURE;
}
/* decode apdu request & encode apdu reply
encode complex ack, invoke id, service choice */
apdu_len =
rpm_ack_encode_apdu_init(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id);
for (;;) {
/* Start by looking for an object ID */
len =
rpm_decode_object_id(&service_request[decode_len],
service_len - decode_len, &rpmdata);
if (len >= 0) {
/* Got one so skip to next stage */
decode_len += len;
} else {
/* bad encoding - skip to error/reject/abort handling */
#if PRINT_ENABLED
fprintf(stderr, "RPM: Bad Encoding.\n");
#endif
error = len;
goto RPM_FAILURE;
}
/* Test for case of indefinite Device object instance */
if ((rpmdata.object_type == OBJECT_DEVICE) &&
(rpmdata.object_instance == BACNET_MAX_INSTANCE)) {
rpmdata.object_instance = Device_Object_Instance_Number();
}
/* Stick this object id into the reply - if it will fit */
len = rpm_ack_encode_apdu_object_begin(&Temp_Buf[0], &rpmdata);
copy_len =
memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], apdu_len,
len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Response too big!\r\n");
#endif
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
goto RPM_FAILURE;
}
apdu_len += copy_len;
/* do each property of this object of the RPM request */
for (;;) {
/* Fetch a property */
len =
rpm_decode_object_property(&service_request[decode_len],
service_len - decode_len, &rpmdata);
if (len < 0) {
/* bad encoding - skip to error/reject/abort handling */
#if PRINT_ENABLED
fprintf(stderr, "RPM: Bad Encoding.\n");
#endif
error = len;
goto RPM_FAILURE;
}
decode_len += len;
/* handle the special properties */
if ((rpmdata.object_property == PROP_ALL) ||
(rpmdata.object_property == PROP_REQUIRED) ||
(rpmdata.object_property == PROP_OPTIONAL)) {
struct special_property_list_t property_list;
unsigned property_count = 0;
unsigned index = 0;
BACNET_PROPERTY_ID special_object_property;
if (rpmdata.array_index != BACNET_ARRAY_ALL) {
/* No array index options for this special property.
Encode error for this object property response */
len =
rpm_ack_encode_apdu_object_property(&Temp_Buf[0],
rpmdata.object_property, rpmdata.array_index);
copy_len =
memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr,
"RPM: Too full to encode property!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
goto RPM_FAILURE;
}
apdu_len += len;
len =
rpm_ack_encode_apdu_object_property_error(&Temp_Buf[0],
ERROR_CLASS_PROPERTY,
ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY);
copy_len =
memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Too full to encode error!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
goto RPM_FAILURE;
}
apdu_len += len;
} else {
special_object_property = rpmdata.object_property;
Device_Objects_Property_List(rpmdata.object_type,
rpmdata.object_instance, &property_list);
property_count =
RPM_Object_Property_Count(&property_list,
special_object_property);
if (property_count == 0) {
/* this only happens with the OPTIONAL property */
/* 135-2016bl-2. Clarify ReadPropertyMultiple
response on OPTIONAL when empty. */
/* If no optional properties are supported then
an empty 'List of Results' shall be returned
for the specified property.*/
} else {
for (index = 0; index < property_count; index++) {
rpmdata.object_property =
RPM_Object_Property(&property_list,
special_object_property, index);
len =
RPM_Encode_Property(&Handler_Transmit_Buffer
[npdu_len], (uint16_t) apdu_len, MAX_APDU,
&rpmdata);
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
fprintf(stderr,
"RPM: Too full for property!\r\n");
#endif
error = len;
goto RPM_FAILURE;
}
}
}
}
} else {
/* handle an individual property */
len =
RPM_Encode_Property(&Handler_Transmit_Buffer[npdu_len],
(uint16_t) apdu_len, MAX_APDU, &rpmdata);
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
fprintf(stderr,
"RPM: Too full for individual property!\r\n");
#endif
error = len;
goto RPM_FAILURE;
}
}
if (decode_is_closing_tag_number(&service_request[decode_len], 1)) {
/* Reached end of property list so cap the result list */
decode_len++;
len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]);
copy_len =
memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0],
apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Too full to encode object end!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
goto RPM_FAILURE;
} else {
apdu_len += copy_len;
}
break; /* finished with this property list */
}
}
if (decode_len >= service_len) {
/* Reached the end so finish up */
break;
}
}
if (apdu_len > service_data->max_resp) {
/* too big for the sender - send an abort */
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
#if PRINT_ENABLED
fprintf(stderr, "RPM: Message too large. Sending Abort!\n");
#endif
goto RPM_FAILURE;
}
RPM_FAILURE:
if (error) {
if (error == BACNET_STATUS_ABORT) {
apdu_len =
abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
abort_convert_error_code(rpmdata.error_code), true);
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Abort!\n");
#endif
} else if (error == BACNET_STATUS_ERROR) {
apdu_len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
rpmdata.error_class, rpmdata.error_code);
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Error!\n");
#endif
} else if (error == BACNET_STATUS_REJECT) {
apdu_len =
reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
reject_convert_error_code(rpmdata.error_code));
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Reject!\n");
#endif
}
}
pdu_len = apdu_len + npdu_len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Failed to send PDU (%s)!\n", strerror(errno));
#endif
}
}
+364
View File
@@ -0,0 +1,364 @@
/**************************************************************************
*
* Copyright (C) 2008 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 <stdlib.h>
#include <assert.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "bactext.h"
#include "rpm.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
/** @file h_rpm_a.c Handles Read Property Multiple Acknowledgments. */
/** Decode the received RPM data and make a linked list of the results.
* @ingroup DSRPM
*
* @param apdu [in] The received apdu data.
* @param apdu_len [in] Total length of the apdu.
* @param read_access_data [out] Pointer to the head of the linked list
* where the RPM data is to be stored.
* @return The number of bytes decoded, or -1 on error
*/
int rpm_ack_decode_service_request(
uint8_t * apdu,
int apdu_len,
BACNET_READ_ACCESS_DATA * read_access_data)
{
int decoded_len = 0; /* return value */
uint32_t error_value = 0; /* decoded error value */
int len = 0; /* number of bytes returned from decoding */
uint8_t tag_number = 0; /* decoded tag number */
uint32_t len_value = 0; /* decoded length value */
BACNET_READ_ACCESS_DATA *rpm_object;
BACNET_READ_ACCESS_DATA *old_rpm_object;
BACNET_PROPERTY_REFERENCE *rpm_property;
BACNET_PROPERTY_REFERENCE *old_rpm_property;
BACNET_APPLICATION_DATA_VALUE *value;
BACNET_APPLICATION_DATA_VALUE *old_value;
assert(read_access_data != NULL);
rpm_object = read_access_data;
old_rpm_object = rpm_object;
while (rpm_object && apdu_len) {
len =
rpm_ack_decode_object_id(apdu, apdu_len, &rpm_object->object_type,
&rpm_object->object_instance);
if (len <= 0) {
old_rpm_object->next = NULL;
free(rpm_object);
break;
}
decoded_len += len;
apdu_len -= len;
apdu += len;
rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE));
rpm_object->listOfProperties = rpm_property;
old_rpm_property = rpm_property;
while (rpm_property && apdu_len) {
len =
rpm_ack_decode_object_property(apdu, apdu_len,
&rpm_property->propertyIdentifier,
&rpm_property->propertyArrayIndex);
if (len <= 0) {
old_rpm_property->next = NULL;
if (rpm_object->listOfProperties == rpm_property) {
/* was this the only property in the list? */
rpm_object->listOfProperties = NULL;
}
free(rpm_property);
break;
}
decoded_len += len;
apdu_len -= len;
apdu += len;
if (apdu_len && decode_is_opening_tag_number(apdu, 4)) {
/* propertyValue */
decoded_len++;
apdu_len--;
apdu++;
/* note: if this is an array, there will be
more than one element to decode */
value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE));
rpm_property->value = value;
old_value = value;
while (value && (apdu_len > 0)) {
if (IS_CONTEXT_SPECIFIC(*apdu)) {
len =
bacapp_decode_context_data(apdu, apdu_len, value,
rpm_property->propertyIdentifier);
} else {
len =
bacapp_decode_application_data(apdu, apdu_len,
value);
}
/* If len == 0 then it's an empty structure, which is OK. */
if (len < 0) {
/* problem decoding */
/* calling function will free the memory */
return BACNET_STATUS_ERROR;
}
decoded_len += len;
apdu_len -= len;
apdu += len;
if (apdu_len && decode_is_closing_tag_number(apdu, 4)) {
decoded_len++;
apdu_len--;
apdu++;
break;
} else {
old_value = value;
value =
calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE));
old_value->next = value;
}
}
} else if (apdu_len && decode_is_opening_tag_number(apdu, 5)) {
/* propertyAccessError */
decoded_len++;
apdu_len--;
apdu++;
/* decode the class and code sequence */
len =
decode_tag_number_and_value(apdu, &tag_number, &len_value);
decoded_len += len;
apdu_len -= len;
apdu += len;
/* FIXME: we could validate that the tag is enumerated... */
len = decode_enumerated(apdu, len_value, &error_value);
rpm_property->error.error_class = error_value;
decoded_len += len;
apdu_len -= len;
apdu += len;
len =
decode_tag_number_and_value(apdu, &tag_number, &len_value);
decoded_len += len;
apdu_len -= len;
apdu += len;
/* FIXME: we could validate that the tag is enumerated... */
len = decode_enumerated(apdu, len_value, &error_value);
rpm_property->error.error_code = error_value;
decoded_len += len;
apdu_len -= len;
apdu += len;
if (apdu_len && decode_is_closing_tag_number(apdu, 5)) {
decoded_len++;
apdu_len--;
apdu++;
}
}
old_rpm_property = rpm_property;
rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE));
old_rpm_property->next = rpm_property;
}
len = rpm_decode_object_end(apdu, apdu_len);
if (len) {
decoded_len += len;
apdu_len -= len;
apdu += len;
}
if (apdu_len) {
old_rpm_object = rpm_object;
rpm_object = calloc(1, sizeof(BACNET_READ_ACCESS_DATA));
old_rpm_object->next = rpm_object;
}
}
return decoded_len;
}
/* for debugging... */
void rpm_ack_print_data(
BACNET_READ_ACCESS_DATA * rpm_data)
{
BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */
BACNET_PROPERTY_REFERENCE *listOfProperties;
BACNET_APPLICATION_DATA_VALUE *value;
bool array_value = false;
if (rpm_data) {
#if PRINT_ENABLED
fprintf(stdout, "%s #%lu\r\n",
bactext_object_type_name(rpm_data->object_type),
(unsigned long) rpm_data->object_instance);
fprintf(stdout, "{\r\n");
#endif
listOfProperties = rpm_data->listOfProperties;
while (listOfProperties) {
#if PRINT_ENABLED
if (listOfProperties->propertyIdentifier < 512) {
fprintf(stdout, " %s: ",
bactext_property_name(listOfProperties->
propertyIdentifier));
} else {
fprintf(stdout, " proprietary %u: ",
(unsigned) listOfProperties->propertyIdentifier);
}
#endif
if (listOfProperties->propertyArrayIndex != BACNET_ARRAY_ALL) {
#if PRINT_ENABLED
fprintf(stdout, "[%d]", listOfProperties->propertyArrayIndex);
#endif
}
value = listOfProperties->value;
if (value) {
#if PRINT_ENABLED
if (value->next) {
fprintf(stdout, "{");
array_value = true;
} else {
array_value = false;
}
#endif
object_value.object_type = rpm_data->object_type;
object_value.object_instance = rpm_data->object_instance;
while (value) {
object_value.object_property =
listOfProperties->propertyIdentifier;
object_value.array_index =
listOfProperties->propertyArrayIndex;
object_value.value = value;
bacapp_print_value(stdout, &object_value);
#if PRINT_ENABLED
if (value->next) {
fprintf(stdout, ",\r\n ");
} else {
if (array_value) {
fprintf(stdout, "}\r\n");
} else {
fprintf(stdout, "\r\n");
}
}
#endif
value = value->next;
}
} else {
#if PRINT_ENABLED
/* AccessError */
fprintf(stdout, "BACnet Error: %s: %s\r\n",
bactext_error_class_name((int) listOfProperties->
error.error_class),
bactext_error_code_name((int) listOfProperties->
error.error_code));
#endif
}
listOfProperties = listOfProperties->next;
}
#if PRINT_ENABLED
fprintf(stdout, "}\r\n");
#endif
}
}
/** Handler for a ReadPropertyMultiple ACK.
* @ingroup DSRPM
* For each read property, print out the ACK'd data for debugging,
* and free the request data items from linked property list.
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
void handler_read_property_multiple_ack(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
{
int len = 0;
BACNET_READ_ACCESS_DATA *rpm_data;
BACNET_READ_ACCESS_DATA *old_rpm_data;
BACNET_PROPERTY_REFERENCE *rpm_property;
BACNET_PROPERTY_REFERENCE *old_rpm_property;
BACNET_APPLICATION_DATA_VALUE *value;
BACNET_APPLICATION_DATA_VALUE *old_value;
(void) src;
(void) service_data; /* we could use these... */
rpm_data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA));
if (rpm_data) {
len =
rpm_ack_decode_service_request(service_request, service_len,
rpm_data);
}
#if 1
fprintf(stderr, "Received Read-Property-Multiple Ack!\n");
#endif
if (len > 0) {
while (rpm_data) {
rpm_ack_print_data(rpm_data);
rpm_property = rpm_data->listOfProperties;
while (rpm_property) {
value = rpm_property->value;
while (value) {
old_value = value;
value = value->next;
free(old_value);
}
old_rpm_property = rpm_property;
rpm_property = rpm_property->next;
free(old_rpm_property);
}
old_rpm_data = rpm_data;
rpm_data = rpm_data->next;
free(old_rpm_data);
}
} else {
#if 1
fprintf(stderr, "RPM Ack Malformed! Freeing memory...\n");
#endif
while (rpm_data) {
rpm_property = rpm_data->listOfProperties;
while (rpm_property) {
value = rpm_property->value;
while (value) {
old_value = value;
value = value->next;
free(old_value);
}
old_rpm_property = rpm_property;
rpm_property = rpm_property->next;
free(old_rpm_property);
}
old_rpm_data = rpm_data;
rpm_data = rpm_data->next;
free(old_rpm_data);
}
}
}
+187
View File
@@ -0,0 +1,187 @@
/**************************************************************************
*
* Copyright (C) 2009 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 <stdio.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 "readrange.h"
#include "device.h"
#include "handlers.h"
/** @file h_rr.c Handles Read Range requests. */
static uint8_t Temp_Buf[MAX_APDU] = { 0 };
/* Encodes the property APDU and returns the length,
or sets the error, and returns -1 */
static int Encode_RR_payload(
uint8_t * apdu,
BACNET_READ_RANGE_DATA * pRequest)
{
int apdu_len = -1;
rr_info_function info_fn_ptr = NULL;
RR_PROP_INFO PropInfo;
/* initialize the default return values */
pRequest->error_class = ERROR_CLASS_SERVICES;
pRequest->error_code = ERROR_CODE_OTHER;
/* handle each object type */
info_fn_ptr = Device_Objects_RR_Info(pRequest->object_type);
if ((info_fn_ptr != NULL) && (info_fn_ptr(pRequest, &PropInfo) != false)) {
/* We try and do some of the more generic error checking here to cut down on duplication of effort */
if ((pRequest->RequestType == RR_BY_POSITION) && (pRequest->Range.RefIndex == 0)) { /* First index is 1 so can't accept 0 */
pRequest->error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */
} else if (((PropInfo.RequestTypes & RR_ARRAY_OF_LISTS) == 0) &&
(pRequest->array_index != 0) &&
(pRequest->array_index != BACNET_ARRAY_ALL)) {
/* Array access attempted on a non array property */
pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
} else if ((pRequest->RequestType != RR_READ_ALL) &&
((PropInfo.RequestTypes & pRequest->RequestType) == 0)) {
/* By Time or By Sequence not supported - By Position is always required */
pRequest->error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */
} else if ((pRequest->Count == 0) && (pRequest->RequestType != RR_READ_ALL)) { /* Count cannot be zero */
pRequest->error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */
} else if (PropInfo.Handler != NULL) {
apdu_len = PropInfo.Handler(apdu, pRequest);
}
} else {
/* Either we don't support RR for this property yet or it is not a list or array of lists */
pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST;
}
return apdu_len;
}
void handler_read_range(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_READ_RANGE_DATA data;
int len = 0;
int pdu_len = 0;
BACNET_NPDU_DATA npdu_data;
bool error = false;
int bytes_sent = 0;
BACNET_ADDRESS my_address;
data.error_class = ERROR_CLASS_OBJECT;
data.error_code = ERROR_CODE_UNKNOWN_OBJECT;
/* 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 (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);
#if PRINT_ENABLED
fprintf(stderr, "RR: Segmented message. Sending Abort!\n");
#endif
goto RR_ABORT;
}
memset(&data, 0, sizeof(data)); /* start with blank canvas */
len = rr_decode_service_request(service_request, service_len, &data);
#if PRINT_ENABLED
if (len <= 0)
fprintf(stderr, "RR: Unable to decode Request!\n");
#endif
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);
#if PRINT_ENABLED
fprintf(stderr, "RR: Bad Encoding. Sending Abort!\n");
#endif
goto RR_ABORT;
}
/* assume that there is an error */
error = true;
len = Encode_RR_payload(&Temp_Buf[0], &data);
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 =
rr_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, &data);
#if PRINT_ENABLED
fprintf(stderr, "RR: Sending Ack!\n");
#endif
error = false;
}
if (error) {
if (len == -2) {
/* BACnet APDU too small to fit data, so proper response is Abort */
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
#if PRINT_ENABLED
fprintf(stderr, "RR: Reply too big to fit into APDU!\n");
#endif
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_READ_RANGE,
data.error_class, data.error_code);
#if PRINT_ENABLED
fprintf(stderr, "RR: Sending Error!\n");
#endif
}
}
RR_ABORT:
pdu_len += len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
#endif
return;
}
+121
View File
@@ -0,0 +1,121 @@
/**************************************************************************
*
* Copyright (C) 2009 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 "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "bactext.h"
#include "readrange.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
/** @file h_rr_a.c Handles Read Range Acknowledgments. */
/* for debugging... */
static void PrintReadRangeData(
BACNET_READ_RANGE_DATA * data)
{
BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */
BACNET_APPLICATION_DATA_VALUE value; /* for decode value data */
int len = 0;
uint8_t *application_data;
int application_data_len;
bool first_value = true;
bool print_brace = false;
if (data) {
application_data = data->application_data;
application_data_len = data->application_data_len;
/* FIXME: what if application_data_len is bigger than 255? */
/* value? need to loop until all of the len is gone... */
for (;;) {
len =
bacapp_decode_application_data(application_data,
(uint8_t) application_data_len, &value);
if (first_value && (len < application_data_len)) {
first_value = false;
#if PRINT_ENABLED
fprintf(stdout, "{");
#endif
print_brace = true;
}
object_value.object_type = data->object_type;
object_value.object_instance = data->object_instance;
object_value.object_property = data->object_property;
object_value.array_index = data->array_index;
object_value.value = &value;
bacapp_print_value(stdout, &object_value);
if (len > 0) {
if (len < application_data_len) {
application_data += len;
application_data_len -= len;
/* there's more! */
#if PRINT_ENABLED
fprintf(stdout, ",");
#endif
} else {
break;
}
} else {
break;
}
}
#if PRINT_ENABLED
if (print_brace)
fprintf(stdout, "}");
fprintf(stdout, "\r\n");
#endif
}
}
void handler_read_range_ack(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
{
int len = 0;
BACNET_READ_RANGE_DATA data;
(void) src;
(void) service_data; /* we could use these... */
len = rr_ack_decode_service_request(service_request, service_len, &data);
#if PRINT_ENABLED
fprintf(stderr, "Received ReadRange Ack!\n");
#endif
if (len > 0)
PrintReadRangeData(&data);
}
+298
View File
@@ -0,0 +1,298 @@
/**************************************************************************
*
* 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 <stdio.h>
#include "config.h"
#include "device.h"
#include "datetime.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "timesync.h"
#include "handlers.h"
#include "client.h"
#include "bacaddr.h"
/** @file h_ts.c Handles TimeSync requests. */
#if defined(BACNET_TIME_MASTER)
/* sending time sync to recipients */
#ifndef MAX_TIME_SYNC_RECIPIENTS
#define MAX_TIME_SYNC_RECIPIENTS 16
#endif
BACNET_RECIPIENT_LIST Time_Sync_Recipients[MAX_TIME_SYNC_RECIPIENTS];
/* variable used for controlling when to
automatically send a TimeSynchronization request */
static BACNET_DATE_TIME Next_Sync_Time;
#endif
#if PRINT_ENABLED
static void show_bacnet_date_time(
BACNET_DATE * bdate,
BACNET_TIME * btime)
{
/* show the date received */
fprintf(stderr, "%u", (unsigned) bdate->year);
fprintf(stderr, "/%u", (unsigned) bdate->month);
fprintf(stderr, "/%u", (unsigned) bdate->day);
/* show the time received */
fprintf(stderr, " %02u", (unsigned) btime->hour);
fprintf(stderr, ":%02u", (unsigned) btime->min);
fprintf(stderr, ":%02u", (unsigned) btime->sec);
fprintf(stderr, ".%02u", (unsigned) btime->hundredths);
fprintf(stderr, "\r\n");
}
#endif
void handler_timesync(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
int len = 0;
BACNET_DATE bdate = {0};
BACNET_TIME btime = {0};
(void) src;
(void) service_len;
len =
timesync_decode_service_request(service_request, service_len, &bdate,
&btime);
if (len > 0) {
if (datetime_is_valid(&bdate, &btime)) {
/* fixme: only set the time if off by some amount */
#if PRINT_ENABLED
fprintf(stderr, "Received TimeSyncronization Request\r\n");
show_bacnet_date_time(&bdate, &btime);
#else
/* FIXME: set the time?
Maybe only set the time if off by some amount */
#endif
}
}
return;
}
void handler_timesync_utc(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
int len = 0;
BACNET_DATE bdate;
BACNET_TIME btime;
(void) src;
(void) service_len;
len =
timesync_decode_service_request(service_request, service_len, &bdate,
&btime);
if (len > 0) {
if (datetime_is_valid(&bdate, &btime)) {
#if PRINT_ENABLED
fprintf(stderr, "Received TimeSyncronization Request\r\n");
show_bacnet_date_time(&bdate, &btime);
#endif
/* FIXME: set the time?
only set the time if off by some amount */
}
}
return;
}
#if defined(BACNET_TIME_MASTER)
/** Handle a request to list all the timesync recipients.
*
* Invoked by a request to read the Device object's
* PROP_TIME_SYNCHRONIZATION_RECIPIENTS.
* Loops through the list of timesync recipients, and, for each valid one,
* adds its data to the APDU.
*
* @param apdu [out] Buffer in which the APDU contents are built.
* @param max_apdu [in] Max length of the APDU buffer.
*
* @return How many bytes were encoded in the buffer, or
* BACNET_STATUS_ABORT if the response would not fit within the buffer.
*/
int handler_timesync_encode_recipients(
uint8_t * apdu,
int max_apdu)
{
return timesync_encode_timesync_recipients(apdu, max_apdu,
&Time_Sync_Recipients[0]);
}
#endif
#if defined(BACNET_TIME_MASTER)
bool handler_timesync_recipient_write(
BACNET_WRITE_PROPERTY_DATA * wp_data)
{
bool status = false;
/* fixme: handle writing of the recipient list */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
return status;
}
#endif
#if defined(BACNET_TIME_MASTER)
static void handler_timesync_send(
BACNET_DATE_TIME * current_date_time)
{
unsigned index = 0;
bool status = false;
for (index = 0; index < MAX_TIME_SYNC_RECIPIENTS; index++) {
if (Time_Sync_Recipients[index].tag == 1) {
if (status) {
Send_TimeSync_Remote(
&Time_Sync_Recipients[index].type.address,
&current_date_time->date,
&current_date_time->time);
}
}
}
}
#endif
#if defined(BACNET_TIME_MASTER)
static void handler_timesync_update(
uint32_t device_interval,
BACNET_DATE_TIME * current_date_time)
{
uint32_t current_minutes = 0;
uint32_t next_minutes = 0;
uint32_t delta_minutes = 0;
uint32_t offset_minutes = 0;
uint32_t interval = 0;
uint32_t interval_offset = 0;
datetime_copy(&Next_Sync_Time, current_date_time);
if (Device_Align_Intervals()) {
interval_offset = Device_Interval_Offset();
/* If periodic time synchronization is enabled and
the time synchronization interval is a factor of
(divides without remainder) an hour or day, then
the beginning of the period specified for time
synchronization shall be aligned to the hour or
day, respectively. */
if ((60 % device_interval) == 0) {
/* factor of an hour alignment */
/* Interval_Minutes = 1 2 3 4 5 6 10 12 15 20 30 60 */
/* determine next interval */
current_minutes = Next_Sync_Time.time.min;
interval = current_minutes/device_interval;
interval++;
next_minutes = interval * device_interval;
offset_minutes = interval_offset % device_interval;
next_minutes += offset_minutes;
delta_minutes = next_minutes - current_minutes;
datetime_add_minutes(&Next_Sync_Time, delta_minutes);
Next_Sync_Time.time.sec = 0;
Next_Sync_Time.time.hundredths = 0;
} else if ((1440 % device_interval) == 0) {
/* factor of a day alignment */
/* Interval_Minutes = 1 2 3 4 5 6 8 9 10 12 15 16
18 20 24 30 32 36 40 45 48 60 72 80 90 96 120
144 160 180 240 288 360 480 720 1440 */
current_minutes =
datetime_minutes_since_midnight(&Next_Sync_Time.time);
interval = current_minutes/device_interval;
interval++;
next_minutes = interval * device_interval;
offset_minutes = interval_offset % device_interval;
next_minutes += offset_minutes;
delta_minutes = next_minutes - current_minutes;
datetime_add_minutes(&Next_Sync_Time, delta_minutes);
Next_Sync_Time.time.sec = 0;
Next_Sync_Time.time.hundredths = 0;
}
} else {
datetime_add_minutes(&Next_Sync_Time, device_interval);
Next_Sync_Time.time.sec = 0;
Next_Sync_Time.time.hundredths = 0;
}
}
#endif
#if defined(BACNET_TIME_MASTER)
bool handler_timesync_recipient_address_set(
unsigned index,
BACNET_ADDRESS *address)
{
bool status = false;
if (address && (index < MAX_TIME_SYNC_RECIPIENTS)) {
Time_Sync_Recipients[index].tag = 1;
bacnet_address_copy(
&Time_Sync_Recipients[index].type.address,
address);
status = true;
}
return status;
}
#endif
#if defined(BACNET_TIME_MASTER)
void handler_timesync_task(
BACNET_DATE_TIME * current_date_time)
{
int compare = 0;
uint32_t device_interval = 0;
device_interval = Device_Time_Sync_Interval();
if (device_interval) {
compare = datetime_compare(current_date_time, &Next_Sync_Time);
/* if the date/times are the same, return is 0
if date1 is before date2, returns negative
if date1 is after date2, returns positive */
if (compare >= 0) {
handler_timesync_update(device_interval, current_date_time);
handler_timesync_send(current_date_time);
}
}
}
#endif
#if defined(BACNET_TIME_MASTER)
void handler_timesync_init(void)
{
unsigned i = 0;
/* connect linked list */
for (; i < (MAX_TIME_SYNC_RECIPIENTS-1); i++) {
Time_Sync_Recipients[i].next = &Time_Sync_Recipients[i+1];
Time_Sync_Recipients[i+1].next = NULL;
}
for (i = 0; i < MAX_TIME_SYNC_RECIPIENTS; i++) {
Time_Sync_Recipients[i].tag = 0xFF;
}
}
#endif
+113
View File
@@ -0,0 +1,113 @@
/**************************************************************************
*
* Copyright (C) 2008 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
/* special for this module */
#include "cov.h"
#include "bactext.h"
#include "handlers.h"
#ifndef MAX_COV_PROPERTIES
#define MAX_COV_PROPERTIES 2
#endif
/** @file h_ucov.c Handles Unconfirmed COV Notifications. */
/* */
/** Handler for an Unconfirmed COV Notification.
* @ingroup DSCOV
* Decodes the received list of Properties to update,
* and print them out with the subscription information.
* @note Nothing is specified in BACnet about what to do with the
* information received from Unconfirmed COV Notifications.
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message (unused)
*/
void handler_ucov_notification(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
BACNET_COV_DATA cov_data;
BACNET_PROPERTY_VALUE property_value[MAX_COV_PROPERTIES];
BACNET_PROPERTY_VALUE *pProperty_value = NULL;
int len = 0;
/* src not needed for this application */
src = src;
/* create linked list to store data if more
than one property value is expected */
bacapp_property_value_list_init(&property_value[0], MAX_COV_PROPERTIES);
cov_data.listOfValues = &property_value[0];
#if PRINT_ENABLED
fprintf(stderr, "UCOV: Received Notification!\n");
#endif
/* decode the service request only */
len =
cov_notify_decode_service_request(service_request, service_len,
&cov_data);
#if PRINT_ENABLED
if (len > 0) {
fprintf(stderr, "UCOV: PID=%u ", cov_data.subscriberProcessIdentifier);
fprintf(stderr, "instance=%u ", cov_data.initiatingDeviceIdentifier);
fprintf(stderr, "%s %u ",
bactext_object_type_name(cov_data.monitoredObjectIdentifier.type),
cov_data.monitoredObjectIdentifier.instance);
fprintf(stderr, "time remaining=%u seconds ", cov_data.timeRemaining);
fprintf(stderr, "\n");
pProperty_value = &property_value[0];
while (pProperty_value) {
fprintf(stderr, "UCOV: ");
if (pProperty_value->propertyIdentifier < 512) {
fprintf(stderr, "%s ",
bactext_property_name
(pProperty_value->propertyIdentifier));
} else {
fprintf(stderr, "proprietary %u ",
pProperty_value->propertyIdentifier);
}
if (pProperty_value->propertyArrayIndex != BACNET_ARRAY_ALL) {
fprintf(stderr, "%u ", pProperty_value->propertyArrayIndex);
}
fprintf(stderr, "\n");
pProperty_value = pProperty_value->next;
}
} else {
fprintf(stderr, "UCOV: Unable to decode service request!\n");
}
#endif
}
+121
View File
@@ -0,0 +1,121 @@
/**************************************************************************
*
* Copyright (C) 2009 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "ptransfer.h"
#include "handlers.h"
/** @file h_upt.c Handles Unconfirmed Private Transfer requests. */
void private_transfer_print_data(
BACNET_PRIVATE_TRANSFER_DATA *private_data)
{
BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */
BACNET_APPLICATION_DATA_VALUE value; /* for decode value data */
int len = 0;
uint8_t *application_data;
int application_data_len;
bool first_value = true;
bool print_brace = false;
if (private_data) {
#if PRINT_ENABLED
printf("PrivateTransfer:vendorID=%u\r\n",
(unsigned) private_data->vendorID);
printf("PrivateTransfer:serviceNumber=%lu\r\n",
(unsigned long) private_data->serviceNumber);
#endif
application_data = private_data->serviceParameters;
application_data_len = private_data->serviceParametersLen;
for (;;) {
len =
bacapp_decode_application_data(application_data,
(uint8_t) application_data_len, &value);
if (first_value && (len < application_data_len)) {
first_value = false;
#if PRINT_ENABLED
fprintf(stdout, "{");
#endif
print_brace = true;
}
/* private transfer doesn't provide any clues */
object_value.object_type = MAX_BACNET_OBJECT_TYPE;
object_value.object_instance = BACNET_MAX_INSTANCE;
object_value.object_property = MAX_BACNET_PROPERTY_ID;
object_value.array_index = BACNET_ARRAY_ALL;
object_value.value = &value;
bacapp_print_value(stdout, &object_value);
if (len > 0) {
if (len < application_data_len) {
application_data += len;
application_data_len -= len;
/* there's more! */
#if PRINT_ENABLED
fprintf(stdout, ",");
#endif
} else {
break;
}
} else {
break;
}
}
#if PRINT_ENABLED
if (print_brace) {
fprintf(stdout, "}");
}
fprintf(stdout, "\r\n");
#endif
}
}
void handler_unconfirmed_private_transfer(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
BACNET_PRIVATE_TRANSFER_DATA private_data;
int len = 0;
#if PRINT_ENABLED
fprintf(stderr, "Received Unconfirmed Private Transfer Request!\n");
#endif
len =
ptransfer_decode_service_request(service_request, service_len,
&private_data);
if (len >= 0) {
private_transfer_print_data(&private_data);
}
}
+154
View File
@@ -0,0 +1,154 @@
/**************************************************************************
*
* 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "whohas.h"
#include "device.h"
#include "client.h"
#include "handlers.h"
/** @file h_whohas.c Handles Who-Has requests. */
/** Local function which responds with either the requested object name
* or object ID, if the Device has a match.
* @param data [in] The decoded who-has payload from the request.
*/
static void match_name_or_object(
BACNET_WHO_HAS_DATA * data)
{
int object_type = 0;
uint32_t object_instance = 0;
bool found = false;
BACNET_CHARACTER_STRING object_name;
/* do we have such an object? If so, send an I-Have.
note: we should have only 1 of such an object */
if (data->is_object_name) {
/* valid name in my device? */
found =
Device_Valid_Object_Name(&data->object.name, &object_type,
&object_instance);
if (found) {
Send_I_Have(Device_Object_Instance_Number(),
(BACNET_OBJECT_TYPE) object_type, object_instance,
&data->object.name);
}
} else {
/* valid object_name copy in my device? */
found =
Device_Object_Name_Copy((BACNET_OBJECT_TYPE) data->
object.identifier.type, data->object.identifier.instance,
&object_name);
if (found) {
Send_I_Have(Device_Object_Instance_Number(),
(BACNET_OBJECT_TYPE) data->object.identifier.type,
data->object.identifier.instance, &object_name);
}
}
}
/** Handler for Who-Has requests, with broadcast I-Have response.
* Will respond if the device Object ID matches, and we have
* the Object or Object Name requested.
*
* @ingroup DMDOB
* @param service_request [in] The received message to be handled.
* @param service_len [in] Length of the service_request message.
* @param src [in] The BACNET_ADDRESS of the message's source.
*/
void handler_who_has(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
int len = 0;
BACNET_WHO_HAS_DATA data;
bool directed_to_me = false;
(void) src;
len = whohas_decode_service_request(service_request, service_len, &data);
if (len > 0) {
if ((data.low_limit == -1) || (data.high_limit == -1))
directed_to_me = true;
else if ((Device_Object_Instance_Number() >= (uint32_t) data.low_limit)
&& (Device_Object_Instance_Number() <= (uint32_t) data.high_limit))
directed_to_me = true;
if (directed_to_me) {
match_name_or_object(&data);
}
}
}
#ifdef BAC_ROUTING /* was for BAC_ROUTING - delete in 2/2012 if still unused */
/* EKH: I restored this to BAC_ROUTING (from DEPRECATED) because I found that the server demo with the built-in
virtual Router did not insert the SADRs of the virtual devices on the virtual network without it */
/** Handler for Who-Has requests in the virtual routing setup,
* with broadcast I-Have response.
* Will respond if the device Object ID matches, and we have
* the Object or Object Name requested.
*
* @ingroup DMDOB
* @param service_request [in] The received message to be handled.
* @param service_len [in] Length of the service_request message.
* @param src [in] The BACNET_ADDRESS of the message's source (ignored).
*/
void handler_who_has_for_routing(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
int len = 0;
BACNET_WHO_HAS_DATA data;
int32_t dev_instance;
int cursor = 0; /* Starting hint */
int my_list[2] = { 0, -1 }; /* Not really used, so dummy values */
BACNET_ADDRESS bcast_net;
(void) src;
len = whohas_decode_service_request(service_request, service_len, &data);
if (len > 0) {
/* Go through all devices, starting with the root gateway Device */
memset(&bcast_net, 0, sizeof(BACNET_ADDRESS));
bcast_net.net = BACNET_BROADCAST_NETWORK; /* That's all we have to set */
while (Routed_Device_GetNext(&bcast_net, my_list, &cursor)) {
dev_instance = Device_Object_Instance_Number();
if ((data.low_limit == -1) || (data.high_limit == -1) ||
((dev_instance >= data.low_limit) &&
(dev_instance <= data.high_limit)))
match_name_or_object(&data);
}
}
}
#endif /* BAC_ROUTING */
+203
View File
@@ -0,0 +1,203 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "whois.h"
#include "iam.h"
#include "device.h"
#include "client.h"
#include "txbuf.h"
#include "handlers.h"
/** @file h_whois.c Handles Who-Is requests. */
/** Handler for Who-Is requests, with broadcast I-Am response.
* @ingroup DMDDB
* @param service_request [in] The received message to be handled.
* @param service_len [in] Length of the service_request message.
* @param src [in] The BACNET_ADDRESS of the message's source (ignored).
*/
void handler_who_is(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
int len = 0;
int32_t low_limit = 0;
int32_t high_limit = 0;
(void) src;
len =
whois_decode_service_request(service_request, service_len, &low_limit,
&high_limit);
if (len == 0) {
Send_I_Am(&Handler_Transmit_Buffer[0]);
} else if (len != BACNET_STATUS_ERROR) {
/* is my device id within the limits? */
if ((Device_Object_Instance_Number() >= (uint32_t) low_limit) &&
(Device_Object_Instance_Number() <= (uint32_t) high_limit)) {
Send_I_Am(&Handler_Transmit_Buffer[0]);
}
}
return;
}
/** Handler for Who-Is requests, with Unicast I-Am response (per Addendum 135-2004q).
* @ingroup DMDDB
* @param service_request [in] The received message to be handled.
* @param service_len [in] Length of the service_request message.
* @param src [in] The BACNET_ADDRESS of the message's source that the
* response will be sent back to.
*/
void handler_who_is_unicast(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
int len = 0;
int32_t low_limit = 0;
int32_t high_limit = 0;
len =
whois_decode_service_request(service_request, service_len, &low_limit,
&high_limit);
/* If no limits, then always respond */
if (len == 0) {
Send_I_Am_Unicast(&Handler_Transmit_Buffer[0], src);
} else if (len != BACNET_STATUS_ERROR) {
/* is my device id within the limits? */
if ((Device_Object_Instance_Number() >= (uint32_t) low_limit) &&
(Device_Object_Instance_Number() <= (uint32_t) high_limit)) {
Send_I_Am_Unicast(&Handler_Transmit_Buffer[0], src);
}
}
return;
}
#ifdef BAC_ROUTING /* was for BAC_ROUTING - delete in 2/2012 if still unused */
/* EKH: I restored this to BAC_ROUTING (from DEPRECATED) because I found that the server demo with the built-in
virtual Router did not insert the SADRs of the virtual devices on the virtual network without it */
/** Local function to check Who-Is requests against our Device IDs.
* Will check the gateway (root Device) and all virtual routed
* Devices against the range and respond for each that matches.
*
* @param service_request [in] The received message to be handled.
* @param service_len [in] Length of the service_request message.
* @param src [in] The BACNET_ADDRESS of the message's source.
* @param is_unicast [in] True if should send unicast response(s)
* back to the src, else False if should broadcast response(s).
*/
static void check_who_is_for_routing(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
bool is_unicast)
{
int len = 0;
int32_t low_limit = 0;
int32_t high_limit = 0;
int32_t dev_instance;
int cursor = 0; /* Starting hint */
int my_list[2] = { 0, -1 }; /* Not really used, so dummy values */
BACNET_ADDRESS bcast_net;
len =
whois_decode_service_request(service_request, service_len, &low_limit,
&high_limit);
if (len == BACNET_STATUS_ERROR) {
/* Invalid; just leave */
return;
}
/* Go through all devices, starting with the root gateway Device */
memset(&bcast_net, 0, sizeof(BACNET_ADDRESS));
bcast_net.net = BACNET_BROADCAST_NETWORK; /* That's all we have to set */
while (Routed_Device_GetNext(&bcast_net, my_list, &cursor)) {
dev_instance = Device_Object_Instance_Number();
/* If len == 0, no limits and always respond */
if ((len == 0) || ((dev_instance >= low_limit) &&
(dev_instance <= high_limit))) {
if (is_unicast)
Send_I_Am_Unicast(&Handler_Transmit_Buffer[0], src);
else
Send_I_Am(&Handler_Transmit_Buffer[0]);
}
}
}
/** Handler for Who-Is requests in the virtual routing setup,
* with broadcast I-Am response(s).
* @ingroup DMDDB
* Will check the gateway (root Device) and all virtual routed
* Devices against the range and respond for each that matches.
*
* @ingroup DMDDB
* @param service_request [in] The received message to be handled.
* @param service_len [in] Length of the service_request message.
* @param src [in] The BACNET_ADDRESS of the message's source (ignored).
*/
void handler_who_is_bcast_for_routing(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
check_who_is_for_routing(service_request, service_len, src, false);
}
/** Handler for Who-Is requests in the virtual routing setup,
* with unicast I-Am response(s) returned to the src.
* Will check the gateway (root Device) and all virtual routed
* Devices against the range and respond for each that matches.
*
* @ingroup DMDDB
* @param service_request [in] The received message to be handled.
* @param service_len [in] Length of the service_request message.
* @param src [in] The BACNET_ADDRESS of the message's source that the
* response will be sent back to.
*/
void handler_who_is_unicast_for_routing(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
check_who_is_for_routing(service_request, service_len, src, true);
}
#endif /* BAC_ROUTING */
+221
View File
@@ -0,0 +1,221 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <stdio.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 "wp.h"
/* device object has the handling for all objects */
#include "device.h"
#include "handlers.h"
/** @file h_wp.c Handles Write Property requests. */
/** Handler for a WriteProperty Service request.
* @ingroup DSWP
* This handler will be invoked by apdu_handler() if it has been enabled
* by a call to apdu_set_confirmed_handler().
* This handler builds a response packet, which is
* - an Abort if
* - the message is segmented
* - if decoding fails
* - an ACK if Device_Write_Property() succeeds
* - an Error if Device_Write_Property() fails
* or there isn't enough room in the APDU to fit the data.
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
void handler_write_property(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_WRITE_PROPERTY_DATA wp_data;
int len = 0;
int pdu_len = 0;
BACNET_NPDU_DATA npdu_data;
int bytes_sent = 0;
BACNET_ADDRESS my_address;
/* 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 PRINT_ENABLED
fprintf(stderr, "WP: Received Request!\n");
#endif
if (service_data->segmented_message) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
true);
#if PRINT_ENABLED
fprintf(stderr, "WP: Segmented message. Sending Abort!\n");
#endif
goto WP_ABORT;
} /* decode the service request only */
len = wp_decode_service_request(service_request, service_len, &wp_data);
#if PRINT_ENABLED
if (len > 0)
fprintf(stderr,
"WP: type=%lu instance=%lu property=%lu priority=%lu index=%ld\n",
(unsigned long) wp_data.object_type,
(unsigned long) wp_data.object_instance,
(unsigned long) wp_data.object_property,
(unsigned long) wp_data.priority, (long) wp_data.array_index);
else
fprintf(stderr, "WP: Unable to decode Request!\n");
#endif
/* bad decoding or something we didn't understand - send an abort */
if (len <= 0) {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr, "WP: Bad Encoding. Sending Abort!\n");
#endif
goto WP_ABORT;
}
if (Device_Write_Property(&wp_data)) {
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY);
#if PRINT_ENABLED
fprintf(stderr, "WP: Sending Simple Ack!\n");
#endif
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY,
wp_data.error_class, wp_data.error_code);
#if PRINT_ENABLED
fprintf(stderr, "WP: Sending Error!\n");
#endif
}
WP_ABORT:
pdu_len += len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "WP: Failed to send PDU (%s)!\n", strerror(errno));
#endif
}
return;
}
/** Perform basic validation of Write Property argument based on
* the assumption that it is a string. Check for correct data type,
* correct encoding (fixed here as ANSI X34),correct length, and
* finally if it is allowed to be empty.
*/
bool WPValidateString(
BACNET_APPLICATION_DATA_VALUE * pValue,
int iMaxLen,
bool bEmptyAllowed,
BACNET_ERROR_CLASS * pErrorClass,
BACNET_ERROR_CODE * pErrorCode)
{
bool bResult;
/* Save on a bit of code duplication by pre selecting the most
* common outcomes from the tests (not necessarily the most likely
* outcome of the tests).
*/
bResult = false;
*pErrorClass = ERROR_CLASS_PROPERTY;
if (pValue->tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) {
if (characterstring_encoding(&pValue->type.Character_String) ==
CHARACTER_ANSI_X34) {
if ((bEmptyAllowed == false) &&
(characterstring_length(&pValue->type.Character_String) ==
0)) {
*pErrorCode = ERROR_CODE_VALUE_OUT_OF_RANGE;
} else if ((bEmptyAllowed == false) &&
(!characterstring_printable(&pValue->type.Character_String))) {
/* assumption: non-empty also means must be "printable" */
*pErrorCode = ERROR_CODE_VALUE_OUT_OF_RANGE;
} else if (characterstring_length(&pValue->type.Character_String) >
(uint16_t) iMaxLen) {
*pErrorClass = ERROR_CLASS_RESOURCES;
*pErrorCode = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY;
} else
bResult = true; /* It's all good! */
} else
*pErrorCode = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED;
} else
*pErrorCode = ERROR_CODE_INVALID_DATA_TYPE;
return (bResult);
}
/** Perform simple validation of type of Write Property argument based
* the expected type vs the actual. Set up error response if the
* validation fails. Cuts out reams of repeated code in the object code.
*/
bool WPValidateArgType(
BACNET_APPLICATION_DATA_VALUE * pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS * pErrorClass,
BACNET_ERROR_CODE * pErrorCode)
{
bool bResult;
/*
* start out assuming success and only set up error
* response if validation fails.
*/
bResult = true;
if (pValue->tag != ucExpectedTag) {
bResult = false;
*pErrorClass = ERROR_CLASS_PROPERTY;
*pErrorCode = ERROR_CODE_INVALID_DATA_TYPE;
}
return (bResult);
}
+213
View File
@@ -0,0 +1,213 @@
/**************************************************************************
*
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <stdio.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 "wp.h"
#include "reject.h"
#include "wpm.h"
/* device object has the handling for all objects */
#include "device.h"
#include "handlers.h"
/** @file h_wpm.c Handles Write Property Multiple requests. */
/** Handler for a WriteProperty Service request.
* @ingroup DSWP
* This handler will be invoked by apdu_handler() if it has been enabled
* by a call to apdu_set_confirmed_handler().
* This handler builds a response packet, which is
* - an Abort if
* - the message is segmented
* - if decoding fails
* - an ACK if Device_Write_Property_Multiple() succeeds
* - an Error if Device_Write_PropertyMultiple() encounters an error
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
void handler_write_property_multiple(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
int len = 0;
int apdu_len = 0;
int npdu_len = 0;
int pdu_len = 0;
int decode_len = 0;
bool error = false;
BACNET_WRITE_PROPERTY_DATA wp_data;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
int bytes_sent = 0;
if (service_data->segmented_message) {
wp_data.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
len = BACNET_STATUS_ABORT;
#if PRINT_ENABLED
fprintf(stderr, "WPM: Segmented message. Sending Abort!\n");
#endif
goto WPM_ABORT;
}
/* decode service request */
decode_len = 0;
do {
/* decode Object Identifier */
len =
wpm_decode_object_id(&service_request[decode_len],
service_len - decode_len, &wp_data);
if (len > 0) {
uint8_t tag_number = 0;
decode_len += len;
/* Opening tag 1 - List of Properties */
if (decode_is_opening_tag_number(&service_request[decode_len++],
1)) {
do {
/* decode a 'Property Identifier'; (3) an optional 'Property Array Index' */
/* (4) a 'Property Value'; and (5) an optional 'Priority'. */
len =
wpm_decode_object_property(&service_request
[decode_len], service_len - decode_len, &wp_data);
if (len > 0) {
decode_len += len;
#if PRINT_ENABLED
fprintf(stderr,
"WPM: type=%lu instance=%lu property=%lu priority=%lu index=%ld\n",
(unsigned long) wp_data.object_type,
(unsigned long) wp_data.object_instance,
(unsigned long) wp_data.object_property,
(unsigned long) wp_data.priority,
(long) wp_data.array_index);
#endif
if (Device_Write_Property(&wp_data) == false) {
error = true;
len = BACNET_STATUS_ERROR;
goto WPM_ABORT;
}
} else {
#if PRINT_ENABLED
fprintf(stderr, "WPM: Bad Encoding!\n");
#endif
error = true;
goto WPM_ABORT;
}
/* Closing tag 1 - List of Properties */
if (decode_is_closing_tag_number(&service_request
[decode_len], 1)) {
tag_number = 1;
decode_len++;
} else
tag_number = 0; /* it was not tag 1, decode next Property Identifier ... */
}
while (tag_number != 1); /* end decoding List of Properties for "that" object */
if (error) {
goto WPM_ABORT;
}
}
} else {
#if PRINT_ENABLED
fprintf(stderr, "WPM: Bad Encoding!\n");
#endif
error = true;
goto WPM_ABORT;
}
} while (decode_len < service_len);
WPM_ABORT:
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
npdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address,
&npdu_data);
apdu_len = 0;
/* handle any errors */
if (error) {
if (len == BACNET_STATUS_ABORT) {
apdu_len =
abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
abort_convert_error_code(wp_data.error_code), true);
#if PRINT_ENABLED
fprintf(stderr, "WPM: Sending Abort!\n");
#endif
} else if (len == BACNET_STATUS_ERROR) {
apdu_len =
wpm_error_ack_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id, &wp_data);
#if PRINT_ENABLED
fprintf(stderr, "WPM: Sending Error!\n");
#endif
} else if (len == BACNET_STATUS_REJECT) {
apdu_len =
reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
reject_convert_error_code(wp_data.error_code));
#if PRINT_ENABLED
fprintf(stderr, "WPM: Sending Reject!\n");
#endif
}
} else {
apdu_len =
wpm_ack_encode_apdu_init(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id);
#if PRINT_ENABLED
fprintf(stderr, "WPM: Sending Ack!\n");
#endif
}
pdu_len = npdu_len + apdu_len;
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0) {
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
}
#else
bytes_sent = bytes_sent;
#endif
}
+91
View File
@@ -0,0 +1,91 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "reject.h"
#include "handlers.h"
#include "device.h"
/** @file noserv.c Handles an unrecognized/unsupported service. */
/** Handler to be invoked when a Service request is received for which no
* handler has been defined.
* @ingroup MISCHNDLR
* This handler builds a Reject response packet, and sends it.
*
* @param service_request [in] The contents of the service request (unused).
* @param service_len [in] The length of the service_request (unused).
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
void handler_unrecognized_service(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
(void) service_request;
(void) service_len;
/* 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);
/* encode the APDU portion of the packet */
len =
reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, REJECT_REASON_UNRECOGNIZED_SERVICE);
pdu_len += len;
/* send the data */
bytes_sent =
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
if (bytes_sent > 0) {
#if PRINT_ENABLED
fprintf(stderr, "Sent Reject!\n");
#endif
} else {
#if PRINT_ENABLED
fprintf(stderr, "Failed to Send Reject (%s)!\n", strerror(errno));
#endif
}
}
+208
View File
@@ -0,0 +1,208 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2008 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include "keylist.h"
#include "objects.h"
/** @file objects.c Manage Device Objects. */
/* list of devices */
static OS_Keylist Device_List = NULL;
void objects_init(
void)
{
if (!Device_List)
Device_List = Keylist_Create();
}
int objects_device_count(
void)
{
objects_init();
return Keylist_Count(Device_List);
}
OBJECT_DEVICE_T *objects_device_data(
int index)
{
objects_init();
return Keylist_Data_Index(Device_List, index);
}
OBJECT_DEVICE_T *objects_device_by_instance(
uint32_t device_instance)
{
objects_init();
return Keylist_Data(Device_List, device_instance);
}
OBJECT_DEVICE_T *objects_device_new(
uint32_t device_instance)
{
OBJECT_DEVICE_T *pDevice = NULL;
KEY key = device_instance;
if (Device_List) {
/* does this device already exist? */
pDevice = Keylist_Data(Device_List, key);
if (pDevice) {
memset(pDevice, 0, sizeof(OBJECT_DEVICE_T));
} else {
pDevice = calloc(1, sizeof(OBJECT_DEVICE_T));
if (pDevice) {
pDevice->Object_Identifier.type = OBJECT_DEVICE;
pDevice->Object_Identifier.instance = device_instance;
pDevice->Object_Type = OBJECT_DEVICE;
pDevice->Object_List = Keylist_Create();
Keylist_Data_Add(Device_List, key, pDevice);
} else {
fprintf(stderr,
"Objects: Unable to allocate device %lu buffer\n",
(unsigned long) device_instance);
}
}
}
return pDevice;
}
OBJECT_DEVICE_T *objects_device_delete(
int index)
{
OBJECT_DEVICE_T *pDevice = NULL;
BACNET_OBJECT_ID *pObject;
if (Device_List) {
pDevice = Keylist_Data_Delete_By_Index(Device_List, index);
if (pDevice) {
fprintf(stderr, "Objects: removing device %lu",
(unsigned long) pDevice->Object_Identifier.instance);
if (pDevice->Object_List) {
do {
pObject =
Keylist_Data_Delete_By_Index(pDevice->Object_List, 0);
/* free any dynamic memory used */
if (pObject) {
free(pObject);
}
} while (pObject);
Keylist_Delete(pDevice->Object_List);
}
free(pDevice);
}
}
return pDevice;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
/* test the object creation and deletion */
void testBACnetObjectsCompare(
Test * pTest,
OBJECT_DEVICE_T * pDevice,
uint32_t device_id)
{
ct_test(pTest, pDevice != NULL);
if (pDevice) {
ct_test(pTest, pDevice->Object_List != NULL);
ct_test(pTest, pDevice->Object_Identifier.instance == device_id);
ct_test(pTest, pDevice->Object_Identifier.type == OBJECT_DEVICE);
ct_test(pTest, pDevice->Object_Type == OBJECT_DEVICE);
}
}
void testBACnetObjects(
Test * pTest)
{
uint32_t device_id = 0;
unsigned test_point = 0;
const unsigned max_test_points = 20;
OBJECT_DEVICE_T *pDevice;
for (test_point = 0; test_point < max_test_points; test_point++) {
device_id = test_point * (BACNET_MAX_INSTANCE / max_test_points);
pDevice = objects_device_new(device_id);
testBACnetObjectsCompare(pTest, pDevice, device_id);
pDevice = objects_device_by_instance(device_id);
testBACnetObjectsCompare(pTest, pDevice, device_id);
}
ct_test(pTest, max_test_points == objects_device_count());
for (test_point = 0; test_point < max_test_points; test_point++) {
device_id = test_point * (BACNET_MAX_INSTANCE / max_test_points);
pDevice = objects_device_by_instance(device_id);
testBACnetObjectsCompare(pTest, pDevice, device_id);
}
for (test_point = 0; test_point < max_test_points; test_point++) {
device_id = test_point * (BACNET_MAX_INSTANCE / max_test_points);
pDevice = objects_device_data(test_point);
testBACnetObjectsCompare(pTest, pDevice, Keylist_Key(Device_List,
test_point));
}
for (test_point = 0; test_point < max_test_points; test_point++) {
pDevice = objects_device_delete(0);
}
}
#ifdef TEST_OBJECT_LIST
int main(
void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Objects", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBACnetObjects);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif
+108
View File
@@ -0,0 +1,108 @@
/**************************************************************************
*
* Copyright (C) 2016 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 <errno.h>
#include <string.h>
#include "config.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "abort.h"
#include "address.h"
#include "tsm.h"
#include "dcc.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "iam.h"
#include "txbuf.h"
/* some demo stuff needed */
#include "handlers.h"
#include "client.h"
/** Encodes an Abort message
* @param buffer The buffer to build the message for sending.
* @param dest - Destination address to send the message
* @param src - Source address from which the message originates
* @param npdu_data - buffer to hold NPDU data encoded
* @param invoke_id - use to match up a reply
* @param reason - #BACNET_ABORT_REASON enumeration
* @param server - true or false
*
* @return Size of the message sent (bytes), or a negative value on error.
*/
int abort_encode_pdu(
uint8_t * buffer,
BACNET_ADDRESS * dest,
BACNET_ADDRESS * src,
BACNET_NPDU_DATA * npdu_data,
uint8_t invoke_id,
BACNET_ABORT_REASON reason,
bool server)
{
int len = 0;
int pdu_len = 0;
/* 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, src, npdu_data);
/* encode the APDU portion of the packet */
len = abort_encode_apdu(&buffer[pdu_len], invoke_id, reason, server);
pdu_len += len;
return pdu_len;
}
/** Sends an Abort message
* @param buffer The buffer to build the message for sending.
* @param dest - Destination address to send the message
* @param invoke_id - use to match up a reply
* @param reason - #BACNET_ABORT_REASON enumeration
* @param server - true or false
*
* @return Size of the message sent (bytes), or a negative value on error.
*/
int Send_Abort_To_Network(
uint8_t * buffer,
BACNET_ADDRESS *dest,
uint8_t invoke_id,
BACNET_ABORT_REASON reason,
bool server)
{
int pdu_len = 0;
BACNET_ADDRESS src;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
datalink_get_my_address(&src);
pdu_len = abort_encode_pdu(buffer, dest, &src, &npdu_data,
invoke_id, reason, server);
bytes_sent = datalink_send_pdu(dest, &npdu_data, &buffer[0], pdu_len);
return bytes_sent;
}
+113
View File
@@ -0,0 +1,113 @@
/**************************************************************************
*
* Copyright (C) 2009 John Minack <minack@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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "whois.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "alarm_ack.h"
#include "client.h"
/** @file s_ack_alarm.c Send an Alarm Acknowledgment. */
/* returns the invoke ID for confirmed request, or zero on failure */
uint8_t Send_Alarm_Acknowledgement(
uint32_t device_id,
BACNET_ALARM_ACK_DATA * data)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
&npdu_data);
len =
alarm_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
data);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send Alarm Ack Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send Alarm Ack Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+120
View File
@@ -0,0 +1,120 @@
/**************************************************************************
*
* 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "dcc.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "arf.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_arfs.c Send part of an Atomic Read File Stream. */
uint8_t Send_Atomic_Read_File_Stream(
uint32_t device_id,
uint32_t file_instance,
int fileStartPosition,
unsigned requestedOctetCount)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
BACNET_NPDU_DATA npdu_data;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_ATOMIC_READ_FILE_DATA data;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* load the data for the encoding */
data.object_type = OBJECT_FILE;
data.object_instance = file_instance;
data.access = FILE_STREAM_ACCESS;
data.type.stream.fileStartPosition = fileStartPosition;
data.type.stream.requestedOctetCount = requestedOctetCount;
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
&npdu_data);
len =
arf_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
&data);
pdu_len += len;
/* will the APDU fit the target device?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send AtomicReadFile Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send AtomicReadFile Request "
"(payload exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+133
View File
@@ -0,0 +1,133 @@
/**************************************************************************
*
* 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "dcc.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "awf.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_awfs.c Send part of an Atomic Write File Stream request. */
uint8_t Send_Atomic_Write_File_Stream(
uint32_t device_id,
uint32_t file_instance,
int fileStartPosition,
BACNET_OCTET_STRING * fileData)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
BACNET_NPDU_DATA npdu_data;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_ATOMIC_WRITE_FILE_DATA data;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* load the data for the encoding */
data.object_type = OBJECT_FILE;
data.object_instance = file_instance;
data.access = FILE_STREAM_ACCESS;
data.type.stream.fileStartPosition = fileStartPosition;
status = octetstring_copy(&data.fileData[0], fileData);
if (status) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest,
&my_address, &npdu_data);
/* encode the APDU portion of the packet */
len =
awf_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
&data);
pdu_len += len;
/* will the APDU fit the target device?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len <= max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0],
(uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send AtomicWriteFile Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send AtomicWriteFile Request "
"(payload [%d] exceeds destination maximum APDU [%u])!\n",
pdu_len, max_apdu);
#endif
}
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send AtomicWriteFile Request "
"(payload [%d] exceeds octet string capacity)!\n", pdu_len);
#endif
}
}
return invoke_id;
}
+114
View File
@@ -0,0 +1,114 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <errno.h>
#include <string.h>
#include "event.h"
#include "device.h"
#include "datalink.h"
#include "tsm.h"
#include "dcc.h"
#include "address.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_cevent.c Send a ConfirmedEventNotification Request. */
/** Sends an Confirmed Alarm/Event Notification.
* @ingroup EVNOTFCN
*
* @param device_id [in] ID of the destination device
* @param data [in] The information about the Event to be sent.
* @return invoke id of outgoing message, or 0 if communication is disabled,
* or no tsm slot is available.
*/
uint8_t Send_CEvent_Notify(
uint32_t device_id,
BACNET_EVENT_NOTIFICATION_DATA * data)
{
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
bool status = false;
uint8_t invoke_id = 0;
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
len =
cevent_notify_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
invoke_id, data);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0) {
fprintf(stderr,
"Failed to Send ConfirmedEventNotification Request (%s)!\n",
strerror(errno));
}
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send ConfirmedEventNotification Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+183
View File
@@ -0,0 +1,183 @@
/**************************************************************************
*
* Copyright (C) 2008 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 <errno.h>
#include <string.h>
#include "config.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "dcc.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "cov.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_cov.c Send a Change of Value (COV) update or a Subscribe COV request. */
/** Encodes an Unconfirmed COV Notification.
* @ingroup DSCOV
*
* @param buffer [in,out] The buffer to build the message in for sending.
* @param buffer_len [in] Number of bytes in the buffer
* @param dest [in] Destination address
* @param npdu_data [in] Network Layer information
* @param cov_data [in] The COV update information to be encoded.
* @return Size of the message sent (bytes), or a negative value on error.
*/
int ucov_notify_encode_pdu(
uint8_t * buffer,
unsigned buffer_len,
BACNET_ADDRESS * dest,
BACNET_NPDU_DATA * npdu_data,
BACNET_COV_DATA * cov_data)
{
int len = 0;
int pdu_len = 0;
BACNET_ADDRESS my_address;
datalink_get_my_address(&my_address);
/* 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, &my_address, npdu_data);
/* encode the APDU portion of the packet */
len = ucov_notify_encode_apdu(&buffer[pdu_len],
buffer_len - pdu_len, cov_data);
if (len) {
pdu_len += len;
} else {
pdu_len = 0;
}
return pdu_len;
}
/** Sends an Unconfirmed COV Notification.
* @ingroup DSCOV
*
* @param buffer [in,out] The buffer to build the message in for sending.
* @param buffer_len [in] Number of bytes in the buffer
* @param cov_data [in] The COV update information to be encoded.
* @return Size of the message sent (bytes), or a negative value on error.
*/
int Send_UCOV_Notify(
uint8_t * buffer,
unsigned buffer_len,
BACNET_COV_DATA * cov_data)
{
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
pdu_len = ucov_notify_encode_pdu(buffer, buffer_len, &dest, &npdu_data,
cov_data);
bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len);
return bytes_sent;
}
/** Sends a COV Subscription request.
* @ingroup DSCOV
*
* @param device_id [in] ID of the destination device
* @param cov_data [in] The COV subscription information to be encoded.
* @return invoke id of outgoing message, or 0 if communication is disabled or
* no slot is available from the tsm for sending.
*/
uint8_t Send_COV_Subscribe(
uint32_t device_id,
BACNET_SUBSCRIBE_COV_DATA * cov_data)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status) {
invoke_id = tsm_next_free_invokeID();
}
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
len =
cov_subscribe_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
sizeof(Handler_Transmit_Buffer)-pdu_len, invoke_id, cov_data);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "Failed to Send SubscribeCOV Request (%s)!\n",
strerror(errno));
#endif
}
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send SubscribeCOV Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+126
View File
@@ -0,0 +1,126 @@
/**************************************************************************
*
* 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_dcc.c Send a Device Communication Control (DCC) request. */
/** Sends a Device Communication Control (DCC) request.
* @ingroup DMDCC
*
* @param device_id [in] The index to the device address in our address cache.
* @param timeDuration [in] If non-zero, the minutes that the remote device
* shall ignore all APDUs except DCC and, if supported, RD APDUs.
* @param state [in] Choice to Enable or Disable communication.
* @param password [in] Optional password, up to 20 chars.
* @return The invokeID of the transmitted message, or 0 on failure.
*/
uint8_t Send_Device_Communication_Control_Request(
uint32_t device_id,
uint16_t timeDuration, /* 0=optional */
BACNET_COMMUNICATION_ENABLE_DISABLE state,
char *password)
{ /* NULL=optional */
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_CHARACTER_STRING password_string;
BACNET_NPDU_DATA npdu_data;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
characterstring_init_ansi(&password_string, password);
len =
dcc_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
timeDuration, state, password ? &password_string : NULL);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send DeviceCommunicationControl Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send DeviceCommunicationControl Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+110
View File
@@ -0,0 +1,110 @@
/**************************************************************************
*
* Copyright (C) 2016 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 <errno.h>
#include <string.h>
#include "config.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "bacenum.h"
#include "bacerror.h"
#include "address.h"
#include "tsm.h"
#include "dcc.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
/* some demo stuff needed */
#include "handlers.h"
#include "client.h"
/** Encodes an Error message
* @param buffer The buffer to build the message for sending.
* @param dest - Destination address to send the message
* @param src - Source address from which the message originates
* @param npdu_data - buffer to hold NPDU data encoded
* @param invoke_id - use to match up a reply
* @param reason - #BACNET_ABORT_REASON enumeration
* @param server - true or false
*
* @return Size of the message sent (bytes), or a negative value on error.
*/
int error_encode_pdu(
uint8_t * buffer,
BACNET_ADDRESS * dest,
BACNET_ADDRESS * src,
BACNET_NPDU_DATA * npdu_data,
uint8_t invoke_id,
BACNET_CONFIRMED_SERVICE service,
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code)
{
int len = 0;
int pdu_len = 0;
/* 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, src, npdu_data);
/* encode the APDU portion of the packet */
len = bacerror_encode_apdu(&buffer[pdu_len], invoke_id,
service, error_class, error_code);
pdu_len += len;
return pdu_len;
}
/** Sends an Abort message
* @param buffer The buffer to build the message for sending.
* @param dest - Destination address to send the message
* @param invoke_id - use to match up a reply
* @param reason - #BACNET_ABORT_REASON enumeration
* @param server - true or false
*
* @return Size of the message sent (bytes), or a negative value on error.
*/
int Send_Error_To_Network(
uint8_t * buffer,
BACNET_ADDRESS *dest,
uint8_t invoke_id,
BACNET_CONFIRMED_SERVICE service,
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code)
{
int pdu_len = 0;
BACNET_ADDRESS src;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
datalink_get_my_address(&src);
pdu_len = error_encode_pdu(buffer, dest, &src, &npdu_data,
invoke_id, service, error_class, error_code);
bytes_sent = datalink_send_pdu(dest, &npdu_data, &buffer[0], pdu_len);
return bytes_sent;
}
+128
View File
@@ -0,0 +1,128 @@
/**
* @file
* @author Daniel Blazevic <daniel.blazevic@gmail.com>
* @date 2014
* @brief Get Alarm Summary Request
*
* @section LICENSE
*
* Copyright (C) 2014 Daniel Blazevic <daniel.blazevic@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @section DESCRIPTION
*
* The Get Alarm Summary Request is used by a client BACnet-user to
* obtain a summary of "active alarms." The term "active alarm" refers to
* BACnet standard objects that have an Event_State property whose value is
* not equal to NORMAL and a Notify_Type property whose value is ALARM.
*/
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "handlers.h"
#include "client.h"
#include "get_alarm_sum.h"
uint8_t Send_Get_Alarm_Summary_Address(
BACNET_ADDRESS *dest,
uint16_t max_apdu)
{
int len = 0;
int pdu_len = 0;
uint8_t invoke_id = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
#if PRINT_ENABLED
int bytes_sent = 0;
#endif
/* is there a tsm available? */
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
datalink_get_my_address(&my_address);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], dest,
&my_address, &npdu_data);
/* encode the APDU portion of the packet */
len = get_alarm_summary_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
invoke_id);
pdu_len += len;
if ((uint16_t) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, dest,
&npdu_data, &Handler_Transmit_Buffer[0],
(uint16_t) pdu_len);
#if PRINT_ENABLED
bytes_sent =
#endif
datalink_send_pdu(dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send Get Alarm Summary Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send Get Alarm Summary Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
uint8_t Send_Get_Alarm_Summary(
uint32_t device_id)
{
BACNET_ADDRESS dest;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
if (status) {
invoke_id = Send_Get_Alarm_Summary_Address(
&dest, max_apdu);
}
return invoke_id;
}
+129
View File
@@ -0,0 +1,129 @@
/**
* @file
* @author Daniel Blazevic <daniel.blazevic@gmail.com>
* @date 2014
* @brief Get Event Request
*
* @section LICENSE
*
* Copyright (C) 2014 Daniel Blazevic <daniel.blazevic@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @section DESCRIPTION
*
* The GetEventInformation service is used by a client BACnet-user to obtain
* a summary of all "active event states". The term "active event states"
* refers to all event-initiating objects that have an Event_State property
* whose value is not equal to NORMAL, or have an Acked_Transitions property,
* which has at least one of the bits (TO-OFFNORMAL, TO-FAULT, TONORMAL)
* set to FALSE.
*/
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "handlers.h"
#include "client.h"
#include "getevent.h"
uint8_t Send_Get_Event_Information_Address(
BACNET_ADDRESS *dest,
uint16_t max_apdu,
BACNET_OBJECT_ID * lastReceivedObjectIdentifier)
{
int len = 0;
int pdu_len = 0;
uint8_t invoke_id = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
#if PRINT_ENABLED
int bytes_sent = 0;
#endif
/* is there a tsm available? */
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
datalink_get_my_address(&my_address);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], dest,
&my_address, &npdu_data);
/* encode the APDU portion of the packet */
len = getevent_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
invoke_id, lastReceivedObjectIdentifier);
pdu_len += len;
if ((uint16_t) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, dest,
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
#if PRINT_ENABLED
bytes_sent =
#endif
datalink_send_pdu(dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send Get Event Information Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send Get Event Information Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
uint8_t Send_Get_Event_Information(
uint32_t device_id,
BACNET_OBJECT_ID * lastReceivedObjectIdentifier)
{
BACNET_ADDRESS dest = {0};
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
if (status) {
invoke_id = Send_Get_Event_Information_Address(
&dest, max_apdu, lastReceivedObjectIdentifier);
}
return invoke_id;
}
+111
View File
@@ -0,0 +1,111 @@
/**************************************************************************
*
* Copyright (C) 2015 bowe
*
* 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "getevent.h"
#include "bacenum.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_getevent.c Send a GetEventInformation request. */
/** Send a GetEventInformation request to a remote network for a specific device, a range,
* or any device.
* @param target_address [in] BACnet address of target or broadcast
*/
uint8_t Send_GetEvent(
BACNET_ADDRESS * target_address,
BACNET_OBJECT_ID * lastReceivedObjectIdentifier)
{
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
uint8_t invoke_id = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
datalink_get_my_address(&my_address);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], target_address,
&my_address, &npdu_data);
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the APDU portion of the packet */
len =
getevent_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, lastReceivedObjectIdentifier);
pdu_len += len;
bytes_sent =
datalink_send_pdu(target_address, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send GetEventInformation Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send GetEventInformation Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
return invoke_id;
}
/** Send a global GetEventInformation request.
*/
uint8_t Send_GetEvent_Global( void )
{
BACNET_ADDRESS dest;
if (!dcc_communication_enabled())
return -1;
datalink_get_broadcast_address(&dest);
return Send_GetEvent(&dest, NULL);
}
+237
View File
@@ -0,0 +1,237 @@
/**************************************************************************
*
* Copyright (C) 2008 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 <errno.h>
#include <string.h>
#include "config.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "dcc.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "iam.h"
#include "txbuf.h"
/* some demo stuff needed */
#include "handlers.h"
#include "client.h"
/** @file s_iam.c Send an I-Am message. */
/** Send a I-Am request to a remote network for a specific device.
* @param target_address [in] BACnet address of target router
* @param device_id [in] Device Instance 0 - 4194303
* @param max_apdu [in] Max APDU 0-65535
* @param segmentation [in] #BACNET_SEGMENTATION enumeration
* @param vendor_id [in] BACnet vendor ID 0-65535
*/
void Send_I_Am_To_Network(
BACNET_ADDRESS * target_address,
uint32_t device_id,
unsigned int max_apdu,
int segmentation,
uint16_t vendor_id)
{
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
datalink_get_my_address(&my_address);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], target_address,
&my_address, &npdu_data);
/* encode the APDU portion of the packet */
/* encode the APDU portion of the packet */
len =
iam_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
device_id, max_apdu, segmentation, vendor_id);
pdu_len += len;
bytes_sent =
datalink_send_pdu(target_address, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "Failed to Send I-Am Request (%s)!\n",
strerror(errno));
#endif
}
}
/** Encode an I Am message to be broadcast.
* @param buffer [in,out] The buffer to use for building the message.
* @param dest [out] The destination address information.
* @param npdu_data [out] The NPDU structure describing the message.
* @return The length of the message in buffer[].
*/
int iam_encode_pdu(
uint8_t * buffer,
BACNET_ADDRESS * dest,
BACNET_NPDU_DATA * npdu_data)
{
int len = 0;
int pdu_len = 0;
BACNET_ADDRESS my_address;
datalink_get_my_address(&my_address);
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, &my_address, 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;
return pdu_len;
}
/** Broadcast an I Am message.
* @ingroup DMDDB
*
* @param buffer [in] The buffer to use for building and sending the message.
*/
void Send_I_Am(
uint8_t * buffer)
{
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
#if 0
/* note: there is discussion in the BACnet committee
that we should allow a device to reply with I-Am
so that dynamic binding always work. If the DCC
initiator loses the MAC address and routing info,
they can never re-enable DCC because they can't
find the device with WhoIs/I-Am */
/* are we are forbidden to send? */
if (!dcc_communication_enabled())
return 0;
#endif
/* encode the data */
pdu_len = iam_encode_pdu(buffer, &dest, &npdu_data);
/* send data */
bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "Failed to Send I-Am Reply (%s)!\n", strerror(errno));
#endif
}
}
/** Encode an I Am message to be unicast directly back to the src.
*
* @param buffer [in,out] The buffer to use for building the message.
* @param src [in] The source address information. If the src address is not
* given, the dest address will be a broadcast address.
* @param dest [out] The destination address information.
* @param npdu_data [out] The NPDU structure describing the message.
* @return The length of the message in buffer[].
*/
int iam_unicast_encode_pdu(
uint8_t * buffer,
BACNET_ADDRESS * src,
BACNET_ADDRESS * dest,
BACNET_NPDU_DATA * npdu_data)
{
int npdu_len = 0;
int apdu_len = 0;
int pdu_len = 0;
BACNET_ADDRESS my_address;
/* The destination will be the same as the src, so copy it over. */
bacnet_address_copy(dest, src);
/* dest->net = 0; - no, must direct back to src->net to meet BTL tests */
datalink_get_my_address(&my_address);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(npdu_data, false, MESSAGE_PRIORITY_NORMAL);
npdu_len = npdu_encode_pdu(&buffer[0], dest, &my_address, npdu_data);
/* encode the APDU portion of the packet */
apdu_len =
iam_encode_apdu(&buffer[npdu_len], Device_Object_Instance_Number(),
MAX_APDU, SEGMENTATION_NONE, Device_Vendor_Identifier());
pdu_len = npdu_len + apdu_len;
return pdu_len;
}
/** Send an I-Am message by unicasting directly back to the src.
* @ingroup DMDDB
* @note As of Addendum 135-2008q-1, unicast responses are allowed;
* in modern firewalled corporate networks, this may be the
* only type of response that will reach the source on
* another subnet (without using the BBMD). <br>
* However, some BACnet routers may not correctly handle this message.
*
* @param buffer [in] The buffer to use for building and sending the message.
* @param src [in] The source address information from service handler.
*/
void Send_I_Am_Unicast(
uint8_t * buffer,
BACNET_ADDRESS * src)
{
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
#if 0
/* note: there is discussion in the BACnet committee
that we should allow a device to reply with I-Am
so that dynamic binding always work. If the DCC
initiator loses the MAC address and routing info,
they can never re-enable DCC because they can't
find the device with WhoIs/I-Am */
/* are we are forbidden to send? */
if (!dcc_communication_enabled())
return 0;
#endif
/* encode the data */
pdu_len = iam_unicast_encode_pdu(buffer, src, &dest, &npdu_data);
/* send data */
bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "Failed to Send I-Am Reply (%s)!\n", strerror(errno));
#endif
}
}
+100
View File
@@ -0,0 +1,100 @@
/**************************************************************************
*
* 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "ihave.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_ihave.c Send an I-Have (property) message. */
/** Broadcast an I Have message.
* @ingroup DMDOB
*
* @param device_id [in] My device ID.
* @param object_type [in] The BACNET_OBJECT_TYPE that I Have.
* @param object_instance [in] The Object ID that I Have.
* @param object_name [in] The Name of the Object I Have.
*/
void Send_I_Have(
uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name)
{
int len = 0;
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_I_HAVE_DATA data;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
datalink_get_my_address(&my_address);
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return;
/* Who-Has 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(&Handler_Transmit_Buffer[0], &dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
data.device_id.type = OBJECT_DEVICE;
data.device_id.instance = device_id;
data.object_id.type = object_type;
data.object_id.instance = object_instance;
characterstring_copy(&data.object_name, object_name);
len = ihave_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &data);
pdu_len += len;
/* send the data */
bytes_sent =
datalink_send_pdu(&dest, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "Failed to Send I-Have Reply (%s)!\n",
strerror(errno));
#endif
}
}
+113
View File
@@ -0,0 +1,113 @@
/**************************************************************************
*
* Copyright (C) 2009 John Minack <minack@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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "whois.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "lso.h"
#include "client.h"
/** @file s_lso.c Send BACnet Life Safety Operation message. */
/* returns the invoke ID for confirmed request, or zero on failure */
uint8_t Send_Life_Safety_Operation_Data(
uint32_t device_id,
BACNET_LSO_DATA * data)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
&npdu_data);
len =
lso_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
data);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send Life Safe Op Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send Life Safe Op Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+151
View File
@@ -0,0 +1,151 @@
/**************************************************************************
*
* 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 <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "ptransfer.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "mydata.h"
#include "client.h"
/** @file s_ptransfer.c Send a Private Transfer request. */
/* This function is exported. Hence this unnecessary prototype. */
uint8_t Send_Private_Transfer_Request(
uint32_t device_id,
uint16_t vendor_id,
uint32_t service_number,
char block_number,
DATABLOCK * block);
uint8_t Send_Private_Transfer_Request(
uint32_t device_id,
uint16_t vendor_id,
uint32_t service_number,
char block_number,
DATABLOCK * block)
{ /* NULL=optional */
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
static uint8_t pt_req_buffer[300]; /* Somewhere to build the request packet */
BACNET_PRIVATE_TRANSFER_DATA pt_block;
BACNET_CHARACTER_STRING bsTemp;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
pt_block.vendorID = vendor_id;
pt_block.serviceNumber = service_number;
if (service_number == MY_SVC_READ) {
len += encode_application_unsigned(&pt_req_buffer[len], block_number); /* The block number we want to retrieve */
} else {
len += encode_application_unsigned(&pt_req_buffer[len], block_number); /* The block number */
len += encode_application_unsigned(&pt_req_buffer[len], block->cMyByte1); /* And Then the block contents */
len +=
encode_application_unsigned(&pt_req_buffer[len],
block->cMyByte2);
len +=
encode_application_real(&pt_req_buffer[len], block->fMyReal);
characterstring_init_ansi(&bsTemp, (char *) block->sMyString);
len +=
encode_application_character_string(&pt_req_buffer[len],
&bsTemp);
}
pt_block.serviceParameters = &pt_req_buffer[0];
pt_block.serviceParametersLen = len;
len =
ptransfer_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
&pt_block);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send Private Transfer Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send Private Transfer Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+123
View File
@@ -0,0 +1,123 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "rd.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_rd.c Send a Reinitialize Device request. */
/** Sends a Reinitialize Device (RD) request.
* @ingroup DMRD
*
* @param device_id [in] The index to the device address in our address cache.
* @param state [in] Specifies the desired state of the device after reinitialization.
* @param password [in] Optional password, up to 20 chars.
* @return The invokeID of the transmitted message, or 0 on failure.
*/
uint8_t Send_Reinitialize_Device_Request(
uint32_t device_id,
BACNET_REINITIALIZED_STATE state,
char *password)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_CHARACTER_STRING password_string;
BACNET_NPDU_DATA npdu_data;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
characterstring_init_ansi(&password_string, password);
len =
rd_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, state,
password ? &password_string : NULL);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send ReinitializeDevice Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send ReinitializeDevice Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+117
View File
@@ -0,0 +1,117 @@
/**************************************************************************
*
* Copyright (C) 2008 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "rpm.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "readrange.h"
#include "client.h"
/** @file s_readrange.c Send a ReadRange request. */
/* returns invoke id of 0 if device is not bound or no tsm available */
uint8_t Send_ReadRange_Request(
uint32_t device_id, /* destination device */
BACNET_READ_RANGE_DATA * read_access_data)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
len =
rr_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
read_access_data);
if (len <= 0) {
return 0;
}
pdu_len += len;
/* is it small enough for the the destination to receive?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send ReadRange Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send ReadRange Request (exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+337
View File
@@ -0,0 +1,337 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "bactext.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
#include "debug.h"
/** @file s_router.c Methods to send various BACnet Router Network Layer Messages. */
/** Initialize an npdu_data structure with given parameters and good defaults,
* and add the Network Layer Message fields.
* The name is a misnomer, as it doesn't do any actual encoding here.
* @see npdu_encode_npdu_data for a simpler version to use when sending an
* APDU instead of a Network Layer Message.
*
* @param npdu_data [out] Returns a filled-out structure with information
* provided by the other arguments and good defaults.
* @param network_message_type [in] The type of Network Layer Message.
* @param data_expecting_reply [in] True if message should have a reply.
* @param priority [in] One of the 4 priorities defined in section 6.2.2,
* like B'11' = Life Safety message
*/
static void npdu_encode_npdu_network(
BACNET_NPDU_DATA * npdu_data,
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
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 = true; /* false if APDU */
npdu_data->network_message_type = network_message_type; /* optional */
npdu_data->vendor_id = 0; /* optional, if net message type is > 0x80 */
npdu_data->priority = priority;
npdu_data->hop_count = HOP_COUNT_DEFAULT;
}
}
/** Function to encode and send any supported Network Layer Message.
* The payload for the message is encoded from information in the iArgs[] array.
* The contents of iArgs are are, per message type:
* - NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: Single int for DNET requested
* - NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: Array of DNET(s) to send,
* terminated with -1
* - NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: array of 2 ints,
* first is reason, second is DNET of interest
* - NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: same as I-Am-Router msg
* - NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: same as I-Am-Router msg
* - NETWORK_MESSAGE_INIT_RT_TABLE and NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
* Array of DNET(s) to process as "Ports", terminated with -1. Each DNET
* will be expanded to a BACNET_ROUTER_PORT (with simple defaults for
* most fields) and encoded.
*
* @param network_message_type [in] The type of message to be sent.
* @param dst [in/out] If not NULL, contains the destination for the message.
* @param iArgs [in] An optional array of values whose meaning depends on
* the type of message.
* @return Number of bytes sent, or <=0 if no message was sent.
*/
int Send_Network_Layer_Message(
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
BACNET_ADDRESS * dst,
int *iArgs)
{
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
int *pVal = iArgs; /* Start with first value */
bool data_expecting_reply = false;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS bcastDest;
if (iArgs == NULL)
return 0; /* Can't do anything here */
/* If dst was NULL, get our (local net) broadcast MAC address. */
if (dst == NULL) {
datalink_get_broadcast_address(&bcastDest);
dst = &bcastDest;
}
if (network_message_type == NETWORK_MESSAGE_INIT_RT_TABLE)
data_expecting_reply = true; /* DER in this one case */
npdu_encode_npdu_network(&npdu_data, network_message_type,
data_expecting_reply, MESSAGE_PRIORITY_NORMAL);
/* We don't need src information, since a message can't originate from
* our downstream BACnet network.
*/
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], dst, NULL, &npdu_data);
/* Now encode the optional payload bytes, per message type */
switch (network_message_type) {
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
if (*pVal >= 0) {
len =
encode_unsigned16(&Handler_Transmit_Buffer[pdu_len],
(uint16_t) * pVal);
pdu_len += len;
}
/* else, don't encode a DNET */
break;
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
while (*pVal >= 0) {
len =
encode_unsigned16(&Handler_Transmit_Buffer[pdu_len],
(uint16_t) * pVal);
pdu_len += len;
pVal++;
}
break;
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK:
/* Encode the Reason byte, then the DNET */
Handler_Transmit_Buffer[pdu_len++] = (uint8_t) * pVal;
pVal++;
len =
encode_unsigned16(&Handler_Transmit_Buffer[pdu_len],
(uint16_t) * pVal);
pdu_len += len;
break;
case NETWORK_MESSAGE_INIT_RT_TABLE:
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
/* First, count the number of Ports we will encode */
len = 0; /* Re-purpose len as our counter here */
while (*pVal >= 0) {
len++;
pVal++;
}
Handler_Transmit_Buffer[pdu_len++] = (uint8_t) len;
if (len > 0) {
uint8_t portID = 1;
pVal = iArgs; /* Reset to beginning */
/* Now encode each (virtual) BACNET_ROUTER_PORT.
* We will simply use a positive index for PortID,
* and have no PortInfo.
*/
while (*pVal >= 0) {
len =
encode_unsigned16(&Handler_Transmit_Buffer[pdu_len],
(uint16_t) * pVal);
pdu_len += len;
Handler_Transmit_Buffer[pdu_len++] = portID++;
Handler_Transmit_Buffer[pdu_len++] = 0;
debug_printf(" Sending Routing Table entry for %u \n",
*pVal);
pVal++;
}
}
break;
default:
debug_printf("Not sent: %s message unsupported \n",
bactext_network_layer_msg_name(network_message_type));
return 0;
break; /* Will never reach this line */
}
if (dst != NULL)
debug_printf("Sending %s message to BACnet network %u \n",
bactext_network_layer_msg_name(network_message_type), dst->net);
else
debug_printf("Sending %s message to local BACnet network \n",
bactext_network_layer_msg_name(network_message_type));
/* Now send the message */
bytes_sent =
datalink_send_pdu(dst, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0) {
int wasErrno = errno; /* preserve the errno */
debug_printf("Failed to send %s message (%s)!\n",
bactext_network_layer_msg_name(network_message_type),
strerror(wasErrno));
}
#endif
return bytes_sent;
}
/** Finds a specific router, or all reachable BACnet networks.
* The response(s) will come in I-am-router-to-network message(s).
* @ingroup NMRC
*
* @param dst [in] If NULL, request will be broadcast to the local BACnet
* network. Optionally may designate a particular router
* destination to respond.
* @param dnet [in] Which BACnet network to request for; if -1, no DNET
* will be sent and the receiving router(s) will send
* their full list of reachable BACnet networks.
*/
void Send_Who_Is_Router_To_Network(
BACNET_ADDRESS * dst,
int dnet)
{
Send_Network_Layer_Message(NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK, dst,
&dnet);
}
/** Broadcast an I-am-router-to-network message, giving the list of networks
* we can reach.
* The message will be sent to our normal DataLink Layer interface,
* not the routed backend.
* @ingroup NMRC
*
* @param DNET_list [in] List of BACnet network numbers for which I am a router,
* terminated with -1
*/
void Send_I_Am_Router_To_Network(
const int DNET_list[])
{
/* Use a NULL dst here since we want a broadcast MAC address. */
Send_Network_Layer_Message(NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, NULL,
(int *) DNET_list);
}
/** Finds a specific router, or all reachable BACnet networks.
* The response(s) will come in I-am-router-to-network message(s).
* @ingroup NMRC
*
* @param dst [in] If NULL, request will be broadcast to the local BACnet
* network. Otherwise, designates a particular router
* destination.
* @param reject_reason [in] One of the BACNET_NETWORK_REJECT_REASONS codes.
* @param dnet [in] Which BACnet network orginated the message.
*/
void Send_Reject_Message_To_Network(
BACNET_ADDRESS * dst,
uint8_t reject_reason,
int dnet)
{
int iArgs[2];
iArgs[0] = reject_reason;
iArgs[1] = dnet;
Send_Network_Layer_Message(NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK, dst,
iArgs);
debug_printf(" Reject Reason=%d, DNET=%u\n", reject_reason, dnet);
}
/** Send an Initialize Routing Table message, built from an optional DNET[]
* array.
* There are two cases here:
* 1) We are requesting a destination router's Routing Table.
* In that case, DNET[] should just have one entry of -1 (no routing table
* is sent).
* 2) We are sending out our Routing Table for some reason (normally bcast it).
* @ingroup NMRC
*
* @param dst [in] If NULL, msg will be broadcast to the local BACnet network.
* Optionally may designate a particular router destination,
* especially when requesting a Routing Table.
* @param DNET_list [in] List of BACnet network numbers for which I am a router,
* terminated with -1. Will be just -1 when we are
* requesting a routing table.
*/
void Send_Initialize_Routing_Table(
BACNET_ADDRESS * dst,
const int DNET_list[])
{
/* Use a NULL dst here since we want a broadcast MAC address. */
Send_Network_Layer_Message(NETWORK_MESSAGE_INIT_RT_TABLE, dst,
(int *) DNET_list);
}
/** Sends our Routing Table, built from our DNET[] array, as an ACK.
* There are two cases here:
* 1) We are responding to a NETWORK_MESSAGE_INIT_RT_TABLE requesting our table.
* We will normally broadcast that response.
* 2) We are ACKing the receipt of a NETWORK_MESSAGE_INIT_RT_TABLE containing a
* routing table, and then we will want to respond to that dst router.
* In that case, DNET[] should just have one entry of -1 (no routing table
* is sent).
* @ingroup NMRC
*
* @param dst [in] If NULL, Ack will be broadcast to the local BACnet network.
* Optionally may designate a particular router destination,
* especially when ACKing receipt of this message type.
* @param DNET_list [in] List of BACnet network numbers for which I am a router,
* terminated with -1. May be just -1 when no table
* should be sent.
*/
void Send_Initialize_Routing_Table_Ack(
BACNET_ADDRESS * dst,
const int DNET_list[])
{
Send_Network_Layer_Message(NETWORK_MESSAGE_INIT_RT_TABLE_ACK, dst,
(int *) DNET_list);
}
+167
View File
@@ -0,0 +1,167 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "rp.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_rp.c Send Read Property request. */
/** Sends a Read Property request
* @ingroup DSRP
*
* @param dest [in] BACNET_ADDRESS of the destination device
* @param max_apdu [in]
* @param object_type [in] Type of the object whose property is to be read.
* @param object_instance [in] Instance # of the object to be read.
* @param object_property [in] Property to be read, but not ALL, REQUIRED, or OPTIONAL.
* @param array_index [in] Optional: if the Property is an array,
* - 0 for the array size
* - 1 to n for individual array members
* - BACNET_ARRAY_ALL (~0) for the full array to be read.
* @return invoke id of outgoing message, or 0 if device is not bound or no tsm available
*/
uint8_t Send_Read_Property_Request_Address(
BACNET_ADDRESS * dest,
uint16_t max_apdu,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
uint32_t array_index)
{
BACNET_ADDRESS my_address;
uint8_t invoke_id = 0;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_READ_PROPERTY_DATA data;
BACNET_NPDU_DATA npdu_data;
if (!dcc_communication_enabled()) {
return 0;
}
if (!dest) {
return 0;
}
/* is there a tsm available? */
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
data.object_type = object_type;
data.object_instance = object_instance;
data.object_property = object_property;
data.array_index = array_index;
len =
rp_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
&data);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((uint16_t) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, dest,
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "Failed to Send ReadProperty Request (%s)!\n",
strerror(errno));
#endif
}
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send ReadProperty Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
/** Sends a Read Property request.
* @ingroup DSRP
*
* @param device_id [in] ID of the destination device
* @param object_type [in] Type of the object whose property is to be read.
* @param object_instance [in] Instance # of the object to be read.
* @param object_property [in] Property to be read, but not ALL, REQUIRED, or OPTIONAL.
* @param array_index [in] Optional: if the Property is an array,
* - 0 for the array size
* - 1 to n for individual array members
* - BACNET_ARRAY_ALL (~0) for the full array to be read.
* @return invoke id of outgoing message, or 0 if device is not bound or no tsm available
*/
uint8_t Send_Read_Property_Request(
uint32_t device_id, /* destination device */
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
uint32_t array_index)
{
BACNET_ADDRESS dest = { 0 };
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
if (status) {
invoke_id =
Send_Read_Property_Request_Address(&dest, max_apdu, object_type,
object_instance, object_property, array_index);
}
return invoke_id;
}
+123
View File
@@ -0,0 +1,123 @@
/**************************************************************************
*
* Copyright (C) 2008 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "rpm.h"
/* some demo stuff needed */
#include "handlers.h"
#include "sbuf.h"
#include "client.h"
/** @file s_rpm.c Send Read Property Multiple request. */
/** Sends a Read Property Multiple request.
* @ingroup DSRPM
*
* @param pdu [out] Buffer to build the outgoing message into
* @param max_pdu [in] Length of the pdu buffer.
* @param device_id [in] ID of the destination device
* @param read_access_data [in] Ptr to structure with the linked list of
* properties to be read.
* @return invoke id of outgoing message, or 0 if device is not bound or no tsm available
*/
uint8_t Send_Read_Property_Multiple_Request(
uint8_t * pdu,
size_t max_pdu,
uint32_t device_id, /* destination device */
BACNET_READ_ACCESS_DATA * read_access_data)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&pdu[0], &dest, &my_address, &npdu_data);
/* encode the APDU portion of the packet */
len =
rpm_encode_apdu(&pdu[pdu_len], max_pdu - pdu_len, invoke_id,
read_access_data);
if (len <= 0) {
return 0;
}
pdu_len += len;
/* is it small enough for the destination to receive?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &pdu[0], (uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data, &pdu[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send ReadPropertyMultiple Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send ReadPropertyMultiple Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+195
View File
@@ -0,0 +1,195 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "timesync.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_ts.c Send TimeSync requests. */
/**
* Sends a TimeSync message to a specific destination
*
* @param dest - #BACNET_ADDRESS - the specific destination
* @param bdate - #BACNET_DATE
* @param btime - #BACNET_TIME
*/
void Send_TimeSync_Remote(
BACNET_ADDRESS * dest,
BACNET_DATE * bdate,
BACNET_TIME * btime)
{
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
if (!dcc_communication_enabled())
return;
datalink_get_my_address(&my_address);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
len =
timesync_encode_apdu(&Handler_Transmit_Buffer[pdu_len], bdate, btime);
pdu_len += len;
/* send it out the datalink */
bytes_sent =
datalink_send_pdu(dest, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send Time-Synchronization Request (%s)!\n",
strerror(errno));
#endif
}
/**
* Sends a TimeSync message as a broadcast
*
* @param bdate - #BACNET_DATE
* @param btime - #BACNET_TIME
*/
void Send_TimeSync(
BACNET_DATE * bdate,
BACNET_TIME * btime)
{
BACNET_ADDRESS dest;
datalink_get_broadcast_address(&dest);
Send_TimeSync_Remote(&dest, bdate, btime);
}
/**
* Sends a UTC TimeSync message to a specific destination
*
* @param dest - #BACNET_ADDRESS - the specific destination
* @param bdate - #BACNET_DATE
* @param btime - #BACNET_TIME
*/
void Send_TimeSyncUTC_Remote(
BACNET_ADDRESS * dest,
BACNET_DATE * bdate,
BACNET_TIME * btime)
{
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
if (!dcc_communication_enabled())
return;
datalink_get_my_address(&my_address);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
len =
timesync_utc_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
bdate, btime);
pdu_len += len;
bytes_sent =
datalink_send_pdu(dest, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send UTC-Time-Synchronization Request (%s)!\n",
strerror(errno));
#endif
}
/**
* Sends a UTC TimeSync message as a broadcast
*
* @param bdate - #BACNET_DATE
* @param btime - #BACNET_TIME
*/
void Send_TimeSyncUTC(
BACNET_DATE * bdate,
BACNET_TIME * btime)
{
BACNET_ADDRESS dest;
datalink_get_broadcast_address(&dest);
Send_TimeSyncUTC_Remote(&dest, bdate, btime);
}
/**
* Sends a UTC TimeSync message using the local time from the device.
*/
void Send_TimeSyncUTC_Device(void)
{
int32_t utc_offset_minutes = 0;
bool dst = false;
BACNET_DATE_TIME local_time;
BACNET_DATE_TIME utc_time;
Device_getCurrentDateTime(&local_time);
dst = Device_Daylight_Savings_Status();
utc_offset_minutes = Device_UTC_Offset();
datetime_copy(&utc_time, &local_time);
datetime_add_minutes(&utc_time, utc_offset_minutes);
if (dst) {
datetime_add_minutes(&utc_time, -60);
}
Send_TimeSyncUTC(&utc_time.date, &utc_time.time);
}
/**
* Sends a TimeSync message using the local time from the device.
*/
void Send_TimeSync_Device(void)
{
BACNET_DATE_TIME local_time;
Device_getCurrentDateTime(&local_time);
Send_TimeSync(&local_time.date, &local_time.time);
}
+65
View File
@@ -0,0 +1,65 @@
/**************************************************************************
*
* Copyright (C) 2008 John Minack
*
* 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 <errno.h>
#include "event.h"
#include "datalink.h"
#include "client.h"
#include "device.h"
/** @file s_uevent.c Send an Unconfirmed Event Notification. */
/** Sends an Unconfirmed Alarm/Event Notification.
* @ingroup EVNOTFCN
*
* @param buffer [in,out] The buffer to build the message in for sending.
* @param data [in] The information about the Event to be sent.
* @param dest [in] The destination address information (may be a broadcast).
* @return Size of the message sent (bytes), or a negative value on error.
*/
int Send_UEvent_Notify(
uint8_t * buffer,
BACNET_EVENT_NOTIFICATION_DATA * data,
BACNET_ADDRESS * dest)
{
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
datalink_get_my_address(&my_address);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(buffer, dest, &my_address, &npdu_data);
/* encode the APDU portion of the packet */
len = uevent_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;
}
+85
View File
@@ -0,0 +1,85 @@
/**************************************************************************
*
* Copyright (C) 2009 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "ptransfer.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_upt.c Send an Unconfirmed Private Transfer request. */
int Send_UnconfirmedPrivateTransfer(
BACNET_ADDRESS * dest,
BACNET_PRIVATE_TRANSFER_DATA * private_data)
{
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
if (!dcc_communication_enabled())
return bytes_sent;
datalink_get_my_address(&my_address);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
len =
uptransfer_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
private_data);
pdu_len += len;
bytes_sent =
datalink_send_pdu(dest, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send UnconfirmedPrivateTransfer Request (%s)!\n",
strerror(errno));
#endif
}
return bytes_sent;
}
+154
View File
@@ -0,0 +1,154 @@
/**************************************************************************
*
* 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "whohas.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_whohas.c Send Who-Has requests. */
/** Send a Who-Has request for a device which has a named Object.
* @ingroup DMDOB
* If low_limit and high_limit both are -1, then the device ID range is unlimited.
* If low_limit and high_limit have the same non-negative value, then only
* that device will respond.
* Otherwise, low_limit must be less than high_limit for a range.
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
* @param object_name [in] The Name of the desired Object.
*/
void Send_WhoHas_Name(
int32_t low_limit,
int32_t high_limit,
const char *object_name)
{
int len = 0;
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_WHO_HAS_DATA data;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return;
/* Who-Has is a global broadcast */
datalink_get_broadcast_address(&dest);
datalink_get_my_address(&my_address);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
data.low_limit = low_limit;
data.high_limit = high_limit;
data.is_object_name = true;
characterstring_init_ansi(&data.object.name, object_name);
len = whohas_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &data);
pdu_len += len;
/* send the data */
bytes_sent =
datalink_send_pdu(&dest, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send Who-Has Request (%s)!\n",
strerror(errno));
#endif
}
/** Send a Who-Has request for a device which has a specific Object type and ID.
* @ingroup DMDOB
* If low_limit and high_limit both are -1, then the device ID range is unlimited.
* If low_limit and high_limit have the same non-negative value, then only
* that device will respond.
* Otherwise, low_limit must be less than high_limit for a range.
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
* @param object_type [in] The BACNET_OBJECT_TYPE of the desired Object.
* @param object_instance [in] The ID of the desired Object.
*/
void Send_WhoHas_Object(
int32_t low_limit,
int32_t high_limit,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance)
{
int len = 0;
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_WHO_HAS_DATA data;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return;
/* Who-Has is a global broadcast */
datalink_get_broadcast_address(&dest);
datalink_get_my_address(&my_address);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
data.low_limit = low_limit;
data.high_limit = high_limit;
data.is_object_name = false;
data.object.identifier.type = object_type;
data.object.identifier.instance = object_instance;
len = whohas_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &data);
pdu_len += len;
bytes_sent =
datalink_send_pdu(&dest, &npdu_data, &Handler_Transmit_Buffer[0],
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send Who-Has Request (%s)!\n",
strerror(errno));
#endif
}
+197
View File
@@ -0,0 +1,197 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "whois.h"
#include "bacenum.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_whois.c Send a Who-Is request. */
/** Send a Who-Is request to a remote network for a specific device, a range,
* or any device.
* If low_limit and high_limit both are -1, then the range is unlimited.
* If low_limit and high_limit have the same non-negative value, then only
* that device will respond.
* Otherwise, low_limit must be less than high_limit.
* @param target_address [in] BACnet address of target router
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
*/
void Send_WhoIs_To_Network(
BACNET_ADDRESS * target_address,
int32_t low_limit,
int32_t high_limit)
{
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
datalink_get_my_address(&my_address);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], target_address,
&my_address, &npdu_data);
/* encode the APDU portion of the packet */
len =
whois_encode_apdu(&Handler_Transmit_Buffer[pdu_len], low_limit,
high_limit);
pdu_len += len;
bytes_sent =
datalink_send_pdu(target_address, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send Who-Is Request (%s)!\n",
strerror(errno));
#endif
}
/** Send a global Who-Is request for a specific device, a range, or any device.
* If low_limit and high_limit both are -1, then the range is unlimited.
* If low_limit and high_limit have the same non-negative value, then only
* that device will respond.
* Otherwise, low_limit must be less than high_limit.
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
*/
void Send_WhoIs_Global(
int32_t low_limit,
int32_t high_limit)
{
BACNET_ADDRESS dest;
if (!dcc_communication_enabled())
return;
/* Who-Is is a global broadcast */
datalink_get_broadcast_address(&dest);
Send_WhoIs_To_Network(&dest, low_limit, high_limit);
}
/** Send a local Who-Is request for a specific device, a range, or any device.
* If low_limit and high_limit both are -1, then the range is unlimited.
* If low_limit and high_limit have the same non-negative value, then only
* that device will respond.
* Otherwise, low_limit must be less than high_limit.
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
*/
void Send_WhoIs_Local(
int32_t low_limit,
int32_t high_limit)
{
BACNET_ADDRESS dest;
char temp[6];
int loop;
if (!dcc_communication_enabled())
return;
/* Who-Is is a global broadcast */
datalink_get_broadcast_address(&dest);
/* encode the NPDU portion of the packet */
/* I added this to make it a local broadcast */
dest.net = 0;
/* Not sure why this happens but values are backwards so they need to be reversed */
temp[0] = dest.mac[3];
temp[1] = dest.mac[2];
temp[2] = dest.mac[1];
temp[3] = dest.mac[0];
temp[4] = dest.mac[5];
temp[5] = dest.mac[4];
for (loop = 0; loop < 6; loop++) {
dest.mac[loop] = temp[loop];
}
Send_WhoIs_To_Network(&dest, low_limit, high_limit);
}
/** Send a Who-Is request to a remote network for a specific device, a range,
* or any device.
* @ingroup DMDDB
* If low_limit and high_limit both are -1, then the range is unlimited.
* If low_limit and high_limit have the same non-negative value, then only
* that device will respond.
* Otherwise, low_limit must be less than high_limit.
* @param target_address [in] BACnet address of target router
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
*/
void Send_WhoIs_Remote(
BACNET_ADDRESS * target_address,
int32_t low_limit,
int32_t high_limit)
{
if (!dcc_communication_enabled())
return;
Send_WhoIs_To_Network(target_address, low_limit, high_limit);
}
/** Send a global Who-Is request for a specific device, a range, or any device.
* @ingroup DMDDB
* This was the original Who-Is broadcast but the code was moved to the more
* descriptive Send_WhoIs_Global when Send_WhoIs_Local and Send_WhoIsRemote was
* added.
* If low_limit and high_limit both are -1, then the range is unlimited.
* If low_limit and high_limit have the same non-negative value, then only
* that device will respond.
* Otherwise, low_limit must be less than high_limit.
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
*/
void Send_WhoIs(
int32_t low_limit,
int32_t high_limit)
{
Send_WhoIs_Global(low_limit, high_limit);
}
+176
View File
@@ -0,0 +1,176 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <errno.h>
#include <string.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "whois.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
#include "client.h"
/** @file s_wp.c Send a Write Property request. */
/** returns the invoke ID for confirmed request, or zero on failure */
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,
uint32_t array_index)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_WRITE_PROPERTY_DATA data;
BACNET_NPDU_DATA npdu_data;
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
&npdu_data);
/* encode the APDU portion of the packet */
data.object_type = object_type;
data.object_instance = object_instance;
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);
data.priority = priority;
len =
wp_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
&data);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send WriteProperty Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send WriteProperty Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
/** Sends a Write Property request.
* @ingroup DSWP
*
* @param device_id [in] ID of the destination device
* @param object_type [in] Type of the object whose property is to be written.
* @param object_instance [in] Instance # of the object to be written.
* @param object_property [in] Property to be written.
* @param object_value [in] The value to be written to the property.
* @param priority [in] Write priority of 1 (highest) to 16 (lowest)
* @param array_index [in] Optional: if the Property is an array,
* - 0 for the array size
* - 1 to n for individual array members
* - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent)
* @return invoke id of outgoing message, or 0 on failure.
*/
uint8_t Send_Write_Property_Request(
uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
BACNET_APPLICATION_DATA_VALUE * object_value,
uint8_t priority,
uint32_t array_index)
{
uint8_t application_data[MAX_APDU] = { 0 };
int apdu_len = 0, len = 0;
while (object_value) {
#if PRINT_ENABLED_DEBUG
fprintf(stderr, "WriteProperty service: " "%s tag=%d\n",
(object_value->context_specific ? "context" : "application"),
(int) (object_value->
context_specific ? object_value->context_tag : object_value->
tag));
#endif
len = bacapp_encode_data(&application_data[apdu_len], object_value);
if ((len + apdu_len) < MAX_APDU) {
apdu_len += len;
} else {
return 0;
}
object_value = object_value->next;
}
return Send_Write_Property_Request_Data(device_id, object_type,
object_instance, object_property, &application_data[0], apdu_len,
priority, array_index);
}
+127
View File
@@ -0,0 +1,127 @@
/**
* @file
* @author Daniel Blazevic <daniel.blazevic@gmail.com>
* @date 2013
* @brief Send Write Property Multiple request
*
* @section LICENSE
*
* Copyright (C) 2013 Daniel Blazevic <daniel.blazevic@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include "config.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "wpm.h"
/* some demo stuff needed */
#include "handlers.h"
#include "sbuf.h"
#include "client.h"
/** @file s_wpm.c Send Write Property Multiple request. */
/** Sends a Write Property Multiple request.
* @param pdu [out] Buffer to build the outgoing message into
* @param max_pdu [in] Length of the pdu buffer.
* @param device_id [in] ID of the destination device
* @param write_access_data [in] Ptr to structure with the linked list of
* objects and properties to be written.
* @return invoke id of outgoing message, or 0 if device is not bound or no tsm available
*/
uint8_t Send_Write_Property_Multiple_Request(
uint8_t * pdu,
size_t max_pdu,
uint32_t device_id,
BACNET_WRITE_ACCESS_DATA * write_access_data)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled()) {
return 0;
}
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status) {
invoke_id = tsm_next_free_invokeID();
}
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&pdu[0], &dest, &my_address,&npdu_data);
/* encode the APDU portion of the packet */
len =
wpm_encode_apdu(&pdu[pdu_len], max_pdu - pdu_len,
invoke_id, write_access_data);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &pdu[0], (uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&pdu[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0) {
fprintf(stderr,
"Failed to Send WritePropertyMultiple Request (%s)!\n",
strerror(errno));
}
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send WritePropertyMultiple Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+32
View File
@@ -0,0 +1,32 @@
/**************************************************************************
*
* Copyright (C) 2005 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 "config.h"
#include "datalink.h"
/** @file txbuf.c Declare the global Transmit Buffer for handler functions. */
uint8_t Handler_Transmit_Buffer[MAX_PDU] = { 0 };