adjust root folder
This commit is contained in:
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,
|
||||
¤t_date_time->date,
|
||||
¤t_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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 };
|
||||
Reference in New Issue
Block a user