|
1 /************************************************************************** |
|
2 * |
|
3 * Copyright (C) 2005,2006,2009 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 |
|
27 /** Base "class" for handling all BACnet objects belonging |
|
28 * to a BACnet device, as well as Device-specific properties. */ |
|
29 |
|
30 #include <stdbool.h> |
|
31 #include <stdint.h> |
|
32 #include <string.h> /* for memmove */ |
|
33 #include <time.h> /* for timezone, localtime */ |
|
34 |
|
35 #include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz plugin */ |
|
36 #include "bacdef.h" |
|
37 #include "bacdcode.h" |
|
38 #include "bacenum.h" |
|
39 #include "bacapp.h" |
|
40 #include "apdu.h" |
|
41 #include "wp.h" /* WriteProperty handling */ |
|
42 #include "rp.h" /* ReadProperty handling */ |
|
43 #include "dcc.h" /* DeviceCommunicationControl handling */ |
|
44 #include "version.h" |
|
45 #include "device_%(locstr)s.h" /* me */ |
|
46 #include "handlers.h" |
|
47 #include "datalink.h" |
|
48 #include "address.h" |
|
49 /* os specfic includes */ |
|
50 #include "timer.h" |
|
51 /* include the device object */ |
|
52 #include "device_%(locstr)s.h" |
|
53 #include "ai_%(locstr)s.h" |
|
54 #include "ao_%(locstr)s.h" |
|
55 #include "av_%(locstr)s.h" |
|
56 #include "bi_%(locstr)s.h" |
|
57 #include "bo_%(locstr)s.h" |
|
58 #include "bv_%(locstr)s.h" |
|
59 #include "msi_%(locstr)s.h" |
|
60 #include "mso_%(locstr)s.h" |
|
61 #include "msv_%(locstr)s.h" |
|
62 |
|
63 |
|
64 #if defined(__BORLANDC__) || defined(_WIN32) |
|
65 /* Not included in time.h as specified by The Open Group */ |
|
66 /* Difference from UTC and local standard time */ |
|
67 long int timezone; |
|
68 #endif |
|
69 |
|
70 /* local forward (semi-private) and external prototypes */ |
|
71 int Device_Read_Property_Local( |
|
72 BACNET_READ_PROPERTY_DATA * rpdata); |
|
73 bool Device_Write_Property_Local( |
|
74 BACNET_WRITE_PROPERTY_DATA * wp_data); |
|
75 extern int Routed_Device_Read_Property_Local( |
|
76 BACNET_READ_PROPERTY_DATA * rpdata); |
|
77 extern bool Routed_Device_Write_Property_Local( |
|
78 BACNET_WRITE_PROPERTY_DATA * wp_data); |
|
79 |
|
80 /* may be overridden by outside table */ |
|
81 static object_functions_t *Object_Table; |
|
82 |
|
83 static object_functions_t My_Object_Table[] = { |
|
84 {OBJECT_DEVICE, |
|
85 NULL /* Init - don't init Device or it will recurse! */ , |
|
86 Device_Count, |
|
87 Device_Index_To_Instance, |
|
88 Device_Valid_Object_Instance_Number, |
|
89 Device_Object_Name, |
|
90 Device_Read_Property_Local, |
|
91 Device_Write_Property_Local, |
|
92 Device_Property_Lists, |
|
93 DeviceGetRRInfo, |
|
94 NULL /* Iterator */ , |
|
95 NULL /* Value_Lists */ , |
|
96 NULL /* COV */ , |
|
97 NULL /* COV Clear */ , |
|
98 NULL /* Intrinsic Reporting */ }, |
|
99 {OBJECT_ANALOG_INPUT, |
|
100 Analog_Input_Init, |
|
101 Analog_Input_Count, |
|
102 Analog_Input_Index_To_Instance, |
|
103 Analog_Input_Valid_Instance, |
|
104 Analog_Input_Object_Name, |
|
105 Analog_Input_Read_Property, |
|
106 Analog_Input_Write_Property, |
|
107 Analog_Input_Property_Lists, |
|
108 NULL /* ReadRangeInfo */ , |
|
109 NULL /* Iterator */ , |
|
110 NULL /* Value_Lists */ , |
|
111 NULL /* COV */ , |
|
112 NULL /* COV Clear */ , |
|
113 NULL /* Intrinsic Reporting */ }, |
|
114 {OBJECT_ANALOG_OUTPUT, |
|
115 Analog_Output_Init, |
|
116 Analog_Output_Count, |
|
117 Analog_Output_Index_To_Instance, |
|
118 Analog_Output_Valid_Instance, |
|
119 Analog_Output_Object_Name, |
|
120 Analog_Output_Read_Property, |
|
121 Analog_Output_Write_Property, |
|
122 Analog_Output_Property_Lists, |
|
123 NULL /* ReadRangeInfo */ , |
|
124 NULL /* Iterator */ , |
|
125 NULL /* Value_Lists */ , |
|
126 NULL /* COV */ , |
|
127 NULL /* COV Clear */ , |
|
128 NULL /* Intrinsic Reporting */ }, |
|
129 {OBJECT_ANALOG_VALUE, |
|
130 Analog_Value_Init, |
|
131 Analog_Value_Count, |
|
132 Analog_Value_Index_To_Instance, |
|
133 Analog_Value_Valid_Instance, |
|
134 Analog_Value_Object_Name, |
|
135 Analog_Value_Read_Property, |
|
136 Analog_Value_Write_Property, |
|
137 Analog_Value_Property_Lists, |
|
138 NULL /* ReadRangeInfo */ , |
|
139 NULL /* Iterator */ , |
|
140 NULL /* Value_Lists */ , |
|
141 NULL /* COV */ , |
|
142 NULL /* COV Clear */ , |
|
143 NULL /* Intrinsic Reporting */ }, |
|
144 {OBJECT_BINARY_INPUT, |
|
145 Binary_Input_Init, |
|
146 Binary_Input_Count, |
|
147 Binary_Input_Index_To_Instance, |
|
148 Binary_Input_Valid_Instance, |
|
149 Binary_Input_Object_Name, |
|
150 Binary_Input_Read_Property, |
|
151 Binary_Input_Write_Property, |
|
152 Binary_Input_Property_Lists, |
|
153 NULL /* ReadRangeInfo */ , |
|
154 NULL /* Iterator */ , |
|
155 NULL /* Value_Lists */ , |
|
156 NULL /* COV */ , |
|
157 NULL /* COV Clear */ , |
|
158 NULL /* Intrinsic Reporting */ }, |
|
159 {OBJECT_BINARY_OUTPUT, |
|
160 Binary_Output_Init, |
|
161 Binary_Output_Count, |
|
162 Binary_Output_Index_To_Instance, |
|
163 Binary_Output_Valid_Instance, |
|
164 Binary_Output_Object_Name, |
|
165 Binary_Output_Read_Property, |
|
166 Binary_Output_Write_Property, |
|
167 Binary_Output_Property_Lists, |
|
168 NULL /* ReadRangeInfo */ , |
|
169 NULL /* Iterator */ , |
|
170 NULL /* Value_Lists */ , |
|
171 NULL /* COV */ , |
|
172 NULL /* COV Clear */ , |
|
173 NULL /* Intrinsic Reporting */ }, |
|
174 {OBJECT_BINARY_VALUE, |
|
175 Binary_Value_Init, |
|
176 Binary_Value_Count, |
|
177 Binary_Value_Index_To_Instance, |
|
178 Binary_Value_Valid_Instance, |
|
179 Binary_Value_Object_Name, |
|
180 Binary_Value_Read_Property, |
|
181 Binary_Value_Write_Property, |
|
182 Binary_Value_Property_Lists, |
|
183 NULL /* ReadRangeInfo */ , |
|
184 NULL /* Iterator */ , |
|
185 NULL /* Value_Lists */ , |
|
186 NULL /* COV */ , |
|
187 NULL /* COV Clear */ , |
|
188 NULL /* Intrinsic Reporting */ }, |
|
189 {OBJECT_MULTI_STATE_INPUT, |
|
190 Multistate_Input_Init, |
|
191 Multistate_Input_Count, |
|
192 Multistate_Input_Index_To_Instance, |
|
193 Multistate_Input_Valid_Instance, |
|
194 Multistate_Input_Object_Name, |
|
195 Multistate_Input_Read_Property, |
|
196 Multistate_Input_Write_Property, |
|
197 Multistate_Input_Property_Lists, |
|
198 NULL /* ReadRangeInfo */ , |
|
199 NULL /* Iterator */ , |
|
200 NULL /* Value_Lists */ , |
|
201 NULL /* COV */ , |
|
202 NULL /* COV Clear */ , |
|
203 NULL /* Intrinsic Reporting */ }, |
|
204 {OBJECT_MULTI_STATE_OUTPUT, |
|
205 Multistate_Output_Init, |
|
206 Multistate_Output_Count, |
|
207 Multistate_Output_Index_To_Instance, |
|
208 Multistate_Output_Valid_Instance, |
|
209 Multistate_Output_Object_Name, |
|
210 Multistate_Output_Read_Property, |
|
211 Multistate_Output_Write_Property, |
|
212 Multistate_Output_Property_Lists, |
|
213 NULL /* ReadRangeInfo */ , |
|
214 NULL /* Iterator */ , |
|
215 NULL /* Value_Lists */ , |
|
216 NULL /* COV */ , |
|
217 NULL /* COV Clear */ , |
|
218 NULL /* Intrinsic Reporting */ }, |
|
219 {OBJECT_MULTI_STATE_VALUE, |
|
220 Multistate_Value_Init, |
|
221 Multistate_Value_Count, |
|
222 Multistate_Value_Index_To_Instance, |
|
223 Multistate_Value_Valid_Instance, |
|
224 Multistate_Value_Object_Name, |
|
225 Multistate_Value_Read_Property, |
|
226 Multistate_Value_Write_Property, |
|
227 Multistate_Value_Property_Lists, |
|
228 NULL /* ReadRangeInfo */ , |
|
229 NULL /* Iterator */ , |
|
230 NULL /* Value_Lists */ , |
|
231 NULL /* COV */ , |
|
232 NULL /* COV Clear */ , |
|
233 NULL /* Intrinsic Reporting */ }, |
|
234 {MAX_BACNET_OBJECT_TYPE, |
|
235 NULL /* Init */ , |
|
236 NULL /* Count */ , |
|
237 NULL /* Index_To_Instance */ , |
|
238 NULL /* Valid_Instance */ , |
|
239 NULL /* Object_Name */ , |
|
240 NULL /* Read_Property */ , |
|
241 NULL /* Write_Property */ , |
|
242 NULL /* Property_Lists */ , |
|
243 NULL /* ReadRangeInfo */ , |
|
244 NULL /* Iterator */ , |
|
245 NULL /* Value_Lists */ , |
|
246 NULL /* COV */ , |
|
247 NULL /* COV Clear */ , |
|
248 NULL /* Intrinsic Reporting */ } |
|
249 }; |
|
250 |
|
251 /** Glue function to let the Device object, when called by a handler, |
|
252 * lookup which Object type needs to be invoked. |
|
253 * param: Object_Type [in] The type of BACnet Object the handler wants to access. |
|
254 * return: Pointer to the group of object helper functions that implement this |
|
255 * type of Object. |
|
256 */ |
|
257 static struct object_functions *Device_Objects_Find_Functions( |
|
258 BACNET_OBJECT_TYPE Object_Type) |
|
259 { |
|
260 struct object_functions *pObject = NULL; |
|
261 |
|
262 pObject = Object_Table; |
|
263 while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { |
|
264 /* handle each object type */ |
|
265 if (pObject->Object_Type == Object_Type) { |
|
266 return (pObject); |
|
267 } |
|
268 pObject++; |
|
269 } |
|
270 |
|
271 return (NULL); |
|
272 } |
|
273 |
|
274 /** Try to find a rr_info_function helper function for the requested object type. |
|
275 * |
|
276 * param: object_type [in] The type of BACnet Object the handler wants to access. |
|
277 * return: Pointer to the object helper function that implements the |
|
278 * ReadRangeInfo function, Object_RR_Info, for this type of Object on |
|
279 * success, else a NULL pointer if the type of Object isn't supported |
|
280 * or doesn't have a ReadRangeInfo function. |
|
281 */ |
|
282 rr_info_function Device_Objects_RR_Info( |
|
283 BACNET_OBJECT_TYPE object_type) |
|
284 { |
|
285 struct object_functions *pObject = NULL; |
|
286 |
|
287 pObject = Device_Objects_Find_Functions(object_type); |
|
288 return (pObject != NULL ? pObject->Object_RR_Info : NULL); |
|
289 } |
|
290 |
|
291 /** For a given object type, returns the special property list. |
|
292 * This function is used for ReadPropertyMultiple calls which want |
|
293 * just Required, just Optional, or All properties. |
|
294 * |
|
295 * param: object_type [in] The desired BACNET_OBJECT_TYPE whose properties |
|
296 * are to be listed. |
|
297 * param: pPropertyList [out] Reference to the structure which will, on return, |
|
298 * list, separately, the Required, Optional, and Proprietary object |
|
299 * properties with their counts. |
|
300 */ |
|
301 void Device_Objects_Property_List( |
|
302 BACNET_OBJECT_TYPE object_type, |
|
303 struct special_property_list_t *pPropertyList) |
|
304 { |
|
305 struct object_functions *pObject = NULL; |
|
306 |
|
307 pPropertyList->Required.pList = NULL; |
|
308 pPropertyList->Optional.pList = NULL; |
|
309 pPropertyList->Proprietary.pList = NULL; |
|
310 |
|
311 /* If we can find an entry for the required object type |
|
312 * and there is an Object_List_RPM fn ptr then call it |
|
313 * to populate the pointers to the individual list counters. |
|
314 */ |
|
315 |
|
316 pObject = Device_Objects_Find_Functions(object_type); |
|
317 if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) { |
|
318 pObject->Object_RPM_List(&pPropertyList->Required.pList, |
|
319 &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList); |
|
320 } |
|
321 |
|
322 /* Fetch the counts if available otherwise zero them */ |
|
323 pPropertyList->Required.count = |
|
324 pPropertyList->Required.pList == |
|
325 NULL ? 0 : property_list_count(pPropertyList->Required.pList); |
|
326 |
|
327 pPropertyList->Optional.count = |
|
328 pPropertyList->Optional.pList == |
|
329 NULL ? 0 : property_list_count(pPropertyList->Optional.pList); |
|
330 |
|
331 pPropertyList->Proprietary.count = |
|
332 pPropertyList->Proprietary.pList == |
|
333 NULL ? 0 : property_list_count(pPropertyList->Proprietary.pList); |
|
334 |
|
335 return; |
|
336 } |
|
337 |
|
338 /** Commands a Device re-initialization, to a given state. |
|
339 * The request's password must match for the operation to succeed. |
|
340 * This implementation provides a framework, but doesn't |
|
341 * actually *DO* anything. |
|
342 * @note You could use a mix of states and passwords to multiple outcomes. |
|
343 * @note You probably want to restart *after* the simple ack has been sent |
|
344 * from the return handler, so just set a local flag here. |
|
345 * |
|
346 * param: rd_data [in,out] The information from the RD request. |
|
347 * On failure, the error class and code will be set. |
|
348 * return: True if succeeds (password is correct), else False. |
|
349 */ |
|
350 bool Device_Reinitialize( |
|
351 BACNET_REINITIALIZE_DEVICE_DATA * rd_data) |
|
352 { |
|
353 bool status = false; |
|
354 |
|
355 if (characterstring_ansi_same(&rd_data->password, "Jesus")) { |
|
356 switch (rd_data->state) { |
|
357 case BACNET_REINIT_COLDSTART: |
|
358 case BACNET_REINIT_WARMSTART: |
|
359 dcc_set_status_duration(COMMUNICATION_ENABLE, 0); |
|
360 break; |
|
361 case BACNET_REINIT_STARTBACKUP: |
|
362 break; |
|
363 case BACNET_REINIT_ENDBACKUP: |
|
364 break; |
|
365 case BACNET_REINIT_STARTRESTORE: |
|
366 break; |
|
367 case BACNET_REINIT_ENDRESTORE: |
|
368 break; |
|
369 case BACNET_REINIT_ABORTRESTORE: |
|
370 break; |
|
371 default: |
|
372 break; |
|
373 } |
|
374 /* Note: you could use a mix of state |
|
375 and password to multiple things */ |
|
376 /* note: you probably want to restart *after* the |
|
377 simple ack has been sent from the return handler |
|
378 so just set a flag from here */ |
|
379 status = true; |
|
380 } else { |
|
381 rd_data->error_class = ERROR_CLASS_SECURITY; |
|
382 rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE; |
|
383 } |
|
384 |
|
385 return status; |
|
386 } |
|
387 |
|
388 /* These three arrays are used by the ReadPropertyMultiple handler, |
|
389 * as well as to initialize the XXX_Property_List used by the |
|
390 * Property List (PROP_PROPERTY_LIST) property. |
|
391 */ |
|
392 static const int Device_Properties_Required[] = { |
|
393 /* (1) Currently Supported */ |
|
394 /* (2) Required by standard ASHRAE 135-2016 */ |
|
395 /*(1)(2) */ |
|
396 PROP_OBJECT_IDENTIFIER, /* W R ( 75) */ |
|
397 PROP_OBJECT_NAME, /* W R ( 77) */ |
|
398 PROP_OBJECT_TYPE, /* R R ( 79) */ |
|
399 PROP_SYSTEM_STATUS, /* R R (112) */ |
|
400 PROP_VENDOR_NAME, /* R R (121) */ |
|
401 PROP_VENDOR_IDENTIFIER, /* W R (120) */ |
|
402 PROP_MODEL_NAME, /* W R ( 70) */ |
|
403 PROP_FIRMWARE_REVISION, /* R R ( 44) */ |
|
404 PROP_APPLICATION_SOFTWARE_VERSION, /* R R ( 12) */ |
|
405 PROP_PROTOCOL_VERSION, /* R R ( 98) */ |
|
406 PROP_PROTOCOL_REVISION, /* R R (139) */ |
|
407 PROP_PROTOCOL_SERVICES_SUPPORTED, /* R R ( 97) */ |
|
408 PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, /* R R ( 96) */ |
|
409 PROP_OBJECT_LIST, /* R R ( 76) */ |
|
410 PROP_MAX_APDU_LENGTH_ACCEPTED, /* R R ( 62) */ |
|
411 PROP_SEGMENTATION_SUPPORTED, /* R R (107) */ |
|
412 PROP_APDU_TIMEOUT, /* W R ( 11) */ |
|
413 PROP_NUMBER_OF_APDU_RETRIES, /* W R ( 73) */ |
|
414 PROP_DEVICE_ADDRESS_BINDING, /* R R ( 30) */ |
|
415 PROP_DATABASE_REVISION, /* R R (155) */ |
|
416 // PROP_PROPERTY_LIST, /* R R (371) */ |
|
417 -1 |
|
418 }; |
|
419 |
|
420 static const int Device_Properties_Optional[] = { |
|
421 PROP_DESCRIPTION, /* W O ( 28) */ |
|
422 PROP_LOCAL_TIME, /* R O ( 57) */ |
|
423 PROP_UTC_OFFSET, /* R O (119) */ |
|
424 PROP_LOCAL_DATE, /* R O ( 56) */ |
|
425 PROP_DAYLIGHT_SAVINGS_STATUS, /* R O ( 24) */ |
|
426 PROP_LOCATION, /* W O ( 58) */ |
|
427 -1 |
|
428 }; |
|
429 |
|
430 static const int Device_Properties_Proprietary[] = { |
|
431 -1 |
|
432 }; |
|
433 |
|
434 /* This array stores the PROPERTY_LIST which may be read by clients. |
|
435 * End of list is marked by following the last element with the value '-1' |
|
436 * |
|
437 * It is initialized by Binary_Value_Init() based off the values |
|
438 * stored in Binary_Value_Properties_Required |
|
439 * Binary_Value_Properties_Optional |
|
440 * Binary_Value_Properties_Proprietary |
|
441 */ |
|
442 /* TODO: Allocate memory for this array with malloc() at startup */ |
|
443 static int Device_Properties_List[64]; |
|
444 |
|
445 |
|
446 void Device_Property_Lists( |
|
447 const int **pRequired, |
|
448 const int **pOptional, |
|
449 const int **pProprietary) |
|
450 { |
|
451 if (pRequired) |
|
452 *pRequired = Device_Properties_Required; |
|
453 if (pOptional) |
|
454 *pOptional = Device_Properties_Optional; |
|
455 if (pProprietary) |
|
456 *pProprietary = Device_Properties_Proprietary; |
|
457 |
|
458 return; |
|
459 } |
|
460 |
|
461 |
|
462 static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; |
|
463 |
|
464 static BACNET_CHARACTER_STRING My_Object_Name; |
|
465 static uint32_t Object_Instance_Number = 260001; |
|
466 static uint16_t Vendor_Identifier = BACNET_VENDOR_ID; |
|
467 static char *Vendor_Name = BACNET_VENDOR_NAME; |
|
468 static char *Firmware_Revision = BACNET_FIRMWARE_REVISION; |
|
469 static char Model_Name [MAX_DEV_MOD_LEN + 1] = BACNET_DEVICE_MODEL_NAME; |
|
470 static char Application_Software_Version[MAX_DEV_VER_LEN + 1] = BACNET_DEVICE_APPSOFT_VER; |
|
471 static char Location [MAX_DEV_LOC_LEN + 1] = BACNET_DEVICE_LOCATION; |
|
472 static char Description [MAX_DEV_DESC_LEN + 1] = BACNET_DEVICE_DESCRIPTION; |
|
473 /* static uint8_t Protocol_Version = 1; - constant, not settable */ |
|
474 /* static uint8_t Protocol_Revision = 4; - constant, not settable */ |
|
475 /* Protocol_Services_Supported - dynamically generated */ |
|
476 /* Protocol_Object_Types_Supported - in RP encoding */ |
|
477 /* Object_List - dynamically generated */ |
|
478 /* static BACNET_SEGMENTATION Segmentation_Supported = SEGMENTATION_NONE; */ |
|
479 /* static uint8_t Max_Segments_Accepted = 0; */ |
|
480 /* VT_Classes_Supported */ |
|
481 /* Active_VT_Sessions */ |
|
482 static BACNET_TIME Local_Time; /* rely on OS, if there is one */ |
|
483 static BACNET_DATE Local_Date; /* rely on OS, if there is one */ |
|
484 /* NOTE: BACnet UTC Offset is inverse of common practice. |
|
485 If your UTC offset is -5hours of GMT, |
|
486 then BACnet UTC offset is +5hours. |
|
487 BACnet UTC offset is expressed in minutes. */ |
|
488 static int32_t UTC_Offset = 5 * 60; |
|
489 static bool Daylight_Savings_Status = false; /* rely on OS */ |
|
490 /* List_Of_Session_Keys */ |
|
491 /* Time_Synchronization_Recipients */ |
|
492 /* Max_Master - rely on MS/TP subsystem, if there is one */ |
|
493 /* Max_Info_Frames - rely on MS/TP subsystem, if there is one */ |
|
494 /* Device_Address_Binding - required, but relies on binding cache */ |
|
495 static uint32_t Database_Revision = 0; |
|
496 /* Configuration_Files */ |
|
497 /* Last_Restore_Time */ |
|
498 /* Backup_Failure_Timeout */ |
|
499 /* Active_COV_Subscriptions */ |
|
500 /* Slave_Proxy_Enable */ |
|
501 /* Manual_Slave_Address_Binding */ |
|
502 /* Auto_Slave_Discovery */ |
|
503 /* Slave_Address_Binding */ |
|
504 /* Profile_Name */ |
|
505 |
|
506 unsigned Device_Count( |
|
507 void) |
|
508 { |
|
509 return 1; |
|
510 } |
|
511 |
|
512 uint32_t Device_Index_To_Instance( |
|
513 unsigned index) |
|
514 { |
|
515 index = index; |
|
516 return Object_Instance_Number; |
|
517 } |
|
518 |
|
519 /* methods to manipulate the data */ |
|
520 |
|
521 /** Return the Object Instance number for our (single) Device Object. |
|
522 * This is a key function, widely invoked by the handler code, since |
|
523 * it provides "our" (ie, local) address. |
|
524 * return: The Instance number used in the BACNET_OBJECT_ID for the Device. |
|
525 */ |
|
526 uint32_t Device_Object_Instance_Number( |
|
527 void) |
|
528 { |
|
529 return Object_Instance_Number; |
|
530 } |
|
531 |
|
532 bool Device_Set_Object_Instance_Number( |
|
533 uint32_t object_id) |
|
534 { |
|
535 bool status = true; /* return value */ |
|
536 |
|
537 if (object_id <= BACNET_MAX_INSTANCE) { |
|
538 /* Make the change and update the database revision */ |
|
539 Object_Instance_Number = object_id; |
|
540 Device_Inc_Database_Revision(); |
|
541 } else |
|
542 status = false; |
|
543 |
|
544 return status; |
|
545 } |
|
546 |
|
547 bool Device_Valid_Object_Instance_Number( |
|
548 uint32_t object_id) |
|
549 { |
|
550 return (Object_Instance_Number == object_id); |
|
551 } |
|
552 |
|
553 bool Device_Object_Name( |
|
554 uint32_t object_instance, |
|
555 BACNET_CHARACTER_STRING * object_name) |
|
556 { |
|
557 bool status = false; |
|
558 |
|
559 if (object_instance == Object_Instance_Number) { |
|
560 status = characterstring_copy(object_name, &My_Object_Name); |
|
561 } |
|
562 |
|
563 return status; |
|
564 } |
|
565 |
|
566 bool Device_Set_Object_Name( |
|
567 BACNET_CHARACTER_STRING * object_name) |
|
568 { |
|
569 bool status = false; /*return value */ |
|
570 |
|
571 if (!characterstring_same(&My_Object_Name, object_name)) { |
|
572 /* Make the change and update the database revision */ |
|
573 status = characterstring_copy(&My_Object_Name, object_name); |
|
574 Device_Inc_Database_Revision(); |
|
575 } |
|
576 |
|
577 return status; |
|
578 } |
|
579 |
|
580 BACNET_DEVICE_STATUS Device_System_Status( |
|
581 void) |
|
582 { |
|
583 return System_Status; |
|
584 } |
|
585 |
|
586 int Device_Set_System_Status( |
|
587 BACNET_DEVICE_STATUS status, |
|
588 bool local) |
|
589 { |
|
590 int result = 0; /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ |
|
591 |
|
592 /* We limit the options available depending on whether the source is |
|
593 * internal or external. */ |
|
594 if (local) { |
|
595 switch (status) { |
|
596 case STATUS_OPERATIONAL: |
|
597 case STATUS_OPERATIONAL_READ_ONLY: |
|
598 case STATUS_DOWNLOAD_REQUIRED: |
|
599 case STATUS_DOWNLOAD_IN_PROGRESS: |
|
600 case STATUS_NON_OPERATIONAL: |
|
601 System_Status = status; |
|
602 break; |
|
603 |
|
604 /* Don't support backup at present so don't allow setting */ |
|
605 case STATUS_BACKUP_IN_PROGRESS: |
|
606 result = -2; |
|
607 break; |
|
608 |
|
609 default: |
|
610 result = -1; |
|
611 break; |
|
612 } |
|
613 } else { |
|
614 switch (status) { |
|
615 /* Allow these for the moment as a way to easily alter |
|
616 * overall device operation. The lack of password protection |
|
617 * or other authentication makes allowing writes to this |
|
618 * property a risky facility to provide. |
|
619 */ |
|
620 case STATUS_OPERATIONAL: |
|
621 case STATUS_OPERATIONAL_READ_ONLY: |
|
622 case STATUS_NON_OPERATIONAL: |
|
623 System_Status = status; |
|
624 break; |
|
625 |
|
626 /* Don't allow outsider set this - it should probably |
|
627 * be set if the device config is incomplete or |
|
628 * corrupted or perhaps after some sort of operator |
|
629 * wipe operation. |
|
630 */ |
|
631 case STATUS_DOWNLOAD_REQUIRED: |
|
632 /* Don't allow outsider set this - it should be set |
|
633 * internally at the start of a multi packet download |
|
634 * perhaps indirectly via PT or WF to a config file. |
|
635 */ |
|
636 case STATUS_DOWNLOAD_IN_PROGRESS: |
|
637 /* Don't support backup at present so don't allow setting */ |
|
638 case STATUS_BACKUP_IN_PROGRESS: |
|
639 result = -2; |
|
640 break; |
|
641 |
|
642 default: |
|
643 result = -1; |
|
644 break; |
|
645 } |
|
646 } |
|
647 |
|
648 return (result); |
|
649 } |
|
650 |
|
651 const char *Device_Vendor_Name( |
|
652 void) |
|
653 { |
|
654 return Vendor_Name; |
|
655 } |
|
656 |
|
657 /** Returns the Vendor ID for this Device. |
|
658 * See the assignments at http://www.bacnet.org/VendorID/BACnet%%20Vendor%%20IDs.htm |
|
659 * return: The Vendor ID of this Device. |
|
660 */ |
|
661 uint16_t Device_Vendor_Identifier( |
|
662 void) |
|
663 { |
|
664 return Vendor_Identifier; |
|
665 } |
|
666 |
|
667 void Device_Set_Vendor_Identifier( |
|
668 uint16_t vendor_id) |
|
669 { |
|
670 Vendor_Identifier = vendor_id; |
|
671 } |
|
672 |
|
673 const char *Device_Model_Name( |
|
674 void) |
|
675 { |
|
676 return Model_Name; |
|
677 } |
|
678 |
|
679 bool Device_Set_Model_Name( |
|
680 const char *name, |
|
681 size_t length) |
|
682 { |
|
683 bool status = false; /*return value */ |
|
684 |
|
685 if (length < sizeof(Model_Name)) { |
|
686 memmove(Model_Name, name, length); |
|
687 Model_Name[length] = 0; |
|
688 status = true; |
|
689 } |
|
690 |
|
691 return status; |
|
692 } |
|
693 |
|
694 const char *Device_Firmware_Revision( |
|
695 void) |
|
696 { |
|
697 return Firmware_Revision; |
|
698 } |
|
699 |
|
700 const char *Device_Application_Software_Version( |
|
701 void) |
|
702 { |
|
703 return Application_Software_Version; |
|
704 } |
|
705 |
|
706 bool Device_Set_Application_Software_Version( |
|
707 const char *name, |
|
708 size_t length) |
|
709 { |
|
710 bool status = false; /*return value */ |
|
711 |
|
712 if (length < sizeof(Application_Software_Version)) { |
|
713 memmove(Application_Software_Version, name, length); |
|
714 Application_Software_Version[length] = 0; |
|
715 status = true; |
|
716 } |
|
717 |
|
718 return status; |
|
719 } |
|
720 |
|
721 const char *Device_Description( |
|
722 void) |
|
723 { |
|
724 return Description; |
|
725 } |
|
726 |
|
727 bool Device_Set_Description( |
|
728 const char *name, |
|
729 size_t length) |
|
730 { |
|
731 bool status = false; /*return value */ |
|
732 |
|
733 if (length < sizeof(Description)) { |
|
734 memmove(Description, name, length); |
|
735 Description[length] = 0; |
|
736 status = true; |
|
737 } |
|
738 |
|
739 return status; |
|
740 } |
|
741 |
|
742 const char *Device_Location( |
|
743 void) |
|
744 { |
|
745 return Location; |
|
746 } |
|
747 |
|
748 bool Device_Set_Location( |
|
749 const char *name, |
|
750 size_t length) |
|
751 { |
|
752 bool status = false; /*return value */ |
|
753 |
|
754 if (length < sizeof(Location)) { |
|
755 memmove(Location, name, length); |
|
756 Location[length] = 0; |
|
757 status = true; |
|
758 } |
|
759 |
|
760 return status; |
|
761 } |
|
762 |
|
763 uint8_t Device_Protocol_Version( |
|
764 void) |
|
765 { |
|
766 return BACNET_PROTOCOL_VERSION; |
|
767 } |
|
768 |
|
769 uint8_t Device_Protocol_Revision( |
|
770 void) |
|
771 { |
|
772 return BACNET_PROTOCOL_REVISION; |
|
773 } |
|
774 |
|
775 BACNET_SEGMENTATION Device_Segmentation_Supported( |
|
776 void) |
|
777 { |
|
778 return SEGMENTATION_NONE; |
|
779 } |
|
780 |
|
781 uint32_t Device_Database_Revision( |
|
782 void) |
|
783 { |
|
784 return Database_Revision; |
|
785 } |
|
786 |
|
787 void Device_Set_Database_Revision( |
|
788 uint32_t revision) |
|
789 { |
|
790 Database_Revision = revision; |
|
791 } |
|
792 |
|
793 /* |
|
794 * Shortcut for incrementing database revision as this is potentially |
|
795 * the most common operation if changing object names and ids is |
|
796 * implemented. |
|
797 */ |
|
798 void Device_Inc_Database_Revision( |
|
799 void) |
|
800 { |
|
801 Database_Revision++; |
|
802 } |
|
803 |
|
804 /** Get the total count of objects supported by this Device Object. |
|
805 * @note Since many network clients depend on the object list |
|
806 * for discovery, it must be consistent! |
|
807 * return: The count of objects, for all supported Object types. |
|
808 */ |
|
809 unsigned Device_Object_List_Count( |
|
810 void) |
|
811 { |
|
812 unsigned count = 0; /* number of objects */ |
|
813 struct object_functions *pObject = NULL; |
|
814 |
|
815 /* initialize the default return values */ |
|
816 pObject = Object_Table; |
|
817 while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { |
|
818 if (pObject->Object_Count) { |
|
819 count += pObject->Object_Count(); |
|
820 } |
|
821 pObject++; |
|
822 } |
|
823 |
|
824 return count; |
|
825 } |
|
826 |
|
827 /** Lookup the Object at the given array index in the Device's Object List. |
|
828 * Even though we don't keep a single linear array of objects in the Device, |
|
829 * this method acts as though we do and works through a virtual, concatenated |
|
830 * array of all of our object type arrays. |
|
831 * |
|
832 * param: array_index [in] The desired array index (1 to N) |
|
833 * param: object_type [out] The object's type, if found. |
|
834 * param: instance [out] The object's instance number, if found. |
|
835 * return: True if found, else false. |
|
836 */ |
|
837 bool Device_Object_List_Identifier( |
|
838 unsigned array_index, |
|
839 int *object_type, |
|
840 uint32_t * instance) |
|
841 { |
|
842 bool status = false; |
|
843 unsigned count = 0; |
|
844 unsigned object_index = 0; |
|
845 unsigned temp_index = 0; |
|
846 struct object_functions *pObject = NULL; |
|
847 |
|
848 /* array index zero is length - so invalid */ |
|
849 if (array_index == 0) { |
|
850 return status; |
|
851 } |
|
852 object_index = array_index - 1; |
|
853 /* initialize the default return values */ |
|
854 pObject = Object_Table; |
|
855 while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { |
|
856 if (pObject->Object_Count) { |
|
857 object_index -= count; |
|
858 count = pObject->Object_Count(); |
|
859 if (object_index < count) { |
|
860 /* Use the iterator function if available otherwise |
|
861 * look for the index to instance to get the ID */ |
|
862 if (pObject->Object_Iterator) { |
|
863 /* First find the first object */ |
|
864 temp_index = pObject->Object_Iterator(~(unsigned) 0); |
|
865 /* Then step through the objects to find the nth */ |
|
866 while (object_index != 0) { |
|
867 temp_index = pObject->Object_Iterator(temp_index); |
|
868 object_index--; |
|
869 } |
|
870 /* set the object_index up before falling through to next bit */ |
|
871 object_index = temp_index; |
|
872 } |
|
873 if (pObject->Object_Index_To_Instance) { |
|
874 *object_type = pObject->Object_Type; |
|
875 *instance = |
|
876 pObject->Object_Index_To_Instance(object_index); |
|
877 status = true; |
|
878 break; |
|
879 } |
|
880 } |
|
881 } |
|
882 pObject++; |
|
883 } |
|
884 |
|
885 return status; |
|
886 } |
|
887 |
|
888 /** Determine if we have an object with the given object_name. |
|
889 * If the object_type and object_instance pointers are not null, |
|
890 * and the lookup succeeds, they will be given the resulting values. |
|
891 * param: object_name [in] The desired Object Name to look for. |
|
892 * param: object_type [out] The BACNET_OBJECT_TYPE of the matching Object. |
|
893 * param: object_instance [out] The object instance number of the matching Object. |
|
894 * return: True on success or else False if not found. |
|
895 */ |
|
896 bool Device_Valid_Object_Name( |
|
897 BACNET_CHARACTER_STRING * object_name1, |
|
898 int *object_type, |
|
899 uint32_t * object_instance) |
|
900 { |
|
901 bool found = false; |
|
902 int type = 0; |
|
903 uint32_t instance; |
|
904 unsigned max_objects = 0, i = 0; |
|
905 bool check_id = false; |
|
906 BACNET_CHARACTER_STRING object_name2; |
|
907 struct object_functions *pObject = NULL; |
|
908 |
|
909 max_objects = Device_Object_List_Count(); |
|
910 for (i = 1; i <= max_objects; i++) { |
|
911 check_id = Device_Object_List_Identifier(i, &type, &instance); |
|
912 if (check_id) { |
|
913 pObject = Device_Objects_Find_Functions(type); |
|
914 if ((pObject != NULL) && (pObject->Object_Name != NULL) && |
|
915 (pObject->Object_Name(instance, &object_name2) && |
|
916 characterstring_same(object_name1, &object_name2))) { |
|
917 found = true; |
|
918 if (object_type) { |
|
919 *object_type = type; |
|
920 } |
|
921 if (object_instance) { |
|
922 *object_instance = instance; |
|
923 } |
|
924 break; |
|
925 } |
|
926 } |
|
927 } |
|
928 |
|
929 return found; |
|
930 } |
|
931 |
|
932 /** Determine if we have an object of this type and instance number. |
|
933 * param: object_type [in] The desired BACNET_OBJECT_TYPE |
|
934 * param: object_instance [in] The object instance number to be looked up. |
|
935 * return: True if found, else False if no such Object in this device. |
|
936 */ |
|
937 bool Device_Valid_Object_Id( |
|
938 int object_type, |
|
939 uint32_t object_instance) |
|
940 { |
|
941 bool status = false; /* return value */ |
|
942 struct object_functions *pObject = NULL; |
|
943 |
|
944 pObject = Device_Objects_Find_Functions(object_type); |
|
945 if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) { |
|
946 status = pObject->Object_Valid_Instance(object_instance); |
|
947 } |
|
948 |
|
949 return status; |
|
950 } |
|
951 |
|
952 /** Copy a child object's object_name value, given its ID. |
|
953 * param: object_type [in] The BACNET_OBJECT_TYPE of the child Object. |
|
954 * param: object_instance [in] The object instance number of the child Object. |
|
955 * param: object_name [out] The Object Name found for this child Object. |
|
956 * return: True on success or else False if not found. |
|
957 */ |
|
958 bool Device_Object_Name_Copy( |
|
959 BACNET_OBJECT_TYPE object_type, |
|
960 uint32_t object_instance, |
|
961 BACNET_CHARACTER_STRING * object_name) |
|
962 { |
|
963 struct object_functions *pObject = NULL; |
|
964 bool found = false; |
|
965 |
|
966 pObject = Device_Objects_Find_Functions(object_type); |
|
967 if ((pObject != NULL) && (pObject->Object_Name != NULL)) { |
|
968 found = pObject->Object_Name(object_instance, object_name); |
|
969 } |
|
970 |
|
971 return found; |
|
972 } |
|
973 |
|
974 static void Device_Update_Current_Time( |
|
975 void) |
|
976 { |
|
977 struct tm *tblock = NULL; |
|
978 #if defined(_MSC_VER) |
|
979 time_t tTemp; |
|
980 #else |
|
981 struct timeval tv; |
|
982 #endif |
|
983 /* |
|
984 struct tm |
|
985 |
|
986 int tm_sec Seconds [0,60]. |
|
987 int tm_min Minutes [0,59]. |
|
988 int tm_hour Hour [0,23]. |
|
989 int tm_mday Day of month [1,31]. |
|
990 int tm_mon Month of year [0,11]. |
|
991 int tm_year Years since 1900. |
|
992 int tm_wday Day of week [0,6] (Sunday =0). |
|
993 int tm_yday Day of year [0,365]. |
|
994 int tm_isdst Daylight Savings flag. |
|
995 */ |
|
996 #if defined(_MSC_VER) |
|
997 time(&tTemp); |
|
998 tblock = localtime(&tTemp); |
|
999 #else |
|
1000 if (gettimeofday(&tv, NULL) == 0) { |
|
1001 tblock = localtime(&tv.tv_sec); |
|
1002 } |
|
1003 #endif |
|
1004 |
|
1005 if (tblock) { |
|
1006 datetime_set_date(&Local_Date, (uint16_t) tblock->tm_year + 1900, |
|
1007 (uint8_t) tblock->tm_mon + 1, (uint8_t) tblock->tm_mday); |
|
1008 #if !defined(_MSC_VER) |
|
1009 datetime_set_time(&Local_Time, (uint8_t) tblock->tm_hour, |
|
1010 (uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec, |
|
1011 (uint8_t) (tv.tv_usec / 10000)); |
|
1012 #else |
|
1013 datetime_set_time(&Local_Time, (uint8_t) tblock->tm_hour, |
|
1014 (uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec, 0); |
|
1015 #endif |
|
1016 if (tblock->tm_isdst) { |
|
1017 Daylight_Savings_Status = true; |
|
1018 } else { |
|
1019 Daylight_Savings_Status = false; |
|
1020 } |
|
1021 /* note: timezone is declared in <time.h> stdlib. */ |
|
1022 UTC_Offset = timezone / 60; |
|
1023 } else { |
|
1024 datetime_date_wildcard_set(&Local_Date); |
|
1025 datetime_time_wildcard_set(&Local_Time); |
|
1026 Daylight_Savings_Status = false; |
|
1027 } |
|
1028 } |
|
1029 |
|
1030 void Device_getCurrentDateTime( |
|
1031 BACNET_DATE_TIME * DateTime) |
|
1032 { |
|
1033 Device_Update_Current_Time(); |
|
1034 |
|
1035 DateTime->date = Local_Date; |
|
1036 DateTime->time = Local_Time; |
|
1037 } |
|
1038 |
|
1039 int32_t Device_UTC_Offset(void) |
|
1040 { |
|
1041 Device_Update_Current_Time(); |
|
1042 |
|
1043 return UTC_Offset; |
|
1044 } |
|
1045 |
|
1046 bool Device_Daylight_Savings_Status(void) |
|
1047 { |
|
1048 return Daylight_Savings_Status; |
|
1049 } |
|
1050 |
|
1051 /* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or |
|
1052 BACNET_STATUS_ABORT for abort message */ |
|
1053 int Device_Read_Property_Local( |
|
1054 BACNET_READ_PROPERTY_DATA * rpdata) |
|
1055 { |
|
1056 int apdu_len = 0; /* return value */ |
|
1057 int len = 0; /* apdu len intermediate value */ |
|
1058 BACNET_BIT_STRING bit_string = { 0 }; |
|
1059 BACNET_CHARACTER_STRING char_string = { 0 }; |
|
1060 unsigned i = 0; |
|
1061 int object_type = 0; |
|
1062 uint32_t instance = 0; |
|
1063 unsigned count = 0; |
|
1064 uint8_t *apdu = NULL; |
|
1065 struct object_functions *pObject = NULL; |
|
1066 bool found = false; |
|
1067 |
|
1068 if ((rpdata == NULL) || (rpdata->application_data == NULL) || |
|
1069 (rpdata->application_data_len == 0)) { |
|
1070 return 0; |
|
1071 } |
|
1072 apdu = rpdata->application_data; |
|
1073 switch (rpdata->object_property) { |
|
1074 case PROP_OBJECT_IDENTIFIER: |
|
1075 apdu_len = |
|
1076 encode_application_object_id(&apdu[0], OBJECT_DEVICE, |
|
1077 Object_Instance_Number); |
|
1078 break; |
|
1079 case PROP_OBJECT_NAME: |
|
1080 apdu_len = |
|
1081 encode_application_character_string(&apdu[0], &My_Object_Name); |
|
1082 break; |
|
1083 case PROP_OBJECT_TYPE: |
|
1084 apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE); |
|
1085 break; |
|
1086 case PROP_DESCRIPTION: |
|
1087 characterstring_init_ansi(&char_string, Description); |
|
1088 apdu_len = |
|
1089 encode_application_character_string(&apdu[0], &char_string); |
|
1090 break; |
|
1091 case PROP_SYSTEM_STATUS: |
|
1092 apdu_len = encode_application_enumerated(&apdu[0], System_Status); |
|
1093 break; |
|
1094 case PROP_VENDOR_NAME: |
|
1095 characterstring_init_ansi(&char_string, Vendor_Name); |
|
1096 apdu_len = |
|
1097 encode_application_character_string(&apdu[0], &char_string); |
|
1098 break; |
|
1099 case PROP_VENDOR_IDENTIFIER: |
|
1100 apdu_len = |
|
1101 encode_application_unsigned(&apdu[0], Vendor_Identifier); |
|
1102 break; |
|
1103 case PROP_MODEL_NAME: |
|
1104 characterstring_init_ansi(&char_string, Model_Name); |
|
1105 apdu_len = |
|
1106 encode_application_character_string(&apdu[0], &char_string); |
|
1107 break; |
|
1108 case PROP_FIRMWARE_REVISION: |
|
1109 characterstring_init_ansi(&char_string, Firmware_Revision); |
|
1110 apdu_len = |
|
1111 encode_application_character_string(&apdu[0], &char_string); |
|
1112 break; |
|
1113 case PROP_APPLICATION_SOFTWARE_VERSION: |
|
1114 characterstring_init_ansi(&char_string, |
|
1115 Application_Software_Version); |
|
1116 apdu_len = |
|
1117 encode_application_character_string(&apdu[0], &char_string); |
|
1118 break; |
|
1119 case PROP_LOCATION: |
|
1120 characterstring_init_ansi(&char_string, Location); |
|
1121 apdu_len = |
|
1122 encode_application_character_string(&apdu[0], &char_string); |
|
1123 break; |
|
1124 case PROP_LOCAL_TIME: |
|
1125 Device_Update_Current_Time(); |
|
1126 apdu_len = encode_application_time(&apdu[0], &Local_Time); |
|
1127 break; |
|
1128 case PROP_UTC_OFFSET: |
|
1129 Device_Update_Current_Time(); |
|
1130 apdu_len = encode_application_signed(&apdu[0], UTC_Offset); |
|
1131 break; |
|
1132 case PROP_LOCAL_DATE: |
|
1133 Device_Update_Current_Time(); |
|
1134 apdu_len = encode_application_date(&apdu[0], &Local_Date); |
|
1135 break; |
|
1136 case PROP_DAYLIGHT_SAVINGS_STATUS: |
|
1137 Device_Update_Current_Time(); |
|
1138 apdu_len = |
|
1139 encode_application_boolean(&apdu[0], Daylight_Savings_Status); |
|
1140 break; |
|
1141 case PROP_PROTOCOL_VERSION: |
|
1142 apdu_len = |
|
1143 encode_application_unsigned(&apdu[0], |
|
1144 Device_Protocol_Version()); |
|
1145 break; |
|
1146 case PROP_PROTOCOL_REVISION: |
|
1147 apdu_len = |
|
1148 encode_application_unsigned(&apdu[0], |
|
1149 Device_Protocol_Revision()); |
|
1150 break; |
|
1151 case PROP_PROTOCOL_SERVICES_SUPPORTED: |
|
1152 /* Note: list of services that are executed, not initiated. */ |
|
1153 bitstring_init(&bit_string); |
|
1154 for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { |
|
1155 /* automatic lookup based on handlers set */ |
|
1156 bitstring_set_bit(&bit_string, (uint8_t) i, |
|
1157 apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); |
|
1158 } |
|
1159 apdu_len = encode_application_bitstring(&apdu[0], &bit_string); |
|
1160 break; |
|
1161 case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: |
|
1162 /* Note: this is the list of objects that can be in this device, |
|
1163 not a list of objects that this device can access */ |
|
1164 bitstring_init(&bit_string); |
|
1165 for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { |
|
1166 /* initialize all the object types to not-supported */ |
|
1167 bitstring_set_bit(&bit_string, (uint8_t) i, false); |
|
1168 } |
|
1169 /* set the object types with objects to supported */ |
|
1170 |
|
1171 pObject = Object_Table; |
|
1172 while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { |
|
1173 if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) { |
|
1174 bitstring_set_bit(&bit_string, pObject->Object_Type, true); |
|
1175 } |
|
1176 pObject++; |
|
1177 } |
|
1178 apdu_len = encode_application_bitstring(&apdu[0], &bit_string); |
|
1179 break; |
|
1180 case PROP_OBJECT_LIST: |
|
1181 count = Device_Object_List_Count(); |
|
1182 /* Array element zero is the number of objects in the list */ |
|
1183 if (rpdata->array_index == 0) |
|
1184 apdu_len = encode_application_unsigned(&apdu[0], count); |
|
1185 /* if no index was specified, then try to encode the entire list */ |
|
1186 /* into one packet. Note that more than likely you will have */ |
|
1187 /* to return an error if the number of encoded objects exceeds */ |
|
1188 /* your maximum APDU size. */ |
|
1189 else if (rpdata->array_index == BACNET_ARRAY_ALL) { |
|
1190 for (i = 1; i <= count; i++) { |
|
1191 found = |
|
1192 Device_Object_List_Identifier(i, &object_type, |
|
1193 &instance); |
|
1194 if (found) { |
|
1195 len = |
|
1196 encode_application_object_id(&apdu[apdu_len], |
|
1197 object_type, instance); |
|
1198 apdu_len += len; |
|
1199 /* assume next one is the same size as this one */ |
|
1200 /* can we all fit into the APDU? Don't check for last entry */ |
|
1201 if ((i != count) && (apdu_len + len) >= MAX_APDU) { |
|
1202 /* Abort response */ |
|
1203 rpdata->error_code = |
|
1204 ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; |
|
1205 apdu_len = BACNET_STATUS_ABORT; |
|
1206 break; |
|
1207 } |
|
1208 } else { |
|
1209 /* error: internal error? */ |
|
1210 rpdata->error_class = ERROR_CLASS_SERVICES; |
|
1211 rpdata->error_code = ERROR_CODE_OTHER; |
|
1212 apdu_len = BACNET_STATUS_ERROR; |
|
1213 break; |
|
1214 } |
|
1215 } |
|
1216 } else { |
|
1217 found = |
|
1218 Device_Object_List_Identifier(rpdata->array_index, |
|
1219 &object_type, &instance); |
|
1220 if (found) { |
|
1221 apdu_len = |
|
1222 encode_application_object_id(&apdu[0], object_type, |
|
1223 instance); |
|
1224 } else { |
|
1225 rpdata->error_class = ERROR_CLASS_PROPERTY; |
|
1226 rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; |
|
1227 apdu_len = BACNET_STATUS_ERROR; |
|
1228 } |
|
1229 } |
|
1230 break; |
|
1231 case PROP_MAX_APDU_LENGTH_ACCEPTED: |
|
1232 apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); |
|
1233 break; |
|
1234 case PROP_SEGMENTATION_SUPPORTED: |
|
1235 apdu_len = |
|
1236 encode_application_enumerated(&apdu[0], |
|
1237 Device_Segmentation_Supported()); |
|
1238 break; |
|
1239 case PROP_APDU_TIMEOUT: |
|
1240 apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); |
|
1241 break; |
|
1242 case PROP_NUMBER_OF_APDU_RETRIES: |
|
1243 apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); |
|
1244 break; |
|
1245 case PROP_DEVICE_ADDRESS_BINDING: |
|
1246 /* FIXME: the real max apdu remaining should be passed into function */ |
|
1247 apdu_len = address_list_encode(&apdu[0], MAX_APDU); |
|
1248 break; |
|
1249 case PROP_DATABASE_REVISION: |
|
1250 apdu_len = |
|
1251 encode_application_unsigned(&apdu[0], Database_Revision); |
|
1252 break; |
|
1253 // case PROP_PROPERTY_LIST: |
|
1254 // BACnet_encode_array(Device_Properties_List, |
|
1255 // property_list_count(Device_Properties_List), |
|
1256 // retfalse, encode_application_enumerated); |
|
1257 // break; |
|
1258 default: |
|
1259 rpdata->error_class = ERROR_CLASS_PROPERTY; |
|
1260 rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; |
|
1261 apdu_len = BACNET_STATUS_ERROR; |
|
1262 break; |
|
1263 } |
|
1264 /* only array properties can have array options */ |
|
1265 if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) && |
|
1266 // (rpdata->object_property != PROP_PROPERTY_LIST) && |
|
1267 (rpdata->array_index != BACNET_ARRAY_ALL)) { |
|
1268 rpdata->error_class = ERROR_CLASS_PROPERTY; |
|
1269 rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; |
|
1270 apdu_len = BACNET_STATUS_ERROR; |
|
1271 } |
|
1272 |
|
1273 return apdu_len; |
|
1274 } |
|
1275 |
|
1276 /** Looks up the requested Object and Property, and encodes its Value in an APDU. |
|
1277 * If the Object or Property can't be found, sets the error class and code. |
|
1278 * |
|
1279 * param: rpdata [in,out] Structure with the desired Object and Property info |
|
1280 * on entry, and APDU message on return. |
|
1281 * return: The length of the APDU on success, else BACNET_STATUS_ERROR |
|
1282 */ |
|
1283 int Device_Read_Property( |
|
1284 BACNET_READ_PROPERTY_DATA * rpdata) |
|
1285 { |
|
1286 int apdu_len = BACNET_STATUS_ERROR; |
|
1287 struct object_functions *pObject = NULL; |
|
1288 |
|
1289 /* initialize the default return values */ |
|
1290 rpdata->error_class = ERROR_CLASS_OBJECT; |
|
1291 rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; |
|
1292 pObject = Device_Objects_Find_Functions(rpdata->object_type); |
|
1293 if (pObject != NULL) { |
|
1294 if (pObject->Object_Valid_Instance && |
|
1295 pObject->Object_Valid_Instance(rpdata->object_instance)) { |
|
1296 if (pObject->Object_Read_Property) { |
|
1297 apdu_len = pObject->Object_Read_Property(rpdata); |
|
1298 } |
|
1299 } |
|
1300 } |
|
1301 |
|
1302 return apdu_len; |
|
1303 } |
|
1304 |
|
1305 /* returns true if successful */ |
|
1306 bool Device_Write_Property_Local( |
|
1307 BACNET_WRITE_PROPERTY_DATA * wp_data) |
|
1308 { |
|
1309 bool status = false; /* return value */ |
|
1310 int len = 0; |
|
1311 BACNET_APPLICATION_DATA_VALUE value; |
|
1312 int object_type = 0; |
|
1313 uint32_t object_instance = 0; |
|
1314 int temp; |
|
1315 |
|
1316 /* decode the some of the request */ |
|
1317 len = |
|
1318 bacapp_decode_application_data(wp_data->application_data, |
|
1319 wp_data->application_data_len, &value); |
|
1320 if (len < 0) { |
|
1321 /* error while decoding - a value larger than we can handle */ |
|
1322 wp_data->error_class = ERROR_CLASS_PROPERTY; |
|
1323 wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; |
|
1324 return false; |
|
1325 } |
|
1326 if ((wp_data->object_property != PROP_OBJECT_LIST) && |
|
1327 (wp_data->array_index != BACNET_ARRAY_ALL)) { |
|
1328 /* only array properties can have array options */ |
|
1329 wp_data->error_class = ERROR_CLASS_PROPERTY; |
|
1330 wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; |
|
1331 return false; |
|
1332 } |
|
1333 /* FIXME: len < application_data_len: more data? */ |
|
1334 switch (wp_data->object_property) { |
|
1335 case PROP_OBJECT_IDENTIFIER: |
|
1336 status = |
|
1337 WPValidateArgType(&value, BACNET_APPLICATION_TAG_OBJECT_ID, |
|
1338 &wp_data->error_class, &wp_data->error_code); |
|
1339 if (status) { |
|
1340 if ((value.type.Object_Id.type == OBJECT_DEVICE) && |
|
1341 (Device_Set_Object_Instance_Number(value.type. |
|
1342 Object_Id.instance))) { |
|
1343 /* FIXME: we could send an I-Am broadcast to let the world know */ |
|
1344 } else { |
|
1345 status = false; |
|
1346 wp_data->error_class = ERROR_CLASS_PROPERTY; |
|
1347 wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; |
|
1348 } |
|
1349 } |
|
1350 break; |
|
1351 case PROP_NUMBER_OF_APDU_RETRIES: |
|
1352 status = |
|
1353 WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, |
|
1354 &wp_data->error_class, &wp_data->error_code); |
|
1355 if (status) { |
|
1356 /* FIXME: bounds check? */ |
|
1357 apdu_retries_set((uint8_t) value.type.Unsigned_Int); |
|
1358 } |
|
1359 break; |
|
1360 case PROP_APDU_TIMEOUT: |
|
1361 status = |
|
1362 WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, |
|
1363 &wp_data->error_class, &wp_data->error_code); |
|
1364 if (status) { |
|
1365 /* FIXME: bounds check? */ |
|
1366 apdu_timeout_set((uint16_t) value.type.Unsigned_Int); |
|
1367 } |
|
1368 break; |
|
1369 case PROP_VENDOR_IDENTIFIER: |
|
1370 status = |
|
1371 WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, |
|
1372 &wp_data->error_class, &wp_data->error_code); |
|
1373 if (status) { |
|
1374 /* FIXME: bounds check? */ |
|
1375 Device_Set_Vendor_Identifier((uint16_t) value. |
|
1376 type.Unsigned_Int); |
|
1377 } |
|
1378 break; |
|
1379 // case PROP_SYSTEM_STATUS: |
|
1380 // status = |
|
1381 // WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, |
|
1382 // &wp_data->error_class, &wp_data->error_code); |
|
1383 // if (status) { |
|
1384 // temp = Device_Set_System_Status((BACNET_DEVICE_STATUS) |
|
1385 // value.type.Enumerated, false); |
|
1386 // if (temp != 0) { |
|
1387 // status = false; |
|
1388 // wp_data->error_class = ERROR_CLASS_PROPERTY; |
|
1389 // if (temp == -1) { |
|
1390 // wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; |
|
1391 // } else { |
|
1392 // wp_data->error_code = |
|
1393 // ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; |
|
1394 // } |
|
1395 // } |
|
1396 // } |
|
1397 // break; |
|
1398 case PROP_OBJECT_NAME: |
|
1399 status = |
|
1400 WPValidateString(&value, |
|
1401 characterstring_capacity(&My_Object_Name), false, |
|
1402 &wp_data->error_class, &wp_data->error_code); |
|
1403 if (status) { |
|
1404 /* All the object names in a device must be unique */ |
|
1405 if (Device_Valid_Object_Name(&value.type.Character_String, |
|
1406 &object_type, &object_instance)) { |
|
1407 if ((object_type == wp_data->object_type) && |
|
1408 (object_instance == wp_data->object_instance)) { |
|
1409 /* writing same name to same object */ |
|
1410 status = true; |
|
1411 } else { |
|
1412 status = false; |
|
1413 wp_data->error_class = ERROR_CLASS_PROPERTY; |
|
1414 wp_data->error_code = ERROR_CODE_DUPLICATE_NAME; |
|
1415 } |
|
1416 } else { |
|
1417 Device_Set_Object_Name(&value.type.Character_String); |
|
1418 } |
|
1419 } |
|
1420 break; |
|
1421 case PROP_LOCATION: |
|
1422 status = |
|
1423 WPValidateString(&value, MAX_DEV_LOC_LEN, true, |
|
1424 &wp_data->error_class, &wp_data->error_code); |
|
1425 if (status) { |
|
1426 Device_Set_Location(characterstring_value(&value. |
|
1427 type.Character_String), |
|
1428 characterstring_length(&value.type.Character_String)); |
|
1429 } |
|
1430 break; |
|
1431 |
|
1432 case PROP_DESCRIPTION: |
|
1433 status = |
|
1434 WPValidateString(&value, MAX_DEV_DESC_LEN, true, |
|
1435 &wp_data->error_class, &wp_data->error_code); |
|
1436 if (status) { |
|
1437 Device_Set_Description(characterstring_value(&value. |
|
1438 type.Character_String), |
|
1439 characterstring_length(&value.type.Character_String)); |
|
1440 } |
|
1441 break; |
|
1442 case PROP_MODEL_NAME: |
|
1443 status = |
|
1444 WPValidateString(&value, MAX_DEV_MOD_LEN, true, |
|
1445 &wp_data->error_class, &wp_data->error_code); |
|
1446 if (status) { |
|
1447 Device_Set_Model_Name(characterstring_value(&value. |
|
1448 type.Character_String), |
|
1449 characterstring_length(&value.type.Character_String)); |
|
1450 } |
|
1451 break; |
|
1452 |
|
1453 case PROP_OBJECT_TYPE: |
|
1454 case PROP_SYSTEM_STATUS: |
|
1455 case PROP_VENDOR_NAME: |
|
1456 case PROP_FIRMWARE_REVISION: |
|
1457 case PROP_APPLICATION_SOFTWARE_VERSION: |
|
1458 case PROP_LOCAL_TIME: |
|
1459 case PROP_UTC_OFFSET: |
|
1460 case PROP_LOCAL_DATE: |
|
1461 case PROP_DAYLIGHT_SAVINGS_STATUS: |
|
1462 case PROP_PROTOCOL_VERSION: |
|
1463 case PROP_PROTOCOL_REVISION: |
|
1464 case PROP_PROTOCOL_SERVICES_SUPPORTED: |
|
1465 case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: |
|
1466 case PROP_OBJECT_LIST: |
|
1467 case PROP_MAX_APDU_LENGTH_ACCEPTED: |
|
1468 case PROP_SEGMENTATION_SUPPORTED: |
|
1469 case PROP_DEVICE_ADDRESS_BINDING: |
|
1470 case PROP_DATABASE_REVISION: |
|
1471 // case PROP_PROPERTY_LIST: |
|
1472 wp_data->error_class = ERROR_CLASS_PROPERTY; |
|
1473 wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; |
|
1474 break; |
|
1475 default: |
|
1476 wp_data->error_class = ERROR_CLASS_PROPERTY; |
|
1477 wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; |
|
1478 break; |
|
1479 } |
|
1480 |
|
1481 return status; |
|
1482 } |
|
1483 |
|
1484 /** Looks up the requested Object and Property, and set the new Value in it, |
|
1485 * if allowed. |
|
1486 * If the Object or Property can't be found, sets the error class and code. |
|
1487 * |
|
1488 * param: wp_data [in,out] Structure with the desired Object and Property info |
|
1489 * and new Value on entry, and APDU message on return. |
|
1490 * return: True on success, else False if there is an error. |
|
1491 */ |
|
1492 bool Device_Write_Property( |
|
1493 BACNET_WRITE_PROPERTY_DATA * wp_data) |
|
1494 { |
|
1495 bool status = false; /* Ever the pessamist! */ |
|
1496 struct object_functions *pObject = NULL; |
|
1497 |
|
1498 /* initialize the default return values */ |
|
1499 wp_data->error_class = ERROR_CLASS_OBJECT; |
|
1500 wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; |
|
1501 pObject = Device_Objects_Find_Functions(wp_data->object_type); |
|
1502 if (pObject != NULL) { |
|
1503 if (pObject->Object_Valid_Instance && |
|
1504 pObject->Object_Valid_Instance(wp_data->object_instance)) { |
|
1505 if (pObject->Object_Write_Property) { |
|
1506 status = pObject->Object_Write_Property(wp_data); |
|
1507 } else { |
|
1508 wp_data->error_class = ERROR_CLASS_PROPERTY; |
|
1509 wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; |
|
1510 } |
|
1511 } else { |
|
1512 wp_data->error_class = ERROR_CLASS_OBJECT; |
|
1513 wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; |
|
1514 } |
|
1515 } else { |
|
1516 wp_data->error_class = ERROR_CLASS_OBJECT; |
|
1517 wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; |
|
1518 } |
|
1519 |
|
1520 return (status); |
|
1521 } |
|
1522 |
|
1523 /** Looks up the requested Object, and fills the Property Value list. |
|
1524 * If the Object or Property can't be found, returns false. |
|
1525 * param: [in] The object type to be looked up. |
|
1526 * param: [in] The object instance number to be looked up. |
|
1527 * param: [out] The value list |
|
1528 * return: True if the object instance supports this feature and value changed. |
|
1529 */ |
|
1530 bool Device_Encode_Value_List( |
|
1531 BACNET_OBJECT_TYPE object_type, |
|
1532 uint32_t object_instance, |
|
1533 BACNET_PROPERTY_VALUE * value_list) |
|
1534 { |
|
1535 bool status = false; /* Ever the pessamist! */ |
|
1536 struct object_functions *pObject = NULL; |
|
1537 |
|
1538 pObject = Device_Objects_Find_Functions(object_type); |
|
1539 if (pObject != NULL) { |
|
1540 if (pObject->Object_Valid_Instance && |
|
1541 pObject->Object_Valid_Instance(object_instance)) { |
|
1542 if (pObject->Object_Value_List) { |
|
1543 status = |
|
1544 pObject->Object_Value_List(object_instance, value_list); |
|
1545 } |
|
1546 } |
|
1547 } |
|
1548 |
|
1549 return (status); |
|
1550 } |
|
1551 |
|
1552 /** Checks the COV flag in the requested Object |
|
1553 * param: [in] The object type to be looked up. |
|
1554 * param: [in] The object instance to be looked up. |
|
1555 * return: True if the COV flag is set |
|
1556 */ |
|
1557 bool Device_COV( |
|
1558 BACNET_OBJECT_TYPE object_type, |
|
1559 uint32_t object_instance) |
|
1560 { |
|
1561 bool status = false; /* Ever the pessamist! */ |
|
1562 struct object_functions *pObject = NULL; |
|
1563 |
|
1564 pObject = Device_Objects_Find_Functions(object_type); |
|
1565 if (pObject != NULL) { |
|
1566 if (pObject->Object_Valid_Instance && |
|
1567 pObject->Object_Valid_Instance(object_instance)) { |
|
1568 if (pObject->Object_COV) { |
|
1569 status = pObject->Object_COV(object_instance); |
|
1570 } |
|
1571 } |
|
1572 } |
|
1573 |
|
1574 return (status); |
|
1575 } |
|
1576 |
|
1577 /** Clears the COV flag in the requested Object |
|
1578 * param: [in] The object type to be looked up. |
|
1579 * param: [in] The object instance to be looked up. |
|
1580 */ |
|
1581 void Device_COV_Clear( |
|
1582 BACNET_OBJECT_TYPE object_type, |
|
1583 uint32_t object_instance) |
|
1584 { |
|
1585 struct object_functions *pObject = NULL; |
|
1586 |
|
1587 pObject = Device_Objects_Find_Functions(object_type); |
|
1588 if (pObject != NULL) { |
|
1589 if (pObject->Object_Valid_Instance && |
|
1590 pObject->Object_Valid_Instance(object_instance)) { |
|
1591 if (pObject->Object_COV_Clear) { |
|
1592 pObject->Object_COV_Clear(object_instance); |
|
1593 } |
|
1594 } |
|
1595 } |
|
1596 } |
|
1597 |
|
1598 |
|
1599 /** Looks up the requested Object to see if the functionality is supported. |
|
1600 * param: [in] The object type to be looked up. |
|
1601 * return: True if the object instance supports this feature. |
|
1602 */ |
|
1603 bool Device_Value_List_Supported( |
|
1604 BACNET_OBJECT_TYPE object_type) |
|
1605 { |
|
1606 bool status = false; /* Ever the pessamist! */ |
|
1607 struct object_functions *pObject = NULL; |
|
1608 |
|
1609 pObject = Device_Objects_Find_Functions(object_type); |
|
1610 if (pObject != NULL) { |
|
1611 if (pObject->Object_Value_List) { |
|
1612 status = true; |
|
1613 } |
|
1614 } |
|
1615 |
|
1616 return (status); |
|
1617 } |
|
1618 |
|
1619 /** Initialize the Device Object. |
|
1620 Initialize the group of object helper functions for any supported Object. |
|
1621 Initialize each of the Device Object child Object instances. |
|
1622 * param: The BACnet Object Name of the bacnet server |
|
1623 */ |
|
1624 void Device_Init( |
|
1625 const char * Device_Object_Name) |
|
1626 { |
|
1627 struct object_functions *pObject = NULL; |
|
1628 |
|
1629 /* initialize the Device_Properties_List array */ |
|
1630 int len = 0; |
|
1631 len += BACnet_Init_Properties_List(Device_Properties_List + len, |
|
1632 Device_Properties_Required); |
|
1633 len += BACnet_Init_Properties_List(Device_Properties_List + len, |
|
1634 Device_Properties_Optional); |
|
1635 len += BACnet_Init_Properties_List(Device_Properties_List + len, |
|
1636 Device_Properties_Proprietary); |
|
1637 |
|
1638 characterstring_init_ansi(&My_Object_Name, Device_Object_Name); |
|
1639 Object_Table = &My_Object_Table[0]; // sets glogbal variable! |
|
1640 pObject = Object_Table; |
|
1641 while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { |
|
1642 if (pObject->Object_Init) { |
|
1643 pObject->Object_Init(); |
|
1644 } |
|
1645 pObject++; |
|
1646 } |
|
1647 } |
|
1648 |
|
1649 bool DeviceGetRRInfo( |
|
1650 BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */ |
|
1651 RR_PROP_INFO * pInfo) |
|
1652 { /* Where to put the response */ |
|
1653 bool status = false; /* return value */ |
|
1654 |
|
1655 switch (pRequest->object_property) { |
|
1656 case PROP_VT_CLASSES_SUPPORTED: |
|
1657 case PROP_ACTIVE_VT_SESSIONS: |
|
1658 case PROP_LIST_OF_SESSION_KEYS: |
|
1659 case PROP_TIME_SYNCHRONIZATION_RECIPIENTS: |
|
1660 case PROP_MANUAL_SLAVE_ADDRESS_BINDING: |
|
1661 case PROP_SLAVE_ADDRESS_BINDING: |
|
1662 case PROP_RESTART_NOTIFICATION_RECIPIENTS: |
|
1663 case PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS: |
|
1664 pInfo->RequestTypes = RR_BY_POSITION; |
|
1665 pRequest->error_class = ERROR_CLASS_PROPERTY; |
|
1666 pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY; |
|
1667 break; |
|
1668 |
|
1669 case PROP_DEVICE_ADDRESS_BINDING: |
|
1670 pInfo->RequestTypes = RR_BY_POSITION; |
|
1671 pInfo->Handler = rr_address_list_encode; |
|
1672 status = true; |
|
1673 break; |
|
1674 |
|
1675 default: |
|
1676 pRequest->error_class = ERROR_CLASS_SERVICES; |
|
1677 pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST; |
|
1678 break; |
|
1679 } |
|
1680 |
|
1681 return status; |
|
1682 } |
|
1683 |
|
1684 |
|
1685 |