11/**
22 * @file
3- * @brief The BACnet shell commands for debugging and testing
3+ * @brief The BACnet shell commands for debugging and testing settings
44 * @author Steve Karg <[email protected] > 55 * @date May 2024
66 * @copyright SPDX-License-Identifier: Apache-2.0
77 */
88#include <stdlib.h>
99#include <stdint.h>
1010#include <errno.h>
11+ #include <ctype.h>
1112#include <zephyr/shell/shell.h>
1213#include <bacnet_settings/bacnet_storage.h>
1314#include <bacnet/bactext.h>
@@ -25,13 +26,14 @@ static const char Storage_Base_Name[] = CONFIG_BACNET_STORAGE_BASE_NAME;
2526 */
2627static int cmd_key (BACNET_STORAGE_KEY * key , const struct shell * sh , size_t argc , char * * argv )
2728{
28- uint16_t object_type ;
29- uint32_t object_instance ;
30- uint32_t property_id = 77 ;
29+ uint16_t object_type = 0 ;
30+ uint32_t object_instance = 0 ;
31+ uint32_t property_id = 75 ;
3132 uint32_t array_index = BACNET_STORAGE_ARRAY_INDEX_NONE ;
3233 long value = 0 ;
3334 unsigned long unsigned_value = 0 ;
34- int found_index = 0 ;
35+ int found_index = 0 , scan_count = 0 ;
36+ char property_name [80 ] = {0 };
3537
3638 if (argc < 3 ) {
3739 shell_error (sh , "Usage: %s <object-type> <instance> <property> [value]" , argv [0 ]);
@@ -57,33 +59,30 @@ static int cmd_key(BACNET_STORAGE_KEY *key, const struct shell *sh, size_t argc,
5759 return - EINVAL ;
5860 }
5961 object_instance = (uint32_t )unsigned_value ;
60- /* property can have @ to denote priority or array */
61- char * at_ptr = strchr (argv [3 ], '@' );
62- if (at_ptr ) {
63- if (!bacnet_storage_strtoul (at_ptr + 1 , & unsigned_value )) {
64- shell_error (sh , "Invalid array-index: \"%s\"" , at_ptr );
62+ /* property can have [] to denote priority or array */
63+ scan_count = sscanf (argv [3 ], "%lu[%u]" , & unsigned_value , & array_index );
64+ if (scan_count < 1 ) {
65+ scan_count = sscanf (argv [3 ], "%80s[%u]" , property_name , & array_index );
66+ if (scan_count < 1 ) {
67+ shell_error (sh , "Invalid property: %s." , argv [3 ]);
6568 return - EINVAL ;
6669 }
67- if (unsigned_value > UINT32_MAX ) {
68- shell_error (sh , "Invalid array-index: \"%s\". Must be 0-4294967295." ,
69- at_ptr );
70+ if (bactext_property_strtol (property_name , & found_index )) {
71+ value = found_index ;
72+ } else {
73+ shell_error (sh , "Invalid property: %s." , property_name );
7074 return - EINVAL ;
7175 }
72- array_index = (uint32_t )unsigned_value ;
73- /* null terminate the string at the @ symbol */
74- * at_ptr = 0 ;
75- }
76- if (bactext_property_strtol (argv [3 ], & found_index )) {
77- value = found_index ;
78- } else {
79- shell_error (sh , "Invalid property: %s." , argv [1 ]);
80- return - EINVAL ;
76+ if (value > UINT32_MAX ) {
77+ shell_error (sh , "Invalid property: %s. Must be 0-4294967295." , argv [3 ]);
78+ return - EINVAL ;
79+ }
80+ unsigned_value = (uint32_t )value ;
8181 }
82- if (value > UINT32_MAX ) {
83- shell_error (sh , "Invalid property: %s. Must be 0-4294967295." , argv [3 ]);
84- return - EINVAL ;
82+ if (scan_count < 2 ) {
83+ array_index = BACNET_STORAGE_ARRAY_INDEX_NONE ;
8584 }
86- property_id = (uint32_t )value ;
85+ property_id = (uint32_t )unsigned_value ;
8786 /* setup the storage key */
8887 bacnet_storage_key_init (key , object_type , object_instance , property_id , array_index );
8988
@@ -102,39 +101,59 @@ static int cmd_value(const struct shell *sh, size_t argc, char **argv)
102101 char key_name [BACNET_STORAGE_KEY_SIZE_MAX + 1 ] = {0 };
103102 uint8_t data [BACNET_STORAGE_VALUE_SIZE_MAX + 1 ] = {0 };
104103 BACNET_STORAGE_KEY key = {0 };
105- int rc ;
104+ int rc , len , data_len ;
106105 unsigned enumerated_value = 0 ;
106+ unsigned long unsigned_value = 0 ;
107+ float real_value = 0.0f ;
108+ long signed_value = 0 ;
109+ int scan_count = 0 ;
110+ unsigned object_type = 0 ;
111+ unsigned instance = 0 ;
107112 bool status = false;
108- bool null_value = false;
109113 BACNET_APPLICATION_DATA_VALUE value = {0 };
110114 BACNET_OBJECT_PROPERTY_VALUE object_value = {0 };
111115 char value_name [80 ] = {0 };
112116 char * value_string = NULL ;
113- uint8_t value_tag ;
114- int len ;
115117
116118 rc = cmd_key (& key , sh , argc , argv );
117119 if (rc < 0 ) {
118120 return rc ;
119121 }
122+ /* read the current value which also determines
123+ the tag when setting the value */
124+ rc = bacnet_storage_get (& key , data , sizeof (data ));
125+ if (rc < 0 ) {
126+ shell_error (sh , "Unable to get %s" , key_name );
127+ return - EINVAL ;
128+ }
129+ data_len = rc ;
120130 /* convert the key to a string for the shell to print */
121131 (void )bacnet_storage_key_encode (key_name , sizeof (key_name ), & key );
122- if (argc > 4 ) {
132+ /* check for an assigned tag */
133+ if ((argc > 5 ) && bacnet_storage_strtoul (argv [4 ], & unsigned_value )) {
134+ value .tag = unsigned_value ;
135+ value_string = argv [5 ];
136+ } else if (argc > 4 ) {
123137 value_string = argv [4 ];
124- if (property_list_commandable_member (key .object_type , key .property_id )) {
125- /* check for case insensitive NULL string */
126- if (bacnet_strnicmp (value_string , "NULL" , 4 ) == 0 ) {
127- null_value = true;
128- }
129- }
138+ }
139+ if (value_string ) {
140+ len = bacapp_decode_application_data (data , data_len , & value );
130141 /* convert the string value into a tagged union value */
131- if (null_value ) {
132- value .tag = BACNET_APPLICATION_TAG_NULL ;
133- status = true;
134- } else {
135- value_tag = bacapp_known_property_tag (key .object_type , key .property_id );
136- /* check for known property types */
137- if (value_tag == BACNET_APPLICATION_TAG_ENUMERATED ) {
142+ if (isalpha (value_string [0 ])) {
143+ if (property_list_commandable_member (key .object_type , key .property_id ) &&
144+ (bacnet_strnicmp (value_string , "NULL" , 4 ) == 0 )) {
145+ /* check for case insensitive NULL string */
146+ value .tag = BACNET_APPLICATION_TAG_NULL ;
147+ status = true;
148+ } else if (bacnet_stricmp (value_string , "true" ) == 0 ) {
149+ value .type .Boolean = true;
150+ value .tag = BACNET_APPLICATION_TAG_BOOLEAN ;
151+ status = true;
152+ } else if (bacnet_stricmp (value_string , "false" ) == 0 ) {
153+ value .type .Boolean = false;
154+ value .tag = BACNET_APPLICATION_TAG_BOOLEAN ;
155+ status = true;
156+ } else {
138157 status = bactext_object_property_strtoul (
139158 (BACNET_OBJECT_TYPE )key .object_type ,
140159 (BACNET_PROPERTY_ID )key .property_id , value_string ,
@@ -143,9 +162,63 @@ static int cmd_value(const struct shell *sh, size_t argc, char **argv)
143162 value .tag = BACNET_APPLICATION_TAG_ENUMERATED ;
144163 value .type .Enumerated = (uint32_t )enumerated_value ;
145164 }
146- } else {
147- status = bacapp_parse_application_data (value_tag , value_string ,
148- & value );
165+ }
166+ }
167+ if (!status ) {
168+ switch (value .tag ) {
169+ case BACNET_APPLICATION_TAG_ENUMERATED :
170+ if (bacnet_storage_strtoul (value_string , & unsigned_value )) {
171+ value .type .Enumerated = (uint32_t )unsigned_value ;
172+ status = true;
173+ }
174+ break ;
175+ case BACNET_APPLICATION_TAG_UNSIGNED_INT :
176+ if (bacnet_storage_strtoul (value_string , & unsigned_value )) {
177+ value .type .Unsigned_Int = (uint32_t )unsigned_value ;
178+ status = true;
179+ }
180+ break ;
181+ case BACNET_APPLICATION_TAG_SIGNED_INT :
182+ if (bacnet_storage_strtol (value_string , & signed_value )) {
183+ value .type .Signed_Int = (int32_t )signed_value ;
184+ status = true;
185+ }
186+ break ;
187+ case BACNET_APPLICATION_TAG_REAL :
188+ if (bacnet_storage_strtof (value_string , & real_value )) {
189+ value .type .Real = (float )real_value ;
190+ status = true;
191+ }
192+ break ;
193+ case BACNET_APPLICATION_TAG_BIT_STRING :
194+ status = bitstring_init_ascii (& value .type .Bit_String , value_string );
195+ break ;
196+ case BACNET_APPLICATION_TAG_OCTET_STRING :
197+ status = octetstring_init_ascii_hex (& value .type .Octet_String ,
198+ value_string );
199+ break ;
200+ case BACNET_APPLICATION_TAG_CHARACTER_STRING :
201+ status = characterstring_init_ansi (& value .type .Character_String ,
202+ value_string );
203+ break ;
204+ case BACNET_APPLICATION_TAG_DATE :
205+ status = datetime_date_init_ascii (& value .type .Date , value_string );
206+ break ;
207+ case BACNET_APPLICATION_TAG_TIME :
208+ status = datetime_time_init_ascii (& value .type .Time , value_string );
209+ break ;
210+ case BACNET_APPLICATION_TAG_OBJECT_ID :
211+ scan_count =
212+ sscanf (value_string , "%4d:%7u" , & object_type , & instance );
213+ if (scan_count == 2 ) {
214+ value .type .Object_Id .type = (uint16_t )object_type ;
215+ value .type .Object_Id .instance = instance ;
216+ } else {
217+ status = false;
218+ }
219+ break ;
220+ default :
221+ break ;
149222 }
150223 }
151224 if (status ) {
@@ -171,14 +244,9 @@ static int cmd_value(const struct shell *sh, size_t argc, char **argv)
171244 return - EINVAL ;
172245 }
173246 } else {
174- rc = bacnet_storage_get (& key , data , sizeof (data ));
175- if (rc < 0 ) {
176- shell_error (sh , "Unable to get %s" , key_name );
177- return - EINVAL ;
178- }
179247 /* convert to printable value */
180- len = bacapp_decode_known_array_property (data , rc , & value , key .object_type ,
181- key . property_id , key .array_index );
248+ len = bacapp_decode_known_property (data , rc , & value , key .object_type ,
249+ key .property_id );
182250 if (len < 0 ) {
183251 shell_error (sh , "Unable to decode value for %s" , key_name );
184252 return - EINVAL ;
@@ -287,13 +355,21 @@ static int cmd_delete(const struct shell *sh, size_t argc, char **argv)
287355static int print_storage_data (BACNET_STORAGE_KEY * key , const void * data , size_t data_len ,
288356 void * context )
289357{
358+ char data_string [80 ] = {0 };
359+ char hex_string [3 ] = {0 };
360+ unsigned i ;
290361 const struct shell * sh = context ;
362+
363+ for (i = 0 ; i < data_len && i < ((sizeof (data_string ) / 2 ) - 1 ); i ++ ) {
364+ snprintf (hex_string , sizeof (hex_string ), "%02X" , ((const uint8_t * )data )[i ]);
365+ strcat (data_string , hex_string );
366+ }
291367 if (key -> array_index == BACNET_STORAGE_ARRAY_INDEX_NONE ) {
292- shell_print (sh , "%s/%u/%u/%u" , Storage_Base_Name , key -> object_type ,
293- key -> object_instance , key -> property_id );
368+ shell_print (sh , "%s/%u/%u/%u=%s " , Storage_Base_Name , key -> object_type ,
369+ key -> object_instance , key -> property_id , data_string );
294370 } else {
295- shell_print (sh , "%s/%u/%u/%u/%u" , Storage_Base_Name , key -> object_type ,
296- key -> object_instance , key -> property_id , key -> array_index );
371+ shell_print (sh , "%s/%u/%u/%u/%u=%s " , Storage_Base_Name , key -> object_type ,
372+ key -> object_instance , key -> property_id , key -> array_index , data_string );
297373 }
298374
299375 return 0 ;
@@ -326,9 +402,14 @@ static int cmd_list(const struct shell *sh, size_t argc, char **argv)
326402
327403SHELL_STATIC_SUBCMD_SET_CREATE (
328404 sub_bacnet_settings_cmds , SHELL_CMD (list , NULL , "list BACnet storage strings" , cmd_list ),
329- SHELL_CMD (value , NULL , "get or set BACnet storage value" , cmd_value ),
330- SHELL_CMD (string , NULL , "get or set BACnet storage string" , cmd_string ),
331- SHELL_CMD (delete , NULL , "delete BACnet storage string" , cmd_delete ), SHELL_SUBCMD_SET_END );
405+ SHELL_CMD (value , NULL , "<object type> <object instance> <property id>[array index] [value]" ,
406+ cmd_value ),
407+ SHELL_CMD (string , NULL ,
408+ "<object type> <object instance> <property id>[array index] [string]" ,
409+ cmd_string ),
410+ SHELL_CMD (delete , NULL , "<object type> <object instance> <property id>[array index]" ,
411+ cmd_delete ),
412+ SHELL_SUBCMD_SET_END );
332413
333414SHELL_SUBCMD_ADD ((bacnet ), settings , & sub_bacnet_settings_cmds , "BACnet settings commands" , NULL , 1 ,
334415 0 );
0 commit comments