Updated Demo Who-Is to work with remote networks and report duplicate device-identifiers
This commit is contained in:
+357
-111
@@ -28,6 +28,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
#include "bactext.h"
|
||||
@@ -49,16 +50,131 @@
|
||||
#include "rs485.h"
|
||||
#endif
|
||||
#include "dlenv.h"
|
||||
#include "net.h"
|
||||
|
||||
/* buffer used for receive */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/* global variables used in this file */
|
||||
static int32_t Target_Object_Instance_Min = 0;
|
||||
static int32_t Target_Object_Instance_Max = BACNET_MAX_INSTANCE;
|
||||
|
||||
static int32_t Target_Object_Instance_Min = -1;
|
||||
static int32_t Target_Object_Instance_Max = -1;
|
||||
static int32_t Target_Network = BACNET_BROADCAST_NETWORK;
|
||||
static char Target_Mac[7];
|
||||
static uint32_t Target_MacLen = 0;
|
||||
static bool verbose = true;
|
||||
static bool Error_Detected = false;
|
||||
|
||||
#define BAC_ADDRESS_MULT 1
|
||||
|
||||
struct address_entry {
|
||||
struct address_entry * next;
|
||||
uint8_t Flags;
|
||||
uint32_t device_id;
|
||||
unsigned max_apdu;
|
||||
BACNET_ADDRESS address;
|
||||
};
|
||||
|
||||
static struct address_table {
|
||||
struct address_entry * first;
|
||||
struct address_entry * last;
|
||||
} addr_tbl = {0};
|
||||
|
||||
|
||||
struct address_entry * alloc_address_entry(void)
|
||||
{
|
||||
struct address_entry * rval;
|
||||
rval = (struct address_entry *)calloc(1, sizeof(struct address_entry));
|
||||
if(addr_tbl.first == 0)
|
||||
{
|
||||
addr_tbl.first = addr_tbl.last = rval;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr_tbl.last->next = rval;
|
||||
addr_tbl.last = rval;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool bacnet_address_matches(BACNET_ADDRESS * a1, BACNET_ADDRESS * a2)
|
||||
{
|
||||
int i = 0;
|
||||
if(a1->net != a2->net) return false;
|
||||
if(a1->len != a2->len) return false;
|
||||
for(;i<a1->len;i++)
|
||||
if(a1->adr[i]!=a2->adr[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void address_table_add(
|
||||
uint32_t device_id,
|
||||
unsigned max_apdu,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
struct address_entry *pMatch;
|
||||
uint8_t flags = 0;
|
||||
|
||||
pMatch = addr_tbl.first;
|
||||
while (pMatch) {
|
||||
if (pMatch->device_id == device_id)
|
||||
{
|
||||
if (bacnet_address_matches(&pMatch->address, src))
|
||||
return;
|
||||
flags |= BAC_ADDRESS_MULT;
|
||||
pMatch->Flags |= BAC_ADDRESS_MULT;
|
||||
}
|
||||
pMatch = pMatch->next;
|
||||
}
|
||||
|
||||
pMatch = alloc_address_entry();
|
||||
|
||||
pMatch->Flags = flags;
|
||||
pMatch->device_id = device_id;
|
||||
pMatch->max_apdu = max_apdu;
|
||||
pMatch->address = *src;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void my_i_am_handler(
|
||||
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_table_add(device_id, max_apdu, src);
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, ", but unable to decode it.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void MyAbortHandler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
@@ -87,7 +203,7 @@ void MyRejectHandler(
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
static void Init_Service_Handlers(
|
||||
static void init_service_handlers(
|
||||
void)
|
||||
{
|
||||
Device_Init(NULL);
|
||||
@@ -101,69 +217,189 @@ static void Init_Service_Handlers(
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
handler_read_property);
|
||||
/* handle the reply (request) coming back */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_add);
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, my_i_am_handler);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
static void print_address_cache(
|
||||
void)
|
||||
void print_macaddr(uint8_t * addr, int len)
|
||||
{
|
||||
int i, j;
|
||||
BACNET_ADDRESS address;
|
||||
uint32_t device_id = 0;
|
||||
unsigned max_apdu = 0;
|
||||
unsigned total_addresses = 0;
|
||||
|
||||
printf(";%-7s %-20s %-5s %-20s %-4s\n", "Device", "MAC", "SNET", "SADR",
|
||||
"APDU");
|
||||
printf(";------- -------------------- ----- -------------------- ----\n");
|
||||
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
|
||||
if (address_get_by_index(i, &device_id, &max_apdu, &address)) {
|
||||
total_addresses++;
|
||||
printf(" %-7u ", device_id);
|
||||
for (j = 0; j < MAX_MAC_LEN; j++) {
|
||||
if (j < address.mac_len) {
|
||||
if (j > 0) {
|
||||
printf(":");
|
||||
}
|
||||
printf("%02X", address.mac[j]);
|
||||
} else {
|
||||
if (j > 0) {
|
||||
printf(" ");
|
||||
}
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
printf(" %-5hu ", address.net);
|
||||
if (address.net) {
|
||||
for (j = 0; j < MAX_MAC_LEN; j++) {
|
||||
if (j < address.len) {
|
||||
if (j > 0) {
|
||||
printf(":");
|
||||
}
|
||||
printf("%02X", address.adr[j]);
|
||||
} else {
|
||||
if (j > 0) {
|
||||
printf(" ");
|
||||
}
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("0 ");
|
||||
for (j = 1; j < MAX_MAC_LEN; j++) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
printf(" %-4hu ", max_apdu);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf(";\n; Total Devices: %u\n", total_addresses);
|
||||
int j = 0;
|
||||
if(len == 0)
|
||||
{
|
||||
printf("%20s", "");
|
||||
return;
|
||||
}
|
||||
if(len == 1)
|
||||
{
|
||||
printf("%-6u%14s", addr[0], "");
|
||||
return;
|
||||
}
|
||||
while(j < len)
|
||||
{
|
||||
if (j != 0) {
|
||||
printf(":");
|
||||
}
|
||||
printf("%02X", addr[j++]);
|
||||
}
|
||||
while(j++ < MAX_MAC_LEN)
|
||||
{
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_address_cache(
|
||||
void)
|
||||
{
|
||||
int j;
|
||||
BACNET_ADDRESS address;
|
||||
unsigned total_addresses = 0;
|
||||
unsigned dup_addresses = 0;
|
||||
struct address_entry *addr;
|
||||
|
||||
printf(";%-7s %-20s %-5s %-20s %-4s\n", "Device", "MAC", "SNET", "SADR",
|
||||
"APDU");
|
||||
printf(";-------- -------------------- ----- -------------------- ----\n");
|
||||
|
||||
|
||||
addr = addr_tbl.first;
|
||||
while (addr)
|
||||
{
|
||||
address = addr->address;
|
||||
|
||||
total_addresses++;
|
||||
if(addr->Flags & BAC_ADDRESS_MULT)
|
||||
{
|
||||
dup_addresses++;
|
||||
printf("*");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf(" ");
|
||||
}
|
||||
printf(" %-7u ", addr->device_id);
|
||||
print_macaddr(address.mac, address.mac_len);
|
||||
printf(" %-5hu ", address.net);
|
||||
if (address.net) {
|
||||
print_macaddr(address.adr, address.len);
|
||||
} else {
|
||||
printf("0 ");
|
||||
for (j = 1; j < MAX_MAC_LEN; j++) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
printf(" %-4hu ", addr->max_apdu);
|
||||
printf("\n");
|
||||
|
||||
addr = addr->next;
|
||||
}
|
||||
printf(";\n; Total Devices: %u\n", total_addresses);
|
||||
if (dup_addresses)
|
||||
printf("; * Duplicate Devices: %u\n", dup_addresses);
|
||||
}
|
||||
|
||||
int print_usage(char* exe_name){
|
||||
printf(
|
||||
"Usage:\n"
|
||||
"\n"
|
||||
"%s [[network]:[address]] [device-instance-min [device-instance-max]] [--help]\n" ,
|
||||
exe_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int print_help(char* exe_name){
|
||||
printf(
|
||||
"Usage:\n"
|
||||
"\n"
|
||||
"%s [[network]:[address]] [low_devid [high_devid]] [--help]\n"
|
||||
"\n"
|
||||
" Send BACnet WhoIs service request to a device or multiple devices, and wait\n"
|
||||
" for responses. Displays any devices found an their network information.\n"
|
||||
"\n"
|
||||
"device-instance:\r\n"
|
||||
" BACnet Device Object Instance number that you are trying to send a Who-Is\n"
|
||||
" service request. The value should be in the range of 0 to 4194303. A range\n"
|
||||
" of values can also be specified by using a minimum value and a maximum value.\n"
|
||||
"\n"
|
||||
"network:\n"
|
||||
" BACnet network number for directed requests. Valid range is from 0 to 65535\n"
|
||||
" where 0 is the local connection and 65535 is network broadcast.\n"
|
||||
"\n"
|
||||
"address:\n"
|
||||
" BACnet mac address number. Valid ranges are from 0 to 255 or a IP connection \n"
|
||||
" string including port number like 10.1.2.3:47808.\n"
|
||||
"\n"
|
||||
"Examples:\n\n"
|
||||
"To send a WhoIs request to Network 123:\n"
|
||||
"%s 123:\n\n"
|
||||
"To send a WhoIs request to Network 123 Address 5:\n"
|
||||
"%s 123:5\n\n"
|
||||
"To send a WhoIs request to Device 123:\n"
|
||||
"%s 123\n\n"
|
||||
"To send a WhoIs request to Devices from 1000 to 9000:\n"
|
||||
"%s 1000 9000\n\n"
|
||||
"To send a WhoIs request to Devices from 1000 to 9000 on Network 123:\n"
|
||||
"%s 123: 1000 9000\n\n"
|
||||
"To send a WhoIs request to all devices:\n"
|
||||
"%s\n\n",
|
||||
exe_name,exe_name,exe_name,exe_name,exe_name,exe_name,exe_name,exe_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Parse a string for a bacnet-address
|
||||
**
|
||||
** @return length of address parsed in bytes
|
||||
*/
|
||||
int parse_bac_address(
|
||||
BACNET_ADDRESS *dest, /* [out] BACNET Address*/
|
||||
char *src /* [in] nul terminated string to parse */
|
||||
)
|
||||
{
|
||||
int i = 0;
|
||||
int z = 0;
|
||||
uint16_t s;
|
||||
int a[4],p;
|
||||
int c = sscanf(src,"%u.%u.%u.%u:%u",&a[0],&a[1],&a[2],&a[3],&p);
|
||||
|
||||
dest->len = 0;
|
||||
|
||||
if (c==1)
|
||||
{
|
||||
if( a[0] < 256 ) /* mstp */
|
||||
{
|
||||
dest->adr[0] = a[0];
|
||||
dest->len = 1;
|
||||
}
|
||||
else if ( a[0] < 0x0FFFF ) /* lon */
|
||||
{
|
||||
s = htons((uint16_t)a[0]);
|
||||
memcpy(&dest->adr[0], &s, 2);
|
||||
dest->len = 2;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else if (c==5) /* ip address */
|
||||
{
|
||||
for(i=0;i<4;i++)
|
||||
{
|
||||
if(a[i] == 0 || a[i] > 255)
|
||||
return 0;
|
||||
|
||||
dest->adr[i] = a[i];
|
||||
}
|
||||
s = htons((uint16_t)p);
|
||||
memcpy(&dest->adr[i], &s, 2);
|
||||
dest->len = 6;
|
||||
}
|
||||
return dest->len;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char *argv[])
|
||||
@@ -178,60 +414,70 @@ int main(
|
||||
time_t last_seconds = 0;
|
||||
time_t current_seconds = 0;
|
||||
time_t timeout_seconds = 0;
|
||||
BACNET_ADDRESS dest;
|
||||
int argi;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: %s device-instance | "
|
||||
"device-instance-min device-instance-max\r\n",
|
||||
filename_remove_path(argv[0]));
|
||||
return 0;
|
||||
}
|
||||
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
|
||||
printf("Send BACnet WhoIs service request to a device or\r\n"
|
||||
"multiple devices, and wait for responses. Displays\r\n"
|
||||
"any devices found and their network information.\r\n"
|
||||
"\r\ndevice-instance:\r\n"
|
||||
"BACnet Device Object Instance number that you are trying to\r\n"
|
||||
"send a Who-Is service request. The value should be in\r\n"
|
||||
"the range of 0 to 4194303. A range of values can also be\r\n"
|
||||
"specified by using a minimum value and a maximum value.\r\n"
|
||||
"\r\nExample:\r\n" "To send a WhoIs request to Device 123\r\n"
|
||||
"use the following command:\r\n" "%s 123\r\n"
|
||||
"To send a WhoIs request to Devices from 1000 to 9000:\r\n"
|
||||
"%s 1000 9000\r\n" "To send a WhoIs request to all devices\r\n"
|
||||
"use the following command:\r\n" "%s -1\r\n",
|
||||
filename_remove_path(argv[0]), filename_remove_path(argv[0]),
|
||||
filename_remove_path(argv[0]));
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameters */
|
||||
if (argc < 3) {
|
||||
Target_Object_Instance_Min = strtol(argv[1], NULL, 0);
|
||||
Target_Object_Instance_Max = Target_Object_Instance_Min;
|
||||
if (Target_Object_Instance_Min > BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr,
|
||||
"object-instance-min=%u - it must be less than %u\r\n",
|
||||
Target_Object_Instance_Min, BACNET_MAX_INSTANCE + 1);
|
||||
return 1;
|
||||
}
|
||||
/* print help if requested */
|
||||
for (argi = 1; argi < argc; argi++)
|
||||
{
|
||||
if (strcmp(argv[argi], "--help") == 0) {
|
||||
print_help(filename_remove_path(argv[0]));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
datalink_get_broadcast_address(&dest);
|
||||
|
||||
/* decode the command line parameters */
|
||||
if (argc >= 2) {
|
||||
char *s;
|
||||
long v = strtol(argv[1], &s, 0);
|
||||
if(*s++ == ':')
|
||||
{
|
||||
if(argv[1][0] != ':')
|
||||
dest.net = (uint16_t) v;
|
||||
dest.mac_len = 0;
|
||||
if(isdigit(*s))
|
||||
parse_bac_address(&dest,s);
|
||||
}
|
||||
else
|
||||
{
|
||||
Target_Object_Instance_Min = v;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc <= 2){
|
||||
/* empty */
|
||||
} else if (argc == 3){
|
||||
if(Target_Object_Instance_Min == -1)
|
||||
Target_Object_Instance_Min =
|
||||
Target_Object_Instance_Max = strtol(argv[2], NULL, 0);
|
||||
else
|
||||
Target_Object_Instance_Max = strtol(argv[2], NULL, 0);
|
||||
} else if (argc == 4){
|
||||
Target_Object_Instance_Min = strtol(argv[2], NULL, 0);
|
||||
Target_Object_Instance_Max = strtol(argv[3], NULL, 0);
|
||||
} else {
|
||||
Target_Object_Instance_Min = strtol(argv[1], NULL, 0);
|
||||
Target_Object_Instance_Max = strtol(argv[2], NULL, 0);
|
||||
if (Target_Object_Instance_Min > BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr,
|
||||
"object-instance-min=%u - it must be less than %u\r\n",
|
||||
Target_Object_Instance_Min, BACNET_MAX_INSTANCE + 1);
|
||||
return 1;
|
||||
}
|
||||
if (Target_Object_Instance_Max > BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr,
|
||||
"object-instance-max=%u - it must be less than %u\r\n",
|
||||
Target_Object_Instance_Max, BACNET_MAX_INSTANCE + 1);
|
||||
return 1;
|
||||
}
|
||||
print_usage(filename_remove_path(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
/* setup my info */
|
||||
|
||||
if (Target_Object_Instance_Min > BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr,
|
||||
"object-instance-min=%u - it must be less than %u\r\n",
|
||||
Target_Object_Instance_Min, BACNET_MAX_INSTANCE + 1);
|
||||
return 1;
|
||||
}
|
||||
if (Target_Object_Instance_Max > BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr,
|
||||
"object-instance-max=%u - it must be less than %u\r\n",
|
||||
Target_Object_Instance_Max, BACNET_MAX_INSTANCE + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
Init_Service_Handlers();
|
||||
init_service_handlers();
|
||||
address_init();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
@@ -239,8 +485,8 @@ int main(
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = apdu_timeout() / 1000;
|
||||
/* send the request */
|
||||
Send_WhoIs(Target_Object_Instance_Min, Target_Object_Instance_Max);
|
||||
/* loop forever */
|
||||
Send_WhoIs_To_Network(&dest,Target_Object_Instance_Min, Target_Object_Instance_Max);
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* increment timer - exit if timed out */
|
||||
current_seconds = time(NULL);
|
||||
|
||||
Reference in New Issue
Block a user