You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
4.4 KiB
211 lines
4.4 KiB
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*- |
|
// |
|
// This is free software; you can redistribute it and/or modify it under |
|
// the terms of the GNU Lesser General Public License as published by the |
|
// Free Software Foundation; either version 2.1 of the License, or (at |
|
// your option) any later version. |
|
// |
|
/// The AP variable interface. This allows different types |
|
/// of variables to be passed to blocks for floating point |
|
/// math, memory management, etc. |
|
|
|
#include "AP_Var.h" |
|
|
|
const AP_Float AP_GainUnity(1.0); |
|
const AP_Float AP_GainNegativeUnity(-1.0); |
|
const AP_Float AP_Zero(0); |
|
|
|
// Constructor |
|
// |
|
AP_Var::AP_Var(AP_VarAddress address, |
|
const prog_char *name, |
|
const AP_VarScope *scope) : |
|
_address(address), |
|
_name(name), |
|
_scope(scope) |
|
{ |
|
// if the variable is interesting, link it into the list |
|
if (_name || (_address != AP_VarNoAddress)) { |
|
_link = _variables; |
|
_variables = this; |
|
} |
|
} |
|
|
|
// Destructor |
|
// |
|
// Removes named variables from the list. |
|
// |
|
AP_Var::~AP_Var(void) |
|
{ |
|
AP_Var *p; |
|
|
|
// only do this for variables that were considered interesting |
|
if (_name || (_address != AP_VarNoAddress)) { |
|
// if we are at the head of the list - for variables |
|
// recently constructed this is usually the case |
|
if (_variables == this) { |
|
// remove us from the head of the list |
|
_variables = _link; |
|
} else { |
|
// traverse the list looking for the entry that points to us |
|
p = _variables; |
|
while (p) { |
|
// is it pointing at us? |
|
if (p->_link == this) { |
|
// make it point at what we point at, and done |
|
p->_link = _link; |
|
break; |
|
} |
|
// try the next one |
|
p = p->_link; |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Copy the variable name to the provided buffer. |
|
// |
|
void |
|
AP_Var::copy_name(char *buffer, size_t bufferSize) const |
|
{ |
|
buffer[0] = '\0'; |
|
if (_scope) |
|
_scope->copy_name(buffer, bufferSize); |
|
strlcat_P(buffer, _name, bufferSize); |
|
} |
|
|
|
// Compute any offsets that should be applied to a variable in this scope. |
|
// |
|
// Note that this depends on the default _address value for scopes being zero. |
|
// |
|
AP_Var::AP_VarAddress |
|
AP_VarScope::get_address(void) const |
|
{ |
|
AP_Var::AP_VarAddress addr = _address; |
|
|
|
if (_parent) |
|
addr += _parent->get_address(); |
|
|
|
return addr; |
|
} |
|
|
|
// Compute the address in EEPROM where the variable is stored |
|
// |
|
AP_Var::AP_VarAddress |
|
AP_Var::_get_address(void) const |
|
{ |
|
AP_VarAddress addr = _address; |
|
|
|
// if we have an address at all |
|
if ((addr != AP_VarNoAddress) && _scope) |
|
addr += _scope->get_address(); |
|
|
|
return addr; |
|
} |
|
|
|
// Save the variable to EEPROM, if supported |
|
// |
|
void |
|
AP_Var::save(void) const |
|
{ |
|
AP_VarAddress addr = _get_address(); |
|
|
|
if (addr != AP_VarNoAddress) { |
|
uint8_t vbuf[AP_VarMaxSize]; |
|
size_t size; |
|
|
|
// serialize the variable into the buffer and work out how big it is |
|
size = serialize(vbuf, sizeof(vbuf)); |
|
|
|
// if it fit in the buffer, save it to EEPROM |
|
if (size <= sizeof(vbuf)) |
|
eeprom_write_block(vbuf, (void *)addr, size); |
|
} |
|
} |
|
|
|
// Load the variable from EEPROM, if supported |
|
// |
|
void |
|
AP_Var::load(void) |
|
{ |
|
AP_VarAddress addr = _get_address(); |
|
|
|
if (addr != AP_VarNoAddress) { |
|
uint8_t vbuf[AP_VarMaxSize]; |
|
size_t size; |
|
|
|
// ask the unserializer how big the variable is |
|
size = unserialize(NULL, 0); |
|
|
|
// read the buffer from EEPROM |
|
if (size <= sizeof(vbuf)) { |
|
eeprom_read_block(vbuf, (void *)addr, size); |
|
unserialize(vbuf, size); |
|
} |
|
} |
|
} |
|
|
|
|
|
// |
|
// Lookup interface for variables. |
|
// |
|
AP_Var *AP_Var::_variables = NULL; |
|
AP_Var *AP_Var::_lookupHint = NULL; |
|
int AP_Var::_lookupHintIndex = 0; |
|
|
|
AP_Var * |
|
AP_Var::lookup(int index) |
|
{ |
|
AP_Var *p; |
|
int i; |
|
|
|
// establish initial search state |
|
if (_lookupHintIndex && // we have a cached hint |
|
(index >= _lookupHintIndex)) { // the desired index is at or after the hint |
|
|
|
p = _lookupHint; // start at the hint point |
|
i = index - _lookupHintIndex; // count only the distance from the hint to the index |
|
} else { |
|
|
|
p = _variables; // start at the beginning of the list |
|
i = index; // count to the index |
|
} |
|
|
|
// search |
|
while (index-- && p) // count until we hit the index or the end of the list |
|
p = p->_link; |
|
|
|
// update the cache on hit |
|
if (p) { |
|
_lookupHintIndex = i; |
|
_lookupHint = p; |
|
} |
|
|
|
return(p); |
|
} |
|
|
|
// Save all variables that have an identity. |
|
// |
|
void |
|
AP_Var::save_all(void) |
|
{ |
|
AP_Var *p = _variables; |
|
|
|
while (p) { |
|
p->save(); |
|
p = p->_link; |
|
} |
|
} |
|
|
|
// Load all variables that have an identity. |
|
// |
|
void |
|
AP_Var::load_all(void) |
|
{ |
|
AP_Var *p = _variables; |
|
|
|
while (p) { |
|
p->load(); |
|
p = p->_link; |
|
} |
|
}
|
|
|