--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/server.c Mon Jun 11 08:34:15 2018 +0200
@@ -0,0 +1,645 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* 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 <stdlib.h>
+#include <signal.h>
+#include <time.h>
+#include <inttypes.h> // uint32_t, ..., PRIu32, ...
+
+#include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz pluginh */
+#include "server_%(locstr)s.h"
+#include "address.h"
+#include "bacdef.h"
+#include "handlers.h"
+#include "client.h"
+#include "dlenv.h"
+#include "bacdcode.h"
+#include "npdu.h"
+#include "apdu.h"
+#include "iam.h"
+#include "tsm.h"
+#include "datalink.h"
+#include "dcc.h"
+#include "getevent.h"
+#include "net.h"
+#include "txbuf.h"
+#include "version.h"
+#include "timesync.h"
+
+
+/* A utility function used by most (all?) implementations of BACnet Objects */
+/* Adds to Prop_List all entries in Prop_List_XX that are not
+ * PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PROPERTY_LIST
+ * and returns the number of elements that were added
+ */
+int BACnet_Init_Properties_List(
+ int *Prop_List,
+ const int *Prop_List_XX) {
+
+ unsigned int i = 0;
+ unsigned int j = 0;
+
+ for (j = 0; Prop_List_XX[j] >= 0; j++)
+ // Add any propety, except for the following 4 which should not be included
+ // in the Property_List property array.
+ // (see ASHRAE 135-2016, for example section 12.4.34)
+ if ((Prop_List_XX[j] != PROP_OBJECT_IDENTIFIER) &&
+ (Prop_List_XX[j] != PROP_OBJECT_NAME) &&
+ (Prop_List_XX[j] != PROP_OBJECT_TYPE) &&
+ (Prop_List_XX[j] != PROP_PROPERTY_LIST)) {
+ Prop_List[i] = Prop_List_XX[j];
+ i++;
+ }
+ Prop_List[i] = -1; // marks the end of the list!
+ return i;
+}
+
+
+
+
+
+
+int BACnet_encode_character_string(uint8_t *apdu, const char *str) {
+ BACNET_CHARACTER_STRING char_string;
+ characterstring_init_ansi(&char_string, str);
+ /* FIXME: this might go beyond MAX_APDU length! */
+ return encode_application_character_string(apdu, &char_string);
+}
+
+/* macro that always returns false.
+ * To be used as the 'test_null' parameter to the BACnet_encode_array macro
+ * in situations where we should never encode_null() values.
+ */
+#define retfalse(x) (false)
+
+#define BACnet_encode_array(array, array_len, test_null, encode_function) \
+{ \
+ uint8_t *apdu = NULL; \
+ apdu = rpdata->application_data; \
+ \
+ switch (rpdata->array_index) { \
+ case 0: /* Array element zero is the number of elements in the array */ \
+ apdu_len = encode_application_unsigned(&apdu[0], array_len); \
+ break; \
+ case BACNET_ARRAY_ALL: { \
+ /* if no index was specified, then try to encode the entire list */ \
+ unsigned i = 0; \
+ apdu_len = 0; \
+ for (i = 0; i < array_len; i++) { \
+ /* FIXME: this might go beyond MAX_APDU length! */ \
+ if (!test_null(array[i])) \
+ apdu_len += encode_function (&apdu[apdu_len], array[i]); \
+ else apdu_len += encode_application_null(&apdu[apdu_len]); \
+ /* return error if it does not fit in the APDU */ \
+ if (apdu_len >= MAX_APDU) { \
+ rpdata->error_class = ERROR_CLASS_SERVICES; \
+ rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; \
+ apdu_len = BACNET_STATUS_ERROR; \
+ break; /* for(;;) */ \
+ } \
+ } \
+ break; \
+ } \
+ default: \
+ if (rpdata->array_index <= array_len) { \
+ if (!test_null(array[rpdata->array_index - 1])) \
+ apdu_len += encode_function(&apdu[0], array[rpdata->array_index - 1]); \
+ else apdu_len += encode_application_null(&apdu[0]); \
+ } else { \
+ rpdata->error_class = ERROR_CLASS_PROPERTY; \
+ rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; \
+ apdu_len = BACNET_STATUS_ERROR; \
+ } \
+ break; \
+ } /* switch() */ \
+}
+
+
+/* include the device object */
+#include "device_%(locstr)s.c"
+#include "ai_%(locstr)s.c"
+#include "ao_%(locstr)s.c"
+#include "av_%(locstr)s.c"
+#include "bi_%(locstr)s.c"
+#include "bo_%(locstr)s.c"
+#include "bv_%(locstr)s.c"
+#include "msi_%(locstr)s.c"
+#include "mso_%(locstr)s.c"
+#include "msv_%(locstr)s.c"
+
+
+/** Buffer used for receiving */
+static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
+
+
+
+
+/*******************************************************/
+/* BACnet Service Handlers taylored to Beremiz plugin */
+/*******************************************************/
+
+static void BACNET_show_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");
+}
+
+static time_t __timegm(struct tm *new_time) {
+ time_t sec = mktime(new_time); /* assume new_time is in local time */
+
+ /* sec will be an aproximation of the correct value.
+ * We must now fix this with the current difference
+ * between UTC and localtime.
+ *
+ * WARNING: The following algorithm to determine the current
+ * difference between local time and UTC will not
+ * work if each value (lcl and utc) falls on a different
+ * side of a change to/from DST.
+ * For example, assume a change to DST is made at 12h00
+ * of day X. The following algorithm does not work if:
+ * - lcl falls before 12h00 of day X
+ * - utc falls after 12h00 of day X
+ */
+ struct tm lcl = *localtime(&sec); // lcl will be == new_time
+ struct tm utc = *gmtime (&sec);
+
+ if (lcl.tm_isdst == 1) utc.tm_isdst = 1;
+
+ time_t sec_lcl = mktime(&lcl);
+ time_t sec_utc = mktime(&utc);
+
+ /* difference in seconds between localtime and utc */
+ time_t sec_dif = sec_lcl - sec_utc;
+ return sec + sec_dif;
+}
+
+
+static void BACNET_set_date_time(
+ BACNET_DATE * bdate,
+ BACNET_TIME * btime,
+ int utc /* set to > 0 if date & time in UTC */)
+{
+ struct tm brokendown_time;
+ time_t seconds = 0;
+ struct timespec ts;
+
+ brokendown_time.tm_sec = btime->sec; /* seconds 0..60 */
+ brokendown_time.tm_min = btime->min; /* minutes 0..59 */
+ brokendown_time.tm_hour = btime->hour; /* hours 0..23 */
+ brokendown_time.tm_mday = bdate->day; /* day of the month 1..31 */
+ brokendown_time.tm_mon = bdate->month-1; /* month 0..11 */
+ brokendown_time.tm_year = bdate->year-1900; /* years since 1900 */
+// brokendown_time.tm_wday = ; /* day of the week */
+// brokendown_time.tm_yday = ; /* day in the year */
+ brokendown_time.tm_isdst = -1; /* daylight saving time (-1 => unknown) */
+
+ // Tranform time into format -> 'seconds since epoch'
+ /* WARNING: timegm() is a non standard GNU extension.
+ * If you do not have it on your build system then consider
+ * finding the source code for timegm() (it is LGPL) from GNU
+ * C library and copying it here
+ * (e.g. https://code.woboq.org/userspace/glibc/time/timegm.c.html)
+ * Another alternative is to use the fundion __timegm() above,
+ * which will mostly work but may have errors when the time being
+ * converted is close to the time in the year when changing
+ * to/from DST (daylight saving time)
+ */
+ if (utc > 0) seconds = timegm(&brokendown_time);
+ else seconds = mktime(&brokendown_time);
+
+ ts.tv_sec = seconds;
+ ts.tv_nsec = btime->hundredths*10*1000*1000;
+
+// fprintf(stderr, "clock_settime() s=%%ul, ns=%%u\n", ts.tv_sec, ts.tv_nsec);
+ clock_settime(CLOCK_REALTIME, &ts);
+// clock_gettime(CLOCK_REALTIME, &ts);
+// fprintf(stderr, "clock_gettime() s=%%ul, ns=%%u\n", ts.tv_sec, ts.tv_nsec);
+}
+
+
+
+void BACnet_handler_timesync(
+ 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) {
+ fprintf(stderr, "BACnet plugin: Received TimeSyncronization Request -> ");
+ BACNET_show_date_time(&bdate, &btime);
+ /* set the time */
+ BACNET_set_date_time(&bdate, &btime, 0 /* time in local time */);
+ }
+
+ return;
+}
+
+void BACnet_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) {
+ fprintf(stderr, "BACnet plugin: Received TimeSyncronizationUTC Request -> ");
+ BACNET_show_date_time(&bdate, &btime);
+ /* set the time */
+ BACNET_set_date_time(&bdate, &btime, 1 /* time in UTC */);
+ }
+
+ return;
+}
+
+
+
+/**********************************************/
+/** Initialize the handlers we will utilize. **/
+/**********************************************/
+/*
+ * TLDR: The functions that will create the __Resp.__ messages.
+ *
+ * The service handlers are the functions that will respond to BACnet requests this device receives.
+ * In essence, the service handlers will create and send the Resp. (Response) messages
+ * of the Req. -> Ind. -> Resp. -> Conf. service sequence defined in OSI
+ * (Request, Indication, Response, Confirmation)
+ *
+ */
+static int Init_Service_Handlers(
+ void)
+{
+ /* set the handler for all the services we don't implement */
+ /* It is required to send the proper reject message... */
+ apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service);
+
+ /* Set the handlers for any unconfirmed services that we support. */
+ apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, // DM-DDB-B - Dynamic Device Binding B (Resp.)
+ handler_who_is); // (see ASHRAE 135-2016, section K5.1 and K5.2)
+// apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, // DM-DDB-A - Dynamic Device Binding A (Resp.)
+// handler_i_am_bind); // Responding to I_AM requests is for clients (A)!
+// // (see ASHRAE 135-2016, section K5.1 and K5.2)
+ apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, // DM-DOB-B - Dynamic Object Binding B (Resp.)
+ handler_who_has); // (see ASHRAE 135-2016, section K5.3 and K5.4)
+// apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_HAVE, // DM-DOB-A - Dynamic Object Binding A (Resp.)
+// handler_i_have); // Responding to I_HAVE requests is for clients (A)!
+// // (see ASHRAE 135-2016, section K5.3 and K5.4)
+ apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, // DM-UTC-B -UTCTimeSynchronization-B (Resp.)
+ BACnet_handler_timesync_utc); // (see ASHRAE 135-2016, section K5.14)
+ apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, // DM-TS-B - TimeSynchronization-B (Resp.)
+ BACnet_handler_timesync); // (see ASHRAE 135-2016, section K5.12)
+
+ /* Set the handlers for any confirmed services that we support. */
+ apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, // DS-RP-B - Read Property B (Resp.)
+ handler_read_property); // (see ASHRAE 135-2016, section K1.2)
+// apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, // DS-RPM-B -Read Property Multiple-B (Resp.)
+// handler_read_property_multiple); // (see ASHRAE 135-2016, section K1.4)
+ apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, // DS-WP-B - Write Property B (Resp.)
+ handler_write_property); // (see ASHRAE 135-2016, section K1.8)
+// apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE,// DS-WPM-B -Write Property Multiple B (Resp.)
+// handler_write_property_multiple); // (see ASHRAE 135-2016, section K1.10)
+// apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_RANGE,
+// handler_read_range);
+// apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE,
+// handler_reinitialize_device);
+
+// apdu_set_confirmed_handler(SERVICE_CONFIRMED_SUBSCRIBE_COV,
+// handler_cov_subscribe);
+// apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION,
+// handler_ucov_notification);
+ /* handle communication so we can shutup when asked */
+ apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, // DM-DCC-B - Device Communication Control B
+ handler_device_communication_control); // (see ASHRAE 135-2016, section K5.6)
+// /* handle the data coming back from private requests */
+// apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_PRIVATE_TRANSFER,
+// handler_unconfirmed_private_transfer);
+
+ // success
+ return 0;
+}
+
+
+
+static int Init_Network_Interface(
+ const char *interface, // for linux: /dev/eth0, /dev/eth1, /dev/wlan0, ...
+ // for windows: 192.168.0.1 (IP addr. of interface)
+ // NULL => use default!
+ const char *port, // Port the server will listen on. (NULL => use default)
+ const char *apdu_timeout, // (NULL => use default)
+ const char *apdu_retries // (NULL => use default)
+ )
+{
+ char datalink_layer[4];
+
+ strcpy(datalink_layer, "BIP"); // datalink_set() does not accpet const char *, so we copy it...
+// BIP_Debug = true;
+
+ if (port != NULL) {
+ bip_set_port(htons((uint16_t) strtol(port, NULL, 0)));
+ } else {
+ bip_set_port(htons(0xBAC0));
+ }
+
+ if (apdu_timeout != NULL) {
+ apdu_timeout_set((uint16_t) strtol(apdu_timeout, NULL, 0));
+ }
+
+ if (apdu_retries != NULL) {
+ apdu_retries_set((uint8_t) strtol(apdu_retries, NULL, 0));
+ }
+
+ // datalink_init is a pointer that will actually call bip_init()
+ // datalink_init pointer is set by the call datalink_set("BIP")
+ /* NOTE: current implementation of BACnet stack uses the interface
+ * only to determine the server's local IP address and broacast address.
+ * The local IP addr is later used to discard (broadcast) messages
+ * it receives that were sent by itself.
+ * The broadcast IP addr is used for broadcast messages.
+ * WARNING: The socket itself is created to listen on all
+ * available interfaces (INADDR_ANY), so this setting may induce
+ * the user in error as we will accept messages arriving on other
+ * interfaces (if they exist)
+ * (see bip_init() in ports/linux/bip-init.c)
+ * (see bip_****() in src/bip.c)
+ */
+ char *tmp = (char *)malloc(strlen(interface) + 1);
+ if (tmp == NULL) return -1;
+ strncpy(tmp, interface, strlen(interface) + 1);
+ if (!datalink_init(tmp)) {
+ return -1;
+ }
+// #if (MAX_TSM_TRANSACTIONS)
+// pEnv = getenv("BACNET_INVOKE_ID");
+// if (pEnv) {
+// tsm_invokeID_set((uint8_t) strtol(pEnv, NULL, 0));
+// }
+// #endif
+ dlenv_register_as_foreign_device();
+
+ // success
+ return 0;
+}
+
+
+
+/** Main function of server demo. **/
+int bn_server_run(server_node_t *server_node) {
+ int res = 0;
+ BACNET_ADDRESS src = {0}; /* address where message came from */
+ uint16_t pdu_len = 0;
+ unsigned timeout = 1000; /* milliseconds */
+ time_t last_seconds = 0;
+ time_t current_seconds = 0;
+ uint32_t elapsed_seconds = 0;
+ uint32_t elapsed_milliseconds = 0;
+ uint32_t address_binding_tmr = 0;
+ uint32_t recipient_scan_tmr = 0;
+
+ /* allow the device ID to be set */
+ Device_Set_Object_Instance_Number(server_node->device_id);
+ /* load any static address bindings in our device bindings list */
+ address_init();
+ /* load any entries in the BDT table from backup file */
+ bvlc_bdt_restore_local();
+ /* Initiliaze the bacnet server 'device' */
+ Device_Init(server_node->device_name);
+ /* Set the password (max 31 chars) for Device Communication Control request. */
+ /* Default in the BACnet stack is hardcoded as "filister" */
+ /* (char *) cast is to remove the cast. The function is incorrectly declared/defined in the BACnet stack! */
+ /* BACnet stack needs to change demo/handler/h_dcc.c and include/handlers.h */
+ handler_dcc_password_set((char *)server_node->comm_control_passwd);
+ /* Set callbacks and configure network interface */
+ res = Init_Service_Handlers();
+ if (res < 0) exit(1);
+ res = Init_Network_Interface(
+ server_node->network_interface, // interface (NULL => use default (eth0))
+ server_node->port_number, // Port number (NULL => use default (0xBAC0))
+ NULL, // apdu_timeout (NULL => use default)
+ NULL // apdu_retries (NULL => use default)
+ );
+ if (res < 0) {
+ fprintf(stderr, "BACnet plugin: error initializing bacnet server node %%s!\n", server_node->location);
+ exit(1); // kill the server thread!
+ }
+ /* BACnet stack correcly configured. Give user some feedback! */
+ struct in_addr my_addr, broadcast_addr;
+ my_addr. s_addr = bip_get_addr();
+ broadcast_addr.s_addr = bip_get_broadcast_addr();
+ printf("BACnet plugin:"
+ " Local IP addr: %%s"
+ ", Broadcast IP addr: %%s"
+ ", Port number: 0x%%04X [%%hu]"
+ ", BACnet Device ID: %%d"
+ ", Max APDU: %%d"
+ ", BACnet Stack Version %%s\n",
+ inet_ntoa(my_addr),
+ inet_ntoa(broadcast_addr),
+ ntohs(bip_get_port()), ntohs(bip_get_port()),
+ Device_Object_Instance_Number(),
+ MAX_APDU,
+ Device_Firmware_Revision()
+ );
+ /* configure the timeout values */
+ last_seconds = time(NULL);
+ /* broadcast an I-Am on startup */
+ Send_I_Am(&Handler_Transmit_Buffer[0]);
+ /* loop forever */
+ for (;;) {
+ /* input */
+ current_seconds = time(NULL);
+
+ /* returns 0 bytes on timeout */
+ pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
+
+ /* process */
+ if (pdu_len) {
+ npdu_handler(&src, &Rx_Buf[0], pdu_len);
+ }
+ /* at least one second has passed */
+ elapsed_seconds = (uint32_t) (current_seconds - last_seconds);
+ if (elapsed_seconds) {
+ last_seconds = current_seconds;
+ dcc_timer_seconds(elapsed_seconds);
+ bvlc_maintenance_timer(elapsed_seconds);
+ dlenv_maintenance_timer(elapsed_seconds);
+ elapsed_milliseconds = elapsed_seconds * 1000;
+ tsm_timer_milliseconds(elapsed_milliseconds);
+ }
+ handler_cov_task();
+ /* scan cache address */
+ address_binding_tmr += elapsed_seconds;
+ if (address_binding_tmr >= 60) {
+ address_cache_timer(address_binding_tmr);
+ address_binding_tmr = 0;
+ }
+ }
+
+ /* should never occur!! */
+ return 0;
+}
+
+
+
+
+
+#include <pthread.h>
+
+static void *__bn_server_thread(void *_server_node) {
+ server_node_t *server_node = _server_node;
+
+ // Enable thread cancelation. Enabled is default, but set it anyway to be safe.
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+
+ // bn_server_run() should never return!
+ bn_server_run(server_node);
+ fprintf(stderr, "BACnet plugin: bacnet server for node %%s died unexpectedly!\n", server_node->location); /* should never occur */
+ return NULL;
+}
+
+
+int __cleanup_%(locstr)s ();
+
+int __init_%(locstr)s (int argc, char **argv){
+ int index;
+
+ /* init each local server */
+ /* NOTE: All server_nodes[].init_state are initialised to 0 in the code
+ * generated by the BACnet plugin
+ */
+ /* create the BACnet server */
+ server_node.init_state = 1; // we have created the node
+
+ /* launch a thread to handle this server node */
+ {
+ int res = 0;
+ pthread_attr_t attr;
+ res |= pthread_attr_init(&attr);
+ res |= pthread_create(&(server_node.thread_id), &attr, &__bn_server_thread, (void *)&(server_node));
+ if (res != 0) {
+ fprintf(stderr, "BACnet plugin: Error starting bacnet server thread for node %%s\n", server_node.location);
+ goto error_exit;
+ }
+ }
+ server_node.init_state = 2; // we have created the node and thread
+
+ return 0;
+
+error_exit:
+ __cleanup_%(locstr)s ();
+ return -1;
+}
+
+
+
+
+
+void __publish_%(locstr)s (){
+ Analog_Value_Copy_Located_Var_to_Present_Value();
+ Analog_Input_Copy_Located_Var_to_Present_Value();
+ Analog_Output_Copy_Located_Var_to_Present_Value();
+ Binary_Value_Copy_Located_Var_to_Present_Value();
+ Binary_Input_Copy_Located_Var_to_Present_Value();
+ Binary_Output_Copy_Located_Var_to_Present_Value();
+ Multistate_Value_Copy_Located_Var_to_Present_Value();
+ Multistate_Input_Copy_Located_Var_to_Present_Value();
+ Multistate_Output_Copy_Located_Var_to_Present_Value();
+}
+
+
+
+
+
+void __retrieve_%(locstr)s (){
+ Analog_Value_Copy_Present_Value_to_Located_Var();
+ Analog_Input_Copy_Present_Value_to_Located_Var();
+ Analog_Output_Copy_Present_Value_to_Located_Var();
+ Binary_Value_Copy_Present_Value_to_Located_Var();
+ Binary_Input_Copy_Present_Value_to_Located_Var();
+ Binary_Output_Copy_Present_Value_to_Located_Var();
+ Multistate_Value_Copy_Present_Value_to_Located_Var();
+ Multistate_Input_Copy_Present_Value_to_Located_Var();
+ Multistate_Output_Copy_Present_Value_to_Located_Var();
+}
+
+
+
+
+
+int __cleanup_%(locstr)s (){
+ int index, close;
+ int res = 0;
+
+ /* kill thread and close connections of each modbus server node */
+ close = 0;
+ if (server_node.init_state >= 2) {
+ // thread was launched, so we try to cancel it!
+ close = pthread_cancel(server_node.thread_id);
+ close |= pthread_join (server_node.thread_id, NULL);
+ if (close < 0)
+ fprintf(stderr, "BACnet plugin: Error closing thread for bacnet server %%s\n", server_node.location);
+ }
+ res |= close;
+
+ close = 0;
+ if (server_node.init_state >= 1) {
+ // bacnet server node was created, so we try to close it!
+ // datalink_cleanup is a pointer that will actually call bip_cleanup()
+ // datalink_cleanup pointer is set by the call datalink_set("BIP")
+ datalink_cleanup();
+ }
+ res |= close;
+ server_node.init_state = 0;
+
+ /* bacnet library close */
+ // Nothing to do ???
+
+ return res;
+}
+