-/*
- This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
- Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
- Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "mbed.h"
-#include <string>
-using std::string;
-#include "libs/Kernel.h"
-#include "Config.h"
-#include "libs/nuts_bolts.h"
-#include "libs/utils.h"
-
-Config::Config(){
- config_file_found = false;
-}
-
-void Config::on_module_loaded(){
- this->register_for_event(ON_CONSOLE_LINE_RECEIVED);
-}
-
-// When a new line is received, check if it is a command, and if it is, act upon it
-void Config::on_console_line_received( void* argument ){
- string possible_command = *static_cast<string*>(argument);
-
- // We don't compare to a string but to a checksum of that string, this saves some space in flash memory
- unsigned short check_sum = get_checksum( possible_command.substr(0,possible_command.find_first_of(" \r\n")) ); // todo: put this method somewhere more convenient
- //this->kernel->serial->printf("checksum: %u \r\n", check_sum);
-
- // Act depending on command
- switch( check_sum ){
- case config_get_checksum: this->config_get_command( get_arguments(possible_command)) ; break;
- case config_set_checksum: this->config_set_command( get_arguments(possible_command)) ; break;
- case config_load_checksum:this->config_load_command(get_arguments(possible_command)) ; break;
- }
-}
-
-// Command to retreive the value of a specific configuration setting
-void Config::config_get_command( string parameter ){
- this->kernel->serial->printf("%s\r\n", this->get_string( get_checksum( parameter ) ).c_str() );
-}
-
-// Command to set the value of a specific configuration setting
-void Config::config_set_command( string parameters ){
- string setting = shift_parameter(parameters);
- string value = shift_parameter(parameters);
- this->set_string( get_checksum(setting), value );
-}
-
-// Command to reload configuration in all modules ( usefull if you changed one )
-void Config::config_load_command( string parameters ){
- this->kernel->call_event(ON_CONFIG_RELOAD);
-}
-
-// Set a value from the configuration as a string
-// Because we don't like to waste space in Flash with lengthy config parameter names, we take a checksum instead so that the name does not have to be stored
-// See get_checksum
-void Config::set_string( uint16_t check_sum, string value ){
- // Open the config file ( find it if we haven't already found it )
- FILE *lp = fopen(this->get_config_file().c_str(), "r+");
- string buffer;
- int c;
- // For each line
- do {
- c = fgetc (lp);
- if (c == '\n'){
- // We have a new line
- if( buffer[0] == '#' ){ buffer.clear(); continue; } // Ignore comments
- size_t begin_key = buffer.find_first_not_of(" ");
- size_t begin_value = buffer.find_first_not_of(" ", buffer.find_first_of(" ", begin_key));
- // If this line matches the checksum
- if(get_checksum(buffer.substr(begin_key, buffer.find_first_of(" ", begin_key) - begin_key)) != check_sum){ buffer.clear(); continue; }
- int free_space = int(int(buffer.find_first_of("\r\n#", begin_value+1))-begin_value);
- if( int(value.length()) >= free_space ){ this->kernel->serial->printf("ERROR: Not enough room for value\r\n"); fclose(lp); return; }
- // Update value
- for( int i = value.length(); i < free_space; i++){ value += " "; }
- fpos_t pos;
- fgetpos( lp, &pos );
- int start = pos - buffer.length() + begin_value - 1;
- fseek(lp, start, SEEK_SET);
- fputs(value.c_str(), lp);
- fclose(lp);
- return;
- }else{
- buffer += c;
- }
- } while (c != EOF);
- fclose(lp);
- this->kernel->serial->printf("ERROR: configuration key not found\r\n");
-}
-
-// Get a value from the configuration as a string
-// Because we don't like to waste space in Flash with lengthy config parameter names, we take a checksum instead so that the name does not have to be stored
-// See get_checksum
-string Config::get_string(uint16_t check_sum){
- // Open the config file ( find it if we haven't already found it )
- FILE *lp = fopen(this->get_config_file().c_str(), "r");
- string buffer;
- int c;
- // For each line
- do {
- c = fgetc (lp);
- if (c == '\n'){
- // We have a new line
- if( buffer[0] == '#' ){ buffer.clear(); continue; } // Ignore comments
- size_t begin_key = buffer.find_first_not_of(" ");
- size_t begin_value = buffer.find_first_not_of(" ", buffer.find_first_of(" ", begin_key));
- // If this line matches the checksum
- if(get_checksum(buffer.substr(begin_key, buffer.find_first_of(" ", begin_key) - begin_key)) != check_sum){ buffer.clear(); continue; }
- fclose(lp);
- return buffer.substr(begin_value, buffer.find_first_of("\r\n# ", begin_value+1)-begin_value);
- }else{
- buffer += c;
- }
- } while (c != EOF);
- fclose(lp);
- this->kernel->serial->printf("ERROR: configuration key not found\r\n");
-}
-
-// Get a value from the file as a number
-double Config::get(uint16_t check_sum){
- return atof(this->get_string( check_sum ).c_str());
-}
-
-// Get the filename for the config file
-string Config::get_config_file(){
- if( this->config_file_found ){ return this->config_file; }
- this->try_config_file("/local/config");
- this->try_config_file("/sd/config");
- if( this->config_file_found ){
- return this->config_file;
- }else{
- printf("ERROR: no config file found\r\n");
- }
-}
-
-// Tool function for get_config_file
-inline void Config::try_config_file(string candidate){
- FILE *lp = fopen(candidate.c_str(), "r");
- if(lp){ this->config_file_found = true; this->config_file = candidate; }
- fclose(lp);
-}
-
-
-
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+using namespace std;
+#include <vector>
+#include <string>
+
+#include "libs/Kernel.h"
+#include "Config.h"
+#include "ConfigValue.h"
+#include "ConfigSource.h"
+#include "ConfigCache.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/SerialMessage.h"
+#include "libs/ConfigSources/FileConfigSource.h"
+
+Config::Config(){
+ this->config_cache_loaded = false;
+
+ // Config source for */config files
+ this->config_sources.push_back( new FileConfigSource("/local/config", LOCAL_CONFIGSOURCE_CHECKSUM) );
+ this->config_sources.push_back( new FileConfigSource("/sd/config", SD_CONFIGSOURCE_CHECKSUM ) );
+
+ // Pre-load the config cache
+ this->config_cache_load();
+
+}
+
+void Config::on_module_loaded(){}
+
+void Config::on_console_line_received( void* argument ){}
+
+void Config::set_string( string setting, string value ){
+ ConfigValue* cv = new ConfigValue;
+ cv->found = true;
+ get_checksums(cv->check_sums, setting);
+ cv->value = value;
+
+ this->config_cache.replace_or_push_back(cv);
+
+ this->kernel->call_event(ON_CONFIG_RELOAD);
+}
+
+void Config::get_module_list(vector<uint16_t>* list, uint16_t family){
+ for( unsigned int i=1; i<this->config_cache.size(); i++){
+ ConfigValue* value = this->config_cache.at(i);
+ //if( value->check_sums.size() == 3 && value->check_sums.at(2) == 29545 && value->check_sums.at(0) == family ){
+ if( value->check_sums[2] == 29545 && value->check_sums[0] == family ){
+ // We found a module enable for this family, add it's number
+ list->push_back(value->check_sums[1]);
+ }
+ }
+}
+
+
+// Command to load config cache into buffer for multiple reads during init
+void Config::config_cache_load(){
+
+ this->config_cache_clear();
+
+ // First element is a special empty ConfigValue for values not found
+ ConfigValue* result = new ConfigValue;
+ this->config_cache.push_back(result);
+
+ // For each ConfigSource in our stack
+ for( unsigned int i = 0; i < this->config_sources.size(); i++ ){
+ ConfigSource* source = this->config_sources[i];
+ source->transfer_values_to_cache(&this->config_cache);
+ }
+
+ this->config_cache_loaded = true;
+}
+
+// Command to clear the config cache after init
+void Config::config_cache_clear(){
+ while( ! this->config_cache.empty() ){
+ delete this->config_cache.back();
+ this->config_cache.pop_back();
+ }
+ this->config_cache_loaded = false;
+}
+
+
+ConfigValue* Config::value(uint16_t check_sum_a, uint16_t check_sum_b, uint16_t check_sum_c ){
+ uint16_t check_sums[3];
+ check_sums[0] = check_sum_a;
+ check_sums[1] = check_sum_b;
+ check_sums[2] = check_sum_c;
+ return this->value(check_sums);
+}
+
+ConfigValue* Config::value(uint16_t check_sum_a, uint16_t check_sum_b){
+ uint16_t check_sums[3];
+ check_sums[0] = check_sum_a;
+ check_sums[1] = check_sum_b;
+ check_sums[2] = 0x0000;
+ return this->value(check_sums);
+}
+
+ConfigValue* Config::value(uint16_t check_sum){
+ uint16_t check_sums[3];
+ check_sums[0] = check_sum;
+ check_sums[1] = 0x0000;
+ check_sums[2] = 0x0000;
+ return this->value(check_sums);
+}
+
+// Get a value from the configuration as a string
+// Because we don't like to waste space in Flash with lengthy config parameter names, we take a checksum instead so that the name does not have to be stored
+// See get_checksum
+ConfigValue* Config::value(uint16_t check_sums[]){
+ ConfigValue* result = this->config_cache[0];
+ bool cache_preloaded = this->config_cache_loaded;
+ if( !cache_preloaded ){ this->config_cache_load(); }
+
+ for( unsigned int i=1; i<this->config_cache.size(); i++){
+ // If this line matches the checksum
+ bool match = true;
+ unsigned int counter = 0;
+ while(check_sums[counter] != 0x0000 && counter <= 2 ){
+ if(this->config_cache[i]->check_sums[counter] != check_sums[counter] ){
+ match = false;
+ break;
+ }
+ counter++;
+ }
+ if( match == false ){
+ continue;
+ }
+ result = this->config_cache[i];
+ break;
+ }
+
+ if( !cache_preloaded ){
+ this->config_cache_clear();
+ }
+ return result;
+}
+
+
+