Commander-API  V2.1.0
Simple Command Parser
Loading...
Searching...
No Matches
Commander-API.cpp
Go to the documentation of this file.
1/*
2 * Created on June 18 2020
3 *
4 * Copyright (c) 2020 - Daniel Hajnal
5 * hajnal.daniel96@gmail.com
6 * This file is part of the Commander-API project.
7 * Modified 2022.02.06
8*/
9
10/*
11MIT License
12
13Copyright (c) 2020 Daniel Hajnal
14
15Permission is hereby granted, free of charge, to any person obtaining a copy
16of this software and associated documentation files (the "Software"), to deal
17in the Software without restriction, including without limitation the rights
18to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19copies of the Software, and to permit persons to whom the Software is
20furnished to do so, subject to the following conditions:
21
22The above copyright notice and this permission notice shall be included in all
23copies or substantial portions of the Software.
24
25THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31SOFTWARE.
32*/
33
34
35#include "Commander-API.hpp"
36
38
39void Commander::attachTreeFunction( API_t *API_tree_p, uint32_t API_tree_size_p ){
40
41 // Save parameters to internal variables.
42 API_tree = API_tree_p;
43 API_tree_size = API_tree_size_p;
44
45 #if defined( ARDUINO ) && defined( __AVR__ )
46
47 dbgResponse -> print( F( "API tree attached with " ) );
48 dbgResponse -> print( API_tree_size );
49 dbgResponse -> println( F( " commands." ) );
50
51 #else
52
53 dbgResponse -> print( (const char*)"API tree attached with " );
54 dbgResponse -> print( API_tree_size );
55 dbgResponse -> println( " commands." );
56
57 #endif
58
59}
60
62
63 // Generic conter variables.
64 uint32_t i;
65 uint32_t j;
66
67 // Temporary variable, used to flip elements.
68 API_t temp;
69
70 #ifdef __AVR__
71
72 if( API_tree[ 0 ].name == NULL ){
73
77
78 }
79
80 #endif
81
82 #if defined( ARDUINO ) && defined( __AVR__ )
83
84 dbgResponse -> println( F( "Commander init start" ) );
85
86 #else
87
88 dbgResponse -> println( (const char*)"Commander init start" );
89
90 #endif
91
92 // Make the tree ordered by alphabet.
93 #if defined( ARDUINO ) && defined( __AVR__ )
94
95 dbgResponse -> print( F( "\tCreating alphabetical order... " ) );
96
97 #else
98
99 dbgResponse -> print( (const char*)"\tCreating alphabetical order... " );
100
101 #endif
102
103 for( i = 0; i < API_tree_size; i++ ){
104
105 for( j = i + 1; j < API_tree_size; j++ ){
106
107 if( ( this ->* commander_strcmp )( &API_tree[ i ], &API_tree[ j ] ) > 0 ){
108
109 temp = API_tree[ i ];
110 API_tree[ i ] = API_tree[ j ];
111 API_tree[ j ] = temp;
112
113 }
114
115 }
116
117 }
118
119 // Fill the place variable in the tree with
120 // correct alphabetical place.
121 for( i = 0; i < API_tree_size; i++ ){
122
123 API_tree[ i ].place = i;
124
125 }
126
127 #if defined( ARDUINO ) && defined( __AVR__ )
128
129 dbgResponse -> println( F( "[ OK ]" ) );
130
131 #else
132
133 dbgResponse -> println( (const char*)"[ OK ]" );
134
135 #endif
136
137
138 // Optimize the tree to make it balanced.
139 // It is necessary to speed up the command
140 // search phase.
141 #if defined( ARDUINO ) && defined( __AVR__ )
142
143 dbgResponse -> print( F( "\tCreate balanced binary structure... " ) );
144
145 #else
146
147 dbgResponse -> print( (const char*)"\tCreate balanced binary structure... " );
148
149 #endif
151
152 #if defined( ARDUINO ) && defined( __AVR__ )
153
154 dbgResponse -> println( F( "[ OK ]" ) );
155 dbgResponse -> println( F( "Commander init finished!" ) );
156
157 #else
158
159 dbgResponse -> println( (const char*)"[ OK ]" );
160 dbgResponse -> println( (const char*)"Commander init finished!" );
161
162 #endif
163
164}
165
166uint16_t Commander::find_api_index_by_place( uint16_t place ){
167
168 // Generic counter variable
169 uint16_t i;
170
171 // Go through all commands
172 for( i = 0; i < API_tree_size; i++ ){
173
174 // Check that if we found the desired command
175 if( API_tree[i].place == place ){
176
177 // If we found it, return the index of it.
178 return i;
179
180 }
181
182 }
183
184 return 0;
185
186}
187
188void Commander::swap_api_elements( uint16_t index, uint16_t place ){
189
190 // Buffer that will temporary hold an element.
191 // This is required for a swap.
192 API_t buffer;
193
194 // This variable will store the address of the element defined by the second argument( place ).
195 uint16_t current_index;
196
197 // Find the index in the array by place of the 'i'-th element
198 current_index = find_api_index_by_place( place );
199
200 // save the context of the 'i'-th element to the buffer
201 buffer = API_tree[index];
202
203 // write the 'current_index'-th element to the 'i'-th element
204 API_tree[index] = API_tree[current_index];
205
206 // write the buffer to the 'current_index'-th element
207 API_tree[current_index] = buffer;
208
209}
210
212
213 uint32_t i;
214
215 //API_t buffer;
216
217 // Stores the next elements address in the tree
218 API_t *next;
219
220 // Stores the previous elements address in the tree
221 API_t *prev;
222
223 // It will store string comparison result
224 int32_t comp_res;
225
226 // recursive optimizer need to initialize elementCounter to 0
227 elementCounter = 0;
228
229 // recursively finds the order which is optimal for a balanced tree
231
232 // The order is good, but the connection between the branches broken,
233 // because we swapped the API_tree array elements.
234 // To fix this problem we have to add elements from index 1 and
235 // place them in the binary tree.
236 for( i = 1; i < API_tree_size; i++ ){
237
238 prev = &API_tree[ 0 ];
239
240 comp_res = ( this ->* commander_strcmp )( prev, &API_tree[ i ] );
241
242 (comp_res > 0) ? (next = (prev->left)) : ( next = (prev->right));
243
244 while( next != NULL ){
245
246 prev = next;
247 comp_res = ( this ->* commander_strcmp )( prev, &API_tree[ i ] );
248 (comp_res > 0) ? (next = (prev->left)) : ( next = (prev->right));
249
250 }
251
252 ( comp_res > 0 ) ? ( ( prev->left ) = &API_tree[ i ] ) : ( ( prev->right ) = &API_tree[ i ] );
253
254 }
255
256}
257
258void Commander::recursive_optimizer( int32_t start_index, int32_t stop_index ){
259
260 // The middle number between start and stop index
261 // will be stored in this variable.
262 int32_t mid;
263
264 // Detect the end of recursion.
265 if( start_index > stop_index ){
266
267 return;
268
269 }
270
271 // Find the middle of the interval
272 mid = ( start_index + stop_index ) / 2;
273
274 // Put the right element to it's place
277
278 // Do some recursion for the other intervals
279 recursive_optimizer( start_index, mid - 1 );
280 recursive_optimizer( mid + 1, stop_index );
281
282
283}
284
286
287 // The beginning of the argument list will be stored in this pointer
288 char *arg;
289
290 // This variable tracks the command name length
291 uint32_t cmd_name_cntr;
292
293 // If this flag is set, than the description message will be printed,
294 // and the commands function won't be called.
295 uint8_t show_description = 0;
296
297 // Pointer to the selected command data.
298 API_t *commandData_ptr;
299
300 int32_t pipePos;
301
302 uint32_t i;
303
304 // Copy the command data to the internal buffer.
305 // It is necessary because we have to modify the content
306 // of it. If it is points to a const char array we will
307 // get a bus-fault error without a buffer.
308 strncpy( tempBuff, cmd, COMMANDER_MAX_COMMAND_SIZE );
309
310 pipePos = hasChar( tempBuff, '|' );
311
312 if( pipePos >= 0 ){
313
314 #ifdef COMMANDER_ENABLE_PIPE_MODULE
315 // Terminate where pip is found.
316 tempBuff[ pipePos ] = '\0';
317 #else
318
319 #ifdef __AVR__
320 response -> println( F( "Piping not available on this device!" ) );
321 #else
322 response -> println( (const char*)"Piping not available on this device!" );
323 #endif
324
325 return;
326
327 #endif
328
329 }
330
331 // tempBuff is the address of the first character of the incoming command.
332 // If we give arg variable the value stored in tempBuff means arg will point to
333 // the first character of the command as well.
334 arg = tempBuff;
335
336 // Reset the name counter before we start counting
337 cmd_name_cntr = 0;
338
339 // Find the first space, question mark or a string-end character.
340 // At this time count how long is the command name( in characters )
341 while( ( *arg != '\0' ) && ( *arg != ' ' ) && ( *arg != '?' ) ){
342
343 cmd_name_cntr++;
344 arg++;
345
346 }
347
348 // If a space character found we have to terminate the string there.
349 // It is important because strcmp function will search for string terminator
350 // character, and this way we can separate the command name from its arguments.
351 if( *arg == ' ' ){
352
353 *arg = '\0';
354 arg++;
355
356 }
357
358 // The process is the same as above. The only difference is that this time
359 // we have to set the show_description flag.
360 else if( *arg == '?' ){
361
362 *arg = '\0';
363 arg++;
364 show_description = 1;
365
366 }
367
368 // Try to find the command datata.
369 commandData_ptr = (*this)[ tempBuff ];
370
371 // If it is not a NULL pointer, that means we have a mtach.
372 if( commandData_ptr ){
373
374 // Because we have found the command in the API tree we have to choose
375 // between description printing and executing.
376 // If show_description flag is set, than we have to print the description.
377 if( show_description ){
378
379 if( memoryType == MEMORY_REGULAR ){
380
381 // Print the description text to the output channel.
382 response -> print( commandData_ptr -> name );
383 response -> print( ':' );
384 response -> print( ' ' );
385 response -> println( commandData_ptr -> desc );
386
387 }
388
389 #ifdef __AVR__
390
391 else if( memoryType == MEMORY_PROGMEM ){
392
393 // Print the description text to the output channel.
394 response -> print( commandData_ptr -> name_P );
395 response -> print( ':' );
396 response -> print( ' ' );
397 response -> println( commandData_ptr -> desc_P );
398
399 }
400
401 #endif
402
403
404 }
405
406 // If show_description flag is not set, than we have to execute the commands function.
407 else{
408
409 #ifdef COMMANDER_ENABLE_PIPE_MODULE
410
411 if( pipeChannel.available() > 0 ){
412
413 // pipeChannel.readBytesUntil( '\0', pipeArgBuffer, COMMANDER_MAX_COMMAND_SIZE );
414
415 i = 0;
416
417 while( pipeChannel.available() ){
418
420
422
423 }
424
425 else{
426
428
429 }
430
431 i++;
432
433 }
434
436
437 pipeArgBuffer[ i ] = '\0';
438
439 }
440
442
443 arg = pipeArgBuffer;
444
445 }
446
447 if( pipePos > 0 ){
448
449 // Execute commands function and redirect the output to pipe.
450 (commandData_ptr -> func)( arg, &pipeChannel );
451
452 }
453
454 else{
455
456 // Execute command function.
457 (commandData_ptr -> func)( arg, response );
458
459 }
460
461 if( pipePos > 0 ){
462
463 // To remowe whitespace from the new command begin.
464 while( tempBuff[ pipePos + 1 ] == ' ' ){
465 pipePos++;
466 }
467
468 executeCommand( &tempBuff[ pipePos + 1 ] );
469
470 }
471
472 #else
473
474 // Execute command function.
475 (commandData_ptr -> func)( arg, response );
476
477 #endif
478
479 }
480
481 }
482
483 // If it is not an added function, we have to check for internal functions.
484 // 'help' is an internal function that prints the available commands in order.
485 else if( strcmp( tempBuff, (const char*)"help" ) == 0 ){
486
487 // We have to check for single or described help function.
488 if( strcmp( arg, (const char*)"-d" ) == 0 ){
489
490 helpFunction( true );
491
492 }
493
494 else{
495
496 helpFunction();
497
498 }
499
500 }
501
502 else{
503
504 // If we went through the whole tree and we did not found the command in it,
505 // we have to notice the user abut the problem. Maybe a Type-O
506 #if defined( ARDUINO ) && defined( __AVR__ )
507
508 response -> print( F( "Command \'" ) );
509 response -> print( tempBuff );
510 response -> println( F( "\' not found!" ) );
511
512 #else
513
514 response -> print( (const char*)"Command \'" );
515 response -> print( tempBuff );
516 response -> println( (const char*)"\' not found!" );
517
518 #endif
519
520 #ifdef COMMANDER_ENABLE_PIPE_MODULE
521
522 // Clear the pipe at error.
523 while( pipeChannel.available() ){
524
526
527 }
528
529 #endif
530
531 }
532
533}
534
535void Commander::execute( char *cmd ){
536
537 // Default execute handler, so the default response will be chosen.
539
540 // Execute the command.
541 executeCommand( cmd );
542
543}
544
545void Commander::execute( const char *cmd ){
546
547 // Default execute handler, so the default response will be chosen.
549
550 // Execute the command.
551 executeCommand( (char*)cmd );
552
553}
554
555void Commander::execute( char *cmd, Stream *resp ){
556
557 response = resp;
558
559 // Execute the command.
560 executeCommand( cmd );
561
562}
563
564void Commander::execute( const char *cmd, Stream *resp ){
565
566 response = resp;
567
568 // Execute the command.
569 executeCommand( (char*)cmd );
570
571}
572
573void Commander::attachDebugChannel( Stream *resp ){
574
575 dbgResponse = resp;
576
577 // Enable debug messages.
578 debugEnabled = true;
579
580}
581
583
584 debugEnabled = true;
585
586}
587
589
590 debugEnabled = false;
591
592}
593
595
596 // Detect wrong addressing.
597 if( ( i < 0 ) || ( i >= (int)API_tree_size ) ){
598
599 return NULL;
600
601 }
602
603 return &API_tree[ i ];
604
605}
606
608
609 // Stores the next elements address in the tree
610 API_t *next;
611
612 // Stores the previous elements address in the tree
613 API_t *prev;
614
615 // It will store string compersation result
616 int8_t comp_res;
617
618 prev = &API_tree[ 0 ];
619
620 comp_res = ( this ->* commander_strcmp_tree_ram )( prev, name );
621
622 (comp_res > 0) ? (next = (prev->left)) : ( next = (prev->right));
623
624 // Go through the binary tree until you find a match, or until you find the
625 // end of the tree.
626 while( ( comp_res !=0 ) && ( next != NULL ) ){
627
628 prev = next;
629 comp_res = ( this ->* commander_strcmp_tree_ram )( prev, name );
630 (comp_res > 0) ? (next = (prev->left)) : ( next = (prev->right));
631
632 }
633
634 // If comp_res variable has a zero in it, that means in the last iteration
635 // we had a match.
636 if( comp_res == 0 ){
637
638 return prev;
639
640 }
641
642 // If we did not found the command we return NULL.
643 return NULL;
644
645}
646
648
649 return (*this)[ (char*)name ];
650
651}
652
653void Commander::helpFunction( bool description ){
654
655 helpFunction( description, response );
656
657}
658
659void Commander::helpFunction( bool description, Stream* out, bool style ){
660
661 uint32_t i;
662 uint32_t j;
663
664 if( style ){
665
666 #if defined( ARDUINO ) && defined( __AVR__ )
667
668 out -> println( F( "\033[1;31m----\033[1;32m Available commands \033[1;31m----\033[0;37m\r\n" ) );
669
670 #else
671
672 out -> println( (const char*)"\033[1;31m----\033[1;32m Available commands \033[1;31m----\033[0;37m\r\n" );
673
674 #endif
675
676 }
677
678 else{
679
680 #if defined( ARDUINO ) && defined( __AVR__ )
681
682 out -> println( F( "---- Available commands ----\r\n" ) );
683
684 #else
685
686 out -> println( (const char*)"---- Available commands ----\r\n" );
687
688 #endif
689
690 }
691
692 for( i = 0; i < API_tree_size; i++ ){
693
694 for( j = 0; j < API_tree_size; j++ ){
695
696 if( API_tree[ j ].place == i ){
697
698 // Check if the description is required to print.
699 if( description ){
700
701 // Check if style is enabled.
702 if( style ){
703
704 if( memoryType == MEMORY_REGULAR ){
705
706 out -> print( (const char*)"\033[1;32m" );
707 out -> print( API_tree[ j ].name );
708 out -> print( (const char*)"\033[0;37m" );
709 out -> print( ':' );
710 out -> print( ' ' );
711 out -> print( API_tree[ j ].desc );
712 out -> println();
713
714 }
715
716 #ifdef __AVR__
717
718 else if( memoryType == MEMORY_PROGMEM ){
719
720 out -> print( F( "\033[1;32m" ) );
721 out -> print( API_tree[ j ].name_P );
722 out -> print( F( "\033[0;37m" ) );
723 out -> print( ':' );
724 out -> print( ' ' );
725 out -> print( API_tree[ j ].desc_P );
726 out -> println();
727 out -> println();
728
729 }
730
731 #endif
732
733 }
734
735 else{
736
737 if( memoryType == MEMORY_REGULAR ){
738
739 out -> print( API_tree[ j ].name );
740 out -> println( ':' );
741 out -> print( '\t' );
742 out -> print( API_tree[ j ].desc );
743 out -> println();
744 out -> println();
745
746 }
747
748 #ifdef __AVR__
749
750 else if( memoryType == MEMORY_PROGMEM ){
751
752 out -> print( API_tree[ j ].name_P );
753 out -> println( ':' );
754 out -> print( '\t' );
755 out -> print( API_tree[ j ].desc_P );
756 out -> println();
757 out -> println();
758
759 }
760
761 #endif
762
763 }
764
765 }
766
767 else{
768
769 if( memoryType == MEMORY_REGULAR ){
770
771 out -> println( API_tree[ j ].name );
772
773 }
774
775 #ifdef __AVR__
776
777 else if( memoryType == MEMORY_PROGMEM ){
778
779 out -> println( API_tree[ j ].name_P );
780
781 }
782
783 #endif
784
785 }
786
787 continue;
788
789 }
790
791 }
792
793 }
794
795}
796
797int32_t Commander::hasChar( char* str, char c ){
798
799 int32_t cntr = 0;
800
801 while( str[ cntr ] ){
802
803 if( str[ cntr ] == c ){
804
805 return cntr;
806
807 }
808
809 cntr++;
810
811 }
812
813 return -1;
814
815}
816
817void Commander::printHelp( Stream* out ){
818
819 helpFunction( true, out, true );
820
821}
822
824
825 return strcmp( element1 -> name, element2 -> name );
826
827}
828
830
831 return strcmp( element1 -> name, element2 );
832
833}
834
835#ifdef __AVR__
836
838
839 strncpy_P( progmemBuffer, ( PGM_P ) element1 -> name_P, COMMANDER_MAX_COMMAND_SIZE );
840 return strcmp_P( progmemBuffer, ( PGM_P )element2 -> name_P );
841
842}
843
845
846 return strcmp_P( element2, (PGM_P)element1 -> name_P ) * -1;
847
848}
849
850#endif
#define COMMANDER_API_VERSION
#define COMMANDER_MAX_COMMAND_SIZE
API_t * operator[](int i)
Array index operator overload for int type.
void swap_api_elements(uint16_t index, uint16_t place)
Swap two API elements in the tree.
memoryType_t memoryType
Flag for memory type.
void disableDebug()
Disables debug messages.
API_t * API_tree
Starting address of the API-tree.
int commander_strcmp_tree_ram_progmem(API_t *element1, char *element2)
Compare an API-tree element's name with a regular string.
uint32_t API_tree_size
Number of elements in the API-tree.
int(Commander::* commander_strcmp)(API_t *element1, API_t *element2)
Function pointer to an internal strcmp like function.
char tempBuff[COMMANDER_MAX_COMMAND_SIZE]
Internal command buffer.
void executeCommand(char *cmd)
Command execution.
commandResponse defaultResponse
Default response handler class.
uint32_t elementCounter
Internal variable for counting purpose.
int(Commander::* commander_strcmp_tree_ram)(API_t *element1, char *element2)
Function pointer to an internal strcmp like function.
int commander_strcmp_progmem(API_t *element1, API_t *element2)
Compare two API-tree element's name.
Stream * response
Pointer to response class.
char progmemBuffer[COMMANDER_MAX_COMMAND_SIZE]
With the PROGMEM implementation we need to copy the data from the PROGMEM area to a buffer for comper...
static const char * version
Library version string.
void helpFunction(bool description=false)
Help function.
void printHelp(Stream *out)
Prints out the help string to the specified Stream.
void init()
Initializer.
int32_t hasChar(char *str, char c)
Search for a character in a string.
void recursive_optimizer(int32_t start_index, int32_t stop_index)
Recursive function optimize a section in the tree.
Stream * dbgResponse
Pointer to response class.
int commander_strcmp_tree_ram_regular(API_t *element1, char *element2)
Compare an API-tree element's name with a regular string.
char pipeArgBuffer[COMMANDER_MAX_COMMAND_SIZE]
If piping happenes the output of the first command will be copied to this buffer.
bool debugEnabled
Flag to enable or disable debug messages.
void attachDebugChannel(Stream *resp)
Debug channel for Serial.
void execute(char *cmd)
Default execution function.
int commander_strcmp_regular(API_t *element1, API_t *element2)
Compare two API-tree element's name.
void optimize_api_tree()
Optimizes the tree to make it balanced.
void enableDebug()
Enables debug messages.
uint16_t find_api_index_by_place(uint16_t place)
Find an API element in the tree by alphabetical place.
commanderPipeChannel pipeChannel
Channel for the internal piping.
void attachTreeFunction(API_t *API_tree_p, uint32_t API_tree_size_p)
Attach API-tree to the object.
@ MEMORY_PROGMEM
Progmem memory implementation.
@ MEMORY_REGULAR
Regular memory implementation.
int read() override
Read one byte form the channel.
int available() override
Available bytes in the channel.
Structure for command data.
struct API_t * left
struct API_t * right