b91735af13
* Enable extra GCC warnings to discover subtle bugs * convert c++ comments to c comments * cleanup pedantic compiler warnings * Compile apps with GNU89 GNU99 GNU11 and GNU17 Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
231 lines
8.8 KiB
C
231 lines
8.8 KiB
C
/**
|
|
* @file
|
|
* @author Steve Karg
|
|
* @date 2022
|
|
* @brief Application to acquire data from a target client
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include "bacnet/config.h"
|
|
#include "bacnet/bacdef.h"
|
|
#include "bacnet/bacenum.h"
|
|
#include "bacnet/bactext.h"
|
|
#include "bacnet/version.h"
|
|
#include "bacnet/basic/sys/filename.h"
|
|
#include "bacnet/basic/sys/debug.h"
|
|
#include "bacnet/basic/sys/mstimer.h"
|
|
#include "bacnet/basic/client/bac-task.h"
|
|
#include "bacnet/basic/client/bac-data.h"
|
|
#include "bacnet/basic/object/device.h"
|
|
#include "bacnet/datalink/datalink.h"
|
|
#include "bacnet/datalink/dlenv.h"
|
|
|
|
/* print with flush by default */
|
|
#define PRINTF debug_aprintf
|
|
|
|
/* current version of the BACnet stack */
|
|
static const char *BACnet_Version = BACNET_VERSION_TEXT;
|
|
|
|
static void print_usage(const char *filename)
|
|
{
|
|
PRINTF("Usage: %s [device-instance]\n", filename);
|
|
PRINTF(" [object-type] [object-instance]\n");
|
|
PRINTF(" [--device][--print-seconds]\n");
|
|
PRINTF(" [--version][--help]\n");
|
|
}
|
|
|
|
static void print_help(const char *filename)
|
|
{
|
|
PRINTF("Simulate a BACnet server-client device.\n");
|
|
PRINTF("device-instance:\n"
|
|
"BACnet Device Object Instance number that you are\n"
|
|
"trying to communicate to. This number will be used\n"
|
|
"to try and bind with the device using Who-Is and\n"
|
|
"I-Am services. For example, if you were reading\n"
|
|
"Device Object 123, the device-instance would be 123.\n");
|
|
PRINTF("\n");
|
|
PRINTF("object-type:\n"
|
|
"The object type is object that you are reading. It\n"
|
|
"can be defined either as the object-type name string\n"
|
|
"as defined in the BACnet specification, or as the\n"
|
|
"integer value of the enumeration BACNET_OBJECT_TYPE\n"
|
|
"in bacenum.h. For example if you were reading Analog\n"
|
|
"Output 2, the object-type would be analog-output or 1.\n");
|
|
PRINTF("\n");
|
|
PRINTF("object-instance:\n"
|
|
"This is the object instance number of the object that\n"
|
|
"you are reading. For example, if you were reading\n"
|
|
"Analog Output 2, the object-instance would be 2.\n");
|
|
PRINTF("\n");
|
|
PRINTF("Example:\n"
|
|
"If you want read the Present-Value of Analog Output 101\n"
|
|
"in Device 123, you could send either of the following\n"
|
|
"commands:\n"
|
|
"%s 123 analog-output 101\n"
|
|
"%s 123 1 101\n"
|
|
"If you want read the Present-Value of Binary Input 1\n"
|
|
"in Device 123, you could send either of the following\n"
|
|
"commands:\n"
|
|
"%s 123 binary-input 1\n"
|
|
"%s 123 3 1\n",
|
|
filename, filename, filename, filename);
|
|
}
|
|
|
|
/**
|
|
* @brief Main function of server-client demo.
|
|
* @param argc [in] Arg count.
|
|
* @param argv [in] Takes one argument: the Device Instance #.
|
|
* @return 0 on success.
|
|
*/
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int argi = 0;
|
|
unsigned int target_args = 0;
|
|
const char *filename = NULL;
|
|
uint32_t device_id = BACNET_MAX_INSTANCE;
|
|
struct mstimer print_value_timer = { 0 };
|
|
float float_value = 0.0;
|
|
bool bool_value = false;
|
|
unsigned object_type = 0;
|
|
uint32_t unsigned_value = 0;
|
|
/* data from the command line */
|
|
unsigned long print_seconds = 10;
|
|
uint32_t target_device_object_instance = BACNET_MAX_INSTANCE;
|
|
uint32_t target_object_instance = BACNET_MAX_INSTANCE;
|
|
BACNET_OBJECT_TYPE target_object_type = OBJECT_ANALOG_INPUT;
|
|
|
|
filename = filename_remove_path(argv[0]);
|
|
for (argi = 1; argi < argc; argi++) {
|
|
if (strcmp(argv[argi], "--help") == 0) {
|
|
print_usage(filename);
|
|
print_help(filename);
|
|
return 0;
|
|
}
|
|
if (strcmp(argv[argi], "--version") == 0) {
|
|
PRINTF("%s %s\n", filename, BACNET_VERSION_TEXT);
|
|
PRINTF("Copyright (C) 2022 by Steve Karg and others.\n"
|
|
"This is free software; see the source for copying "
|
|
"conditions.\n"
|
|
"There is NO warranty; not even for MERCHANTABILITY or\n"
|
|
"FITNESS FOR A PARTICULAR PURPOSE.\n");
|
|
return 0;
|
|
}
|
|
if (strcmp(argv[argi], "--device") == 0) {
|
|
if (++argi < argc) {
|
|
device_id = strtol(argv[argi], NULL, 0);
|
|
if (device_id > BACNET_MAX_INSTANCE) {
|
|
fprintf(stderr, "device=%u - it must be less than %u\n",
|
|
device_id, BACNET_MAX_INSTANCE);
|
|
return 1;
|
|
}
|
|
}
|
|
} else if (strcmp(argv[argi], "--print-seconds") == 0) {
|
|
if (++argi < argc) {
|
|
print_seconds = strtol(argv[argi], NULL, 0);
|
|
}
|
|
} else {
|
|
if (target_args == 0) {
|
|
target_device_object_instance = strtol(argv[argi], NULL, 0);
|
|
target_args++;
|
|
} else if (target_args == 1) {
|
|
if (bactext_object_type_strtol(argv[argi], &object_type) ==
|
|
false) {
|
|
fprintf(stderr, "object-type=%s invalid\n", argv[argi]);
|
|
return 1;
|
|
}
|
|
target_object_type = object_type;
|
|
target_args++;
|
|
} else if (target_args == 2) {
|
|
target_object_instance = strtol(argv[argi], NULL, 0);
|
|
target_args++;
|
|
} else {
|
|
print_usage(filename);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
if (target_args < 2) {
|
|
print_usage(filename);
|
|
return 0;
|
|
}
|
|
Device_Set_Object_Instance_Number(device_id);
|
|
if (target_device_object_instance > BACNET_MAX_INSTANCE) {
|
|
fprintf(stderr, "device-instance=%u - it must be less than %u\n",
|
|
target_device_object_instance, BACNET_MAX_INSTANCE);
|
|
return 1;
|
|
}
|
|
PRINTF("BACnet Server-Client Demo\n"
|
|
"BACnet Stack Version %s\n"
|
|
"BACnet Device ID: %u\n"
|
|
"Max APDU: %d\n",
|
|
BACnet_Version, Device_Object_Instance_Number(), MAX_APDU);
|
|
fflush(stdout);
|
|
dlenv_init();
|
|
atexit(datalink_cleanup);
|
|
bacnet_task_init();
|
|
bacnet_data_poll_seconds_set(print_seconds);
|
|
if (!bacnet_data_object_add(target_device_object_instance,
|
|
target_object_type, target_object_instance)) {
|
|
return 1;
|
|
}
|
|
mstimer_set(&print_value_timer, print_seconds * 1000);
|
|
/* loop forever */
|
|
for (;;) {
|
|
bacnet_task();
|
|
if (mstimer_expired(&print_value_timer)) {
|
|
mstimer_reset(&print_value_timer);
|
|
switch (target_object_type) {
|
|
case OBJECT_ANALOG_INPUT:
|
|
case OBJECT_ANALOG_OUTPUT:
|
|
case OBJECT_ANALOG_VALUE:
|
|
if (bacnet_data_analog_present_value(
|
|
target_device_object_instance, target_object_type,
|
|
target_object_instance, &float_value)) {
|
|
PRINTF("Device %u %s-%u=%f\n",
|
|
(unsigned)target_device_object_instance,
|
|
bactext_object_type_name(target_object_type),
|
|
(unsigned)target_object_instance, float_value);
|
|
}
|
|
break;
|
|
case OBJECT_BINARY_INPUT:
|
|
case OBJECT_BINARY_OUTPUT:
|
|
case OBJECT_BINARY_VALUE:
|
|
if (bacnet_data_binary_present_value(
|
|
target_device_object_instance, target_object_type,
|
|
target_object_instance, &bool_value)) {
|
|
PRINTF("Device %u %s-%u=%s\n",
|
|
(unsigned)target_device_object_instance,
|
|
bactext_object_type_name(target_object_type),
|
|
(unsigned)target_object_instance,
|
|
bool_value ? "active" : "inactive");
|
|
}
|
|
break;
|
|
case OBJECT_MULTI_STATE_INPUT:
|
|
case OBJECT_MULTI_STATE_OUTPUT:
|
|
case OBJECT_MULTI_STATE_VALUE:
|
|
if (bacnet_data_multistate_present_value(
|
|
target_device_object_instance, target_object_type,
|
|
target_object_instance, &unsigned_value)) {
|
|
PRINTF("Device %u %s-%u=%u\n",
|
|
(unsigned)target_device_object_instance,
|
|
bactext_object_type_name(target_object_type),
|
|
(unsigned)target_object_instance,
|
|
(unsigned)unsigned_value);
|
|
}
|
|
break;
|
|
default:
|
|
return 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|