bacnet/runtime/server.c
changeset 2020 6dddf3070806
child 2187 c6321473fac1
equal deleted inserted replaced
2019:92f02bb17c7e 2020:6dddf3070806
       
     1 /**************************************************************************
       
     2 *
       
     3 * Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
       
     4 * Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
       
     5 *
       
     6 * Permission is hereby granted, free of charge, to any person obtaining
       
     7 * a copy of this software and associated documentation files (the
       
     8 * "Software"), to deal in the Software without restriction, including
       
     9 * without limitation the rights to use, copy, modify, merge, publish,
       
    10 * distribute, sublicense, and/or sell copies of the Software, and to
       
    11 * permit persons to whom the Software is furnished to do so, subject to
       
    12 * the following conditions:
       
    13 *
       
    14 * The above copyright notice and this permission notice shall be included
       
    15 * in all copies or substantial portions of the Software.
       
    16 *
       
    17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       
    18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       
    19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
       
    20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
       
    21 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
       
    22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       
    23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       
    24 *
       
    25 *********************************************************************/
       
    26 #include <stddef.h>
       
    27 #include <stdint.h>
       
    28 #include <stdio.h>
       
    29 #include <stdlib.h>
       
    30 #include <signal.h>
       
    31 #include <time.h>
       
    32 #include <inttypes.h>  // uint32_t, ..., PRIu32, ...
       
    33 
       
    34 #include "config_bacnet_for_beremiz_%(locstr)s.h"     /* the custom configuration for beremiz pluginh  */
       
    35 #include "server_%(locstr)s.h"
       
    36 #include "address.h"
       
    37 #include "bacdef.h"
       
    38 #include "handlers.h"
       
    39 #include "client.h"
       
    40 #include "dlenv.h"
       
    41 #include "bacdcode.h"
       
    42 #include "npdu.h"
       
    43 #include "apdu.h"
       
    44 #include "iam.h"
       
    45 #include "tsm.h"
       
    46 #include "datalink.h"
       
    47 #include "dcc.h"
       
    48 #include "getevent.h"
       
    49 #include "net.h"
       
    50 #include "txbuf.h"
       
    51 #include "version.h"
       
    52 #include "timesync.h"
       
    53 
       
    54 
       
    55 /* A utility function used by most (all?) implementations of BACnet Objects */
       
    56 /* Adds to Prop_List all entries in Prop_List_XX that are not
       
    57  * PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PROPERTY_LIST
       
    58  * and returns the number of elements that were added
       
    59  */
       
    60 int BACnet_Init_Properties_List(
       
    61           int *Prop_List, 
       
    62     const int *Prop_List_XX) {
       
    63   
       
    64     unsigned int i = 0;
       
    65     unsigned int j = 0;
       
    66     
       
    67     for (j = 0; Prop_List_XX[j] >= 0; j++)
       
    68         // Add any propety, except for the following 4 which should not be included
       
    69         // in the Property_List property array.
       
    70         //  (see ASHRAE 135-2016, for example section 12.4.34)
       
    71         if ((Prop_List_XX[j] != PROP_OBJECT_IDENTIFIER) &&
       
    72             (Prop_List_XX[j] != PROP_OBJECT_NAME)       &&
       
    73             (Prop_List_XX[j] != PROP_OBJECT_TYPE)       &&
       
    74             (Prop_List_XX[j] != PROP_PROPERTY_LIST)) {
       
    75             Prop_List[i] = Prop_List_XX[j];
       
    76             i++;
       
    77         }
       
    78     Prop_List[i] = -1; // marks the end of the list!
       
    79     return i;
       
    80 }
       
    81 
       
    82 
       
    83 
       
    84 
       
    85 
       
    86 
       
    87 int BACnet_encode_character_string(uint8_t *apdu, const char *str) {
       
    88     BACNET_CHARACTER_STRING   char_string;
       
    89     characterstring_init_ansi(&char_string, str);
       
    90     /* FIXME: this might go beyond MAX_APDU length! */
       
    91     return encode_application_character_string(apdu, &char_string);
       
    92 }
       
    93 
       
    94 /* macro that always returns false.
       
    95  * To be used as the 'test_null' parameter to the BACnet_encode_array macro
       
    96  * in situations where we should never encode_null() values.
       
    97  */
       
    98 #define retfalse(x) (false)
       
    99 
       
   100 #define BACnet_encode_array(array, array_len, test_null, encode_function)                  \
       
   101 {                                                                                           \
       
   102     uint8_t *apdu = NULL;                                                                   \
       
   103     apdu = rpdata->application_data;                                                        \
       
   104                                                                                             \
       
   105     switch (rpdata->array_index) {                                                          \
       
   106       case 0: /* Array element zero is the number of elements in the array */               \
       
   107         apdu_len = encode_application_unsigned(&apdu[0], array_len);                        \
       
   108         break;                                                                              \
       
   109       case BACNET_ARRAY_ALL: {                                                              \
       
   110         /* if no index was specified, then try to encode the entire list */                 \
       
   111         unsigned i = 0;                                                                     \
       
   112         apdu_len   = 0;                                                                     \
       
   113         for (i = 0; i < array_len; i++) {                                                   \
       
   114             /* FIXME: this might go beyond MAX_APDU length! */                              \
       
   115             if (!test_null(array[i]))                                                       \
       
   116                   apdu_len += encode_function        (&apdu[apdu_len], array[i]);           \
       
   117             else  apdu_len += encode_application_null(&apdu[apdu_len]);                     \
       
   118             /* return error if it does not fit in the APDU */                               \
       
   119             if (apdu_len >= MAX_APDU) {                                                     \
       
   120                 rpdata->error_class = ERROR_CLASS_SERVICES;                                 \
       
   121                 rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT;                        \
       
   122                 apdu_len = BACNET_STATUS_ERROR;                                             \
       
   123                 break; /* for(;;) */                                                        \
       
   124             }                                                                               \
       
   125         }                                                                                   \
       
   126         break;                                                                              \
       
   127       }                                                                                     \
       
   128       default:                                                                              \
       
   129         if (rpdata->array_index <= array_len) {                                             \
       
   130             if (!test_null(array[rpdata->array_index - 1]))                                 \
       
   131                   apdu_len += encode_function(&apdu[0], array[rpdata->array_index - 1]);    \
       
   132             else  apdu_len += encode_application_null(&apdu[0]);                            \
       
   133         } else {                                                                            \
       
   134             rpdata->error_class = ERROR_CLASS_PROPERTY;                                     \
       
   135             rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;                            \
       
   136             apdu_len = BACNET_STATUS_ERROR;                                                 \
       
   137         }                                                                                   \
       
   138         break;                                                                              \
       
   139     } /* switch() */                                                                        \
       
   140 }
       
   141 
       
   142 
       
   143 /* include the device object */
       
   144 #include "device_%(locstr)s.c"
       
   145 #include "ai_%(locstr)s.c"
       
   146 #include "ao_%(locstr)s.c"
       
   147 #include "av_%(locstr)s.c"
       
   148 #include "bi_%(locstr)s.c"
       
   149 #include "bo_%(locstr)s.c"
       
   150 #include "bv_%(locstr)s.c"
       
   151 #include "msi_%(locstr)s.c"
       
   152 #include "mso_%(locstr)s.c"
       
   153 #include "msv_%(locstr)s.c"
       
   154 
       
   155 
       
   156 /** Buffer used for receiving */
       
   157 static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
       
   158 
       
   159 
       
   160 
       
   161 
       
   162 /*******************************************************/
       
   163 /* BACnet Service Handlers taylored to Beremiz plugin  */ 
       
   164 /*******************************************************/
       
   165 
       
   166 static void BACNET_show_date_time(
       
   167     BACNET_DATE * bdate,
       
   168     BACNET_TIME * btime)
       
   169 {
       
   170     /* show the date received */
       
   171     fprintf(stderr, "%%u", (unsigned) bdate->year);
       
   172     fprintf(stderr, "/%%u", (unsigned) bdate->month);
       
   173     fprintf(stderr, "/%%u", (unsigned) bdate->day);
       
   174     /* show the time received */
       
   175     fprintf(stderr, " %%02u", (unsigned) btime->hour);
       
   176     fprintf(stderr, ":%%02u", (unsigned) btime->min);
       
   177     fprintf(stderr, ":%%02u", (unsigned) btime->sec);
       
   178     fprintf(stderr, ".%%02u", (unsigned) btime->hundredths);
       
   179     fprintf(stderr, "\r\n");
       
   180 }
       
   181 
       
   182 static time_t __timegm(struct tm *new_time) {
       
   183     time_t     sec =  mktime(new_time);  /* assume new_time is in local time */
       
   184     
       
   185     /* sec will be an aproximation of the correct value. 
       
   186      * We must now fix this with the current difference
       
   187      * between UTC and localtime.
       
   188      * 
       
   189      * WARNING: The following algorithm to determine the current
       
   190      *          difference between local time and UTC will not 
       
   191      *          work if each value (lcl and utc) falls on a different
       
   192      *          side of a change to/from DST.
       
   193      *          For example, assume a change to DST is made at 12h00 
       
   194      *          of day X. The following algorithm does not work if:
       
   195      *            - lcl falls before 12h00 of day X
       
   196      *            - utc falls after  12h00 of day X
       
   197      */
       
   198     struct  tm lcl = *localtime(&sec); // lcl will be == new_time
       
   199     struct  tm utc = *gmtime   (&sec);
       
   200     
       
   201     if (lcl.tm_isdst == 1)  utc.tm_isdst = 1;
       
   202     
       
   203     time_t sec_lcl =  mktime(&lcl);
       
   204     time_t sec_utc =  mktime(&utc);
       
   205 
       
   206     /* difference in seconds between localtime and utc */
       
   207     time_t sec_dif = sec_lcl - sec_utc;
       
   208     return sec + sec_dif;
       
   209 }
       
   210 
       
   211 
       
   212 static void BACNET_set_date_time(
       
   213     BACNET_DATE * bdate,
       
   214     BACNET_TIME * btime,
       
   215     int utc /* set to > 0 if date & time in UTC */)
       
   216 {
       
   217     struct tm       brokendown_time;
       
   218     time_t          seconds = 0;
       
   219     struct timespec ts;
       
   220 
       
   221     brokendown_time.tm_sec   = btime->sec;        /* seconds 0..60    */
       
   222     brokendown_time.tm_min   = btime->min;        /* minutes 0..59    */
       
   223     brokendown_time.tm_hour  = btime->hour;       /* hours   0..23    */
       
   224     brokendown_time.tm_mday  = bdate->day;        /* day of the month 1..31 */
       
   225     brokendown_time.tm_mon   = bdate->month-1;    /* month 0..11      */
       
   226     brokendown_time.tm_year  = bdate->year-1900;  /* years since 1900 */
       
   227 //  brokendown_time.tm_wday  = ;                  /* day of the week  */
       
   228 //  brokendown_time.tm_yday  = ;                  /* day in the year  */
       
   229     brokendown_time.tm_isdst = -1;                /* daylight saving time (-1 => unknown) */
       
   230 
       
   231     // Tranform time into format -> 'seconds since epoch'
       
   232     /* WARNING: timegm() is a non standard GNU extension.
       
   233      *          If you do not have it on your build system then consider
       
   234      *          finding the source code for timegm() (it is LGPL) from GNU
       
   235      *          C library and copying it here 
       
   236      *          (e.g. https://code.woboq.org/userspace/glibc/time/timegm.c.html)
       
   237      *          Another alternative is to use the fundion __timegm() above,
       
   238      *          which will mostly work but may have errors when the time being 
       
   239      *          converted is close to the time in the year when changing 
       
   240      *          to/from DST (daylight saving time)
       
   241      */
       
   242     if (utc > 0) seconds = timegm(&brokendown_time);    
       
   243     else         seconds = mktime(&brokendown_time);
       
   244 
       
   245     ts.tv_sec  = seconds;
       
   246     ts.tv_nsec = btime->hundredths*10*1000*1000;
       
   247 
       
   248 //  fprintf(stderr, "clock_settime() s=%%ul,  ns=%%u\n", ts.tv_sec, ts.tv_nsec);
       
   249     clock_settime(CLOCK_REALTIME, &ts); 
       
   250 //  clock_gettime(CLOCK_REALTIME, &ts); 
       
   251 //  fprintf(stderr, "clock_gettime() s=%%ul,  ns=%%u\n", ts.tv_sec, ts.tv_nsec);
       
   252 }
       
   253 
       
   254 
       
   255 
       
   256 void BACnet_handler_timesync(
       
   257     uint8_t * service_request,
       
   258     uint16_t service_len,
       
   259     BACNET_ADDRESS * src)
       
   260 {
       
   261     int len = 0;
       
   262     BACNET_DATE bdate;
       
   263     BACNET_TIME btime;
       
   264 
       
   265     (void) src;
       
   266     (void) service_len;
       
   267     len =
       
   268         timesync_decode_service_request(service_request, service_len, &bdate, &btime);
       
   269     if (len > 0) {
       
   270         fprintf(stderr, "BACnet plugin: Received TimeSyncronization Request -> ");
       
   271         BACNET_show_date_time(&bdate, &btime);
       
   272         /* set the time */
       
   273         BACNET_set_date_time(&bdate, &btime, 0 /* time in local time */);
       
   274     }
       
   275 
       
   276     return;
       
   277 }
       
   278 
       
   279 void BACnet_handler_timesync_utc(
       
   280     uint8_t * service_request,
       
   281     uint16_t service_len,
       
   282     BACNET_ADDRESS * src)
       
   283 {
       
   284     int len = 0;
       
   285     BACNET_DATE bdate;
       
   286     BACNET_TIME btime;
       
   287 
       
   288     (void) src;
       
   289     (void) service_len;
       
   290     len =
       
   291         timesync_decode_service_request(service_request, service_len, &bdate, &btime);
       
   292     if (len > 0) {
       
   293         fprintf(stderr, "BACnet plugin: Received TimeSyncronizationUTC Request -> ");
       
   294         BACNET_show_date_time(&bdate, &btime);
       
   295         /* set the time */
       
   296         BACNET_set_date_time(&bdate, &btime, 1 /* time in UTC */);
       
   297     }
       
   298 
       
   299     return;
       
   300 }
       
   301 
       
   302 
       
   303 
       
   304 /**********************************************/
       
   305 /** Initialize the handlers we will utilize. **/
       
   306 /**********************************************/
       
   307 /*
       
   308  * TLDR: The functions that will create the __Resp.__ messages.
       
   309  * 
       
   310  * The service handlers are the functions that will respond to BACnet requests this device receives.
       
   311  * In essence, the service handlers will create and send the Resp. (Response) messages
       
   312  * of the Req. -> Ind. -> Resp. -> Conf. service sequence defined in OSI
       
   313  * (Request, Indication, Response, Confirmation)
       
   314  * 
       
   315  */
       
   316 static int Init_Service_Handlers(
       
   317     void)
       
   318 {
       
   319     /* set the handler for all the services we don't implement */
       
   320     /* It is required to send the proper reject message... */
       
   321     apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service);
       
   322 
       
   323     /* Set the handlers for any unconfirmed services that we support. */
       
   324     apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,  // DM-DDB-B - Dynamic Device Binding B (Resp.)
       
   325                                    handler_who_is);           //            (see ASHRAE 135-2016, section K5.1 and K5.2)
       
   326 //  apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM,    // DM-DDB-A - Dynamic Device Binding A (Resp.)
       
   327 //                                 handler_i_am_bind);        //            Responding to I_AM requests is for clients (A)!
       
   328 //                                                            //            (see ASHRAE 135-2016, section K5.1 and K5.2)
       
   329     apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, // DM-DOB-B - Dynamic Object Binding B (Resp.)
       
   330                                    handler_who_has);          //            (see ASHRAE 135-2016, section K5.3 and K5.4)
       
   331 //  apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_HAVE,  // DM-DOB-A - Dynamic Object Binding A (Resp.)
       
   332 //                                 handler_i_have);           //            Responding to I_HAVE requests is for clients (A)!
       
   333 //                                                            //            (see ASHRAE 135-2016, section K5.3 and K5.4)
       
   334     apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, // DM-UTC-B -UTCTimeSynchronization-B (Resp.)
       
   335                                  BACnet_handler_timesync_utc);                 //            (see ASHRAE 135-2016, section K5.14)
       
   336     apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION,     // DM-TS-B - TimeSynchronization-B (Resp.)
       
   337                                  BACnet_handler_timesync);                     //            (see ASHRAE 135-2016, section K5.12)
       
   338 
       
   339     /* Set the handlers for any confirmed services that we support. */
       
   340     apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,      // DS-RP-B - Read Property B (Resp.)
       
   341                                  handler_read_property);             //            (see ASHRAE 135-2016, section K1.2)
       
   342 //  apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, // DS-RPM-B -Read Property Multiple-B (Resp.)
       
   343 //                               handler_read_property_multiple);    //            (see ASHRAE 135-2016, section K1.4)
       
   344     apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,     // DS-WP-B - Write Property B (Resp.)
       
   345                                  handler_write_property);            //            (see ASHRAE 135-2016, section K1.8)
       
   346 //  apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE,// DS-WPM-B -Write Property Multiple B (Resp.)
       
   347 //                               handler_write_property_multiple);   //            (see ASHRAE 135-2016, section K1.10)
       
   348 //  apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_RANGE,
       
   349 //                               handler_read_range);
       
   350 //  apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE,
       
   351 //                               handler_reinitialize_device);
       
   352     
       
   353 //  apdu_set_confirmed_handler(SERVICE_CONFIRMED_SUBSCRIBE_COV,
       
   354 //                               handler_cov_subscribe);
       
   355 //  apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION,
       
   356 //                               handler_ucov_notification);
       
   357     /* handle communication so we can shutup when asked */
       
   358     apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, // DM-DCC-B - Device Communication Control B
       
   359                                  handler_device_communication_control);        //            (see ASHRAE 135-2016, section K5.6)
       
   360 //  /* handle the data coming back from private requests */
       
   361 //  apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_PRIVATE_TRANSFER,
       
   362 //                               handler_unconfirmed_private_transfer);
       
   363     
       
   364     // success
       
   365     return 0;
       
   366 }
       
   367 
       
   368 
       
   369 
       
   370 static int Init_Network_Interface(
       
   371     const char *interface,    // for linux:   /dev/eth0, /dev/eth1, /dev/wlan0, ...
       
   372                               // for windows: 192.168.0.1 (IP addr. of interface)
       
   373                               // NULL => use default!
       
   374     const char *port,         // Port the server will listen on. (NULL => use default)
       
   375     const char *apdu_timeout, // (NULL => use default)
       
   376     const char *apdu_retries  // (NULL => use default)
       
   377    )
       
   378 {
       
   379     char datalink_layer[4];
       
   380 
       
   381     strcpy(datalink_layer, "BIP"); // datalink_set() does not accpet const char *, so we copy it...
       
   382 //  BIP_Debug = true;
       
   383 
       
   384     if (port != NULL) {
       
   385         bip_set_port(htons((uint16_t) strtol(port, NULL, 0)));
       
   386     } else {
       
   387             bip_set_port(htons(0xBAC0));
       
   388     }
       
   389     
       
   390     if (apdu_timeout != NULL) {
       
   391         apdu_timeout_set((uint16_t) strtol(apdu_timeout, NULL, 0));
       
   392     }
       
   393 
       
   394     if (apdu_retries != NULL) {
       
   395         apdu_retries_set((uint8_t) strtol(apdu_retries, NULL, 0));
       
   396     }
       
   397 
       
   398     // datalink_init is a pointer that will actually call bip_init()
       
   399     // datalink_init pointer is set by the call datalink_set("BIP")
       
   400     /* NOTE: current implementation of BACnet stack uses the interface
       
   401      *       only to determine the server's local IP address and broacast address.
       
   402      *       The local IP addr is later used to discard (broadcast) messages
       
   403      *       it receives that were sent by itself.
       
   404      *       The broadcast IP addr is used for broadcast messages.
       
   405      *       WARNING: The socket itself is created to listen on all 
       
   406      *       available interfaces (INADDR_ANY), so this setting may induce
       
   407      *       the user in error as we will accept messages arriving on other
       
   408      *       interfaces (if they exist)
       
   409      *       (see bip_init() in ports/linux/bip-init.c)
       
   410      *       (see bip_****() in src/bip.c)
       
   411      */
       
   412     char *tmp = (char *)malloc(strlen(interface) + 1);
       
   413     if (tmp == NULL) return -1;
       
   414     strncpy(tmp, interface, strlen(interface) + 1);
       
   415     if (!datalink_init(tmp)) {
       
   416         return -1;
       
   417     }
       
   418 // #if (MAX_TSM_TRANSACTIONS)
       
   419 //     pEnv = getenv("BACNET_INVOKE_ID");
       
   420 //     if (pEnv) {
       
   421 //         tsm_invokeID_set((uint8_t) strtol(pEnv, NULL, 0));
       
   422 //     }
       
   423 // #endif
       
   424     dlenv_register_as_foreign_device();
       
   425     
       
   426     // success
       
   427     return 0;
       
   428 }
       
   429 
       
   430 
       
   431 
       
   432 /** Main function of server demo. **/
       
   433 int bn_server_run(server_node_t *server_node) {
       
   434     int res = 0;
       
   435     BACNET_ADDRESS src = {0};  /* address where message came from */
       
   436     uint16_t pdu_len = 0;
       
   437     unsigned timeout = 1000;       /* milliseconds */
       
   438     time_t last_seconds = 0;
       
   439     time_t current_seconds = 0;
       
   440     uint32_t elapsed_seconds = 0;
       
   441     uint32_t elapsed_milliseconds = 0;
       
   442     uint32_t address_binding_tmr = 0;
       
   443     uint32_t recipient_scan_tmr = 0;
       
   444 
       
   445     /* allow the device ID to be set */
       
   446     Device_Set_Object_Instance_Number(server_node->device_id);
       
   447     /* load any static address bindings in our device bindings list */
       
   448     address_init();
       
   449     /* load any entries in the BDT table from backup file */
       
   450     bvlc_bdt_restore_local();
       
   451     /* Initiliaze the bacnet server 'device' */    
       
   452     Device_Init(server_node->device_name);
       
   453     /* Set the password (max 31 chars) for Device Communication Control request. */
       
   454     /* Default in the BACnet stack is hardcoded as "filister" */
       
   455     /* (char *) cast is to remove the cast. The function is incorrectly declared/defined in the BACnet stack! */
       
   456     /* BACnet stack needs to change demo/handler/h_dcc.c and include/handlers.h                               */
       
   457     handler_dcc_password_set((char *)server_node->comm_control_passwd);
       
   458     /* Set callbacks and configure network interface */
       
   459     res = Init_Service_Handlers();
       
   460     if (res < 0) exit(1);
       
   461     res = Init_Network_Interface(
       
   462             server_node->network_interface, // interface    (NULL => use default (eth0))
       
   463             server_node->port_number,       // Port number  (NULL => use default (0xBAC0))
       
   464             NULL,              // apdu_timeout (NULL => use default)
       
   465             NULL               // apdu_retries (NULL => use default)
       
   466            );
       
   467     if (res < 0) {
       
   468         fprintf(stderr, "BACnet plugin: error initializing bacnet server node %%s!\n", server_node->location);
       
   469         exit(1); // kill the server thread!
       
   470     }
       
   471     /* BACnet stack correcly configured. Give user some feedback! */
       
   472     struct in_addr my_addr, broadcast_addr;
       
   473     my_addr.       s_addr = bip_get_addr();
       
   474     broadcast_addr.s_addr = bip_get_broadcast_addr();
       
   475     printf("BACnet plugin:"
       
   476                          " Local IP addr: %%s" 
       
   477                         ", Broadcast IP addr: %%s" 
       
   478                         ", Port number: 0x%%04X [%%hu]" 
       
   479                         ", BACnet Device ID: %%d"
       
   480                         ", Max APDU: %%d"
       
   481                         ", BACnet Stack Version %%s\n",
       
   482                         inet_ntoa(my_addr),
       
   483                         inet_ntoa(broadcast_addr),
       
   484                         ntohs(bip_get_port()), ntohs(bip_get_port()),
       
   485                         Device_Object_Instance_Number(), 
       
   486                         MAX_APDU,
       
   487                         Device_Firmware_Revision()
       
   488                         );
       
   489     /* configure the timeout values */
       
   490     last_seconds = time(NULL);
       
   491     /* broadcast an I-Am on startup */
       
   492     Send_I_Am(&Handler_Transmit_Buffer[0]);
       
   493     /* loop forever */
       
   494     for (;;) {
       
   495         /* input */
       
   496         current_seconds = time(NULL);
       
   497 
       
   498         /* returns 0 bytes on timeout */
       
   499         pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
       
   500 
       
   501         /* process */
       
   502         if (pdu_len) {
       
   503             npdu_handler(&src, &Rx_Buf[0], pdu_len);
       
   504         }
       
   505         /* at least one second has passed */
       
   506         elapsed_seconds = (uint32_t) (current_seconds - last_seconds);
       
   507         if (elapsed_seconds) {
       
   508             last_seconds = current_seconds;
       
   509             dcc_timer_seconds(elapsed_seconds);
       
   510             bvlc_maintenance_timer(elapsed_seconds);
       
   511             dlenv_maintenance_timer(elapsed_seconds);
       
   512             elapsed_milliseconds = elapsed_seconds * 1000;
       
   513             tsm_timer_milliseconds(elapsed_milliseconds);
       
   514         }
       
   515         handler_cov_task();
       
   516         /* scan cache address */
       
   517         address_binding_tmr += elapsed_seconds;
       
   518         if (address_binding_tmr >= 60) {
       
   519             address_cache_timer(address_binding_tmr);
       
   520             address_binding_tmr = 0;
       
   521         }
       
   522     }
       
   523 
       
   524     /* should never occur!! */
       
   525     return 0;
       
   526 }
       
   527 
       
   528 
       
   529 
       
   530 
       
   531 
       
   532 #include <pthread.h>
       
   533 
       
   534 static void *__bn_server_thread(void *_server_node)  {
       
   535 	server_node_t *server_node = _server_node;
       
   536 
       
   537 	// Enable thread cancelation. Enabled is default, but set it anyway to be safe.
       
   538 	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
       
   539 
       
   540 	// bn_server_run() should never return!
       
   541 	bn_server_run(server_node);
       
   542 	fprintf(stderr, "BACnet plugin: bacnet server for node %%s died unexpectedly!\n", server_node->location); /* should never occur */
       
   543 	return NULL;
       
   544 }
       
   545 
       
   546 
       
   547 int __cleanup_%(locstr)s ();
       
   548 
       
   549 int __init_%(locstr)s (int argc, char **argv){
       
   550 	int index;
       
   551 
       
   552 	/* init each local server */
       
   553 	/* NOTE: All server_nodes[].init_state are initialised to 0 in the code 
       
   554 	 *       generated by the BACnet plugin 
       
   555 	 */
       
   556 	/* create the BACnet server */
       
   557 	server_node.init_state = 1; // we have created the node
       
   558 	
       
   559 	/* launch a thread to handle this server node */
       
   560 	{
       
   561 		int res = 0;
       
   562 		pthread_attr_t attr;
       
   563 		res |= pthread_attr_init(&attr);
       
   564 		res |= pthread_create(&(server_node.thread_id), &attr, &__bn_server_thread, (void *)&(server_node));
       
   565 		if (res !=  0) {
       
   566 			fprintf(stderr, "BACnet plugin: Error starting bacnet server thread for node %%s\n", server_node.location);
       
   567 			goto error_exit;
       
   568 		}
       
   569 	}
       
   570 	server_node.init_state = 2; // we have created the node and thread
       
   571 
       
   572 	return 0;
       
   573 	
       
   574 error_exit:
       
   575 	__cleanup_%(locstr)s ();
       
   576 	return -1;
       
   577 }
       
   578 
       
   579 
       
   580 
       
   581 
       
   582 
       
   583 void __publish_%(locstr)s (){
       
   584        Analog_Value_Copy_Located_Var_to_Present_Value();
       
   585        Analog_Input_Copy_Located_Var_to_Present_Value();
       
   586       Analog_Output_Copy_Located_Var_to_Present_Value();
       
   587        Binary_Value_Copy_Located_Var_to_Present_Value();
       
   588        Binary_Input_Copy_Located_Var_to_Present_Value();
       
   589       Binary_Output_Copy_Located_Var_to_Present_Value();
       
   590    Multistate_Value_Copy_Located_Var_to_Present_Value();
       
   591    Multistate_Input_Copy_Located_Var_to_Present_Value();
       
   592   Multistate_Output_Copy_Located_Var_to_Present_Value();
       
   593 }
       
   594 
       
   595 
       
   596 
       
   597 
       
   598 
       
   599 void __retrieve_%(locstr)s (){
       
   600        Analog_Value_Copy_Present_Value_to_Located_Var();
       
   601        Analog_Input_Copy_Present_Value_to_Located_Var();
       
   602       Analog_Output_Copy_Present_Value_to_Located_Var();
       
   603        Binary_Value_Copy_Present_Value_to_Located_Var();
       
   604        Binary_Input_Copy_Present_Value_to_Located_Var();
       
   605       Binary_Output_Copy_Present_Value_to_Located_Var();
       
   606    Multistate_Value_Copy_Present_Value_to_Located_Var();
       
   607    Multistate_Input_Copy_Present_Value_to_Located_Var();
       
   608   Multistate_Output_Copy_Present_Value_to_Located_Var();
       
   609 }
       
   610 
       
   611 
       
   612 
       
   613 
       
   614 
       
   615 int __cleanup_%(locstr)s (){
       
   616 	int index, close;
       
   617 	int res = 0;
       
   618 
       
   619 	/* kill thread and close connections of each modbus server node */
       
   620 	close = 0;
       
   621 	if (server_node.init_state >= 2) {
       
   622 		// thread was launched, so we try to cancel it!
       
   623 		close  = pthread_cancel(server_node.thread_id);
       
   624 		close |= pthread_join  (server_node.thread_id, NULL);
       
   625 		if (close < 0)
       
   626 			fprintf(stderr, "BACnet plugin: Error closing thread for bacnet server %%s\n", server_node.location);
       
   627 	}
       
   628 	res |= close;
       
   629 
       
   630 	close = 0;
       
   631 	if (server_node.init_state >= 1) {
       
   632 		// bacnet server node was created, so we try to close it!
       
   633 		// datalink_cleanup is a pointer that will actually call bip_cleanup()
       
   634 		// datalink_cleanup pointer is set by the call datalink_set("BIP")
       
   635 		datalink_cleanup();
       
   636 	}
       
   637 	res |= close;
       
   638 	server_node.init_state = 0;
       
   639 
       
   640 	/* bacnet library close */
       
   641 	// Nothing to do ???
       
   642 
       
   643 	return res;
       
   644 }
       
   645