add timer support so we can specify when a value should change

This commit is contained in:
Justin Hammond 2014-08-08 15:53:13 +08:00
parent 4b14919d4c
commit 0d621f4fa8
8 changed files with 130 additions and 10 deletions

View file

@ -43,11 +43,13 @@ namespace fl {
virtual std::vector<Proposition*> conclusions() const;
virtual void load(const std::string& consequent, const Engine* engine);
virtual void load(const std::string& consequent, const Engine* engine, int timer = 0);
virtual void modify(scalar strength, const TNorm* activation);
virtual std::string toString() const;
private:
int m_timer;
};
}

View file

@ -54,6 +54,7 @@ namespace fl {
Proposition();
std::string toString() const;
int timer;
};

View file

@ -71,6 +71,8 @@ namespace fl {
static std::string FL_AND;
static std::string FL_OR;
static std::string FL_WITH;
static std::string FL_SET;
static std::string FL_IN;
static std::string ifKeyword();
static std::string isKeyword();

View file

@ -39,7 +39,8 @@ namespace fl {
scalar _lastValidOutput;
bool _lockOutputRange;
bool _lockValidOutput;
int timer;
public:
OutputVariable(const std::string& name = "",
scalar minimum = -fl::inf, scalar maximum = fl::inf);
@ -69,6 +70,9 @@ namespace fl {
virtual scalar defuzzifyNoLocks() const;
virtual std::string toString() const;
virtual int getTimer() const;
virtual void setTimer(int time);
};

View file

@ -208,7 +208,66 @@ int main(int argc, char** argv) {
// fuzzylite::setDecimals(8);
// exportAllExamples("fis", "fld");
// return 0;
return Console::main(argc, argv);
// return Console::main(argc, argv);
fl::Engine* engine = new fl::Engine("simple-dimmer");
fl::InputVariable* ambient = new fl::InputVariable;
ambient->setName("Ambient");
ambient->setRange(0.000, 1.000);
ambient->addTerm(new fl::Triangle("DARK", 0.000, 0.500));
ambient->addTerm(new fl::Triangle("MEDIUM", 0.250, 0.750));
ambient->addTerm(new fl::Triangle("BRIGHT", 0.500, 1.000));
engine->addInputVariable(ambient);
fl::OutputVariable* power = new fl::OutputVariable;
power->setName("Power");
power->setRange(0.000, 2.000);
power->setDefaultValue(fl::nan);
power->addTerm(new fl::Triangle("LOW", 0.000, 1.000));
power->addTerm(new fl::Triangle("MEDIUM", 0.500, 1.500));
power->addTerm(new fl::Triangle("HIGH", 1.000, 2.000));
engine->addOutputVariable(power);
fl::OutputVariable* power1 = new fl::OutputVariable;
power1->setName("Power1");
power1->setRange(0.000, 2.000);
power1->setDefaultValue(fl::nan);
power1->addTerm(new fl::Triangle("LOW", 0.000, 1.000));
power1->addTerm(new fl::Triangle("MEDIUM", 0.500, 1.500));
power1->addTerm(new fl::Triangle("HIGH", 1.000, 2.000));
engine->addOutputVariable(power1);
fl::RuleBlock* ruleblock = new fl::RuleBlock;
ruleblock->addRule(fl::Rule::parse("if Ambient is DARK then Power is HIGH", engine));
ruleblock->addRule(fl::Rule::parse("if Ambient is MEDIUM then Power is MEDIUM", engine));
ruleblock->addRule(fl::Rule::parse("if Ambient is BRIGHT then in 5m set Power is LOW", engine));
ruleblock->addRule(fl::Rule::parse("if Ambient is BRIGHT then in 533m set Power is LOW and Power1 is HIGH", engine));
//# ruleblock->addRule(fl::Rule::parse("if Ambient is BRIGHT then in 5m Power is LOW", engine));
engine->addRuleBlock(ruleblock);
//No Conjunction or Disjunction is needed
engine->configure("", "", "AlgebraicProduct", "AlgebraicSum", "Centroid");
std::string status;
if (not engine->isReady(&status))
throw fl::Exception("Engine not ready. "
"The following errors were encountered:\n" + status, FL_AT);
for (int i = 0; i < 50; ++i){
fl::scalar light = ambient->getMinimum() + i * (ambient->range() / 50);
ambient->setInputValue(light);
engine->process();
FL_LOG("Ambient.input = " << fl::Op::str(light) << " -> " <<
"Power.output = " << fl::Op::str(power->defuzzify()) <<
" Timer: " << power->getTimer() <<
" Power1.output = " << fl::Op::str(power1->defuzzify()) <<
" Timer: " << power1->getTimer());
}
std::cout << engine->toString() << std::endl;
} catch (fl::Exception& e) {
FL_LOG(e.what());
FL_LOG(e.btCallStack());

View file

@ -70,12 +70,13 @@ namespace fl {
term->setThreshold(threshold);
term->setActivation(activation);
OutputVariable* outputVariable = dynamic_cast<OutputVariable*> (proposition->variable);
outputVariable->setTimer(m_timer);
outputVariable->fuzzyOutput()->addTerm(term);
FL_DBG("Accumulating " << term->toString());
}
}
void Consequent::load(const std::string& consequent, const Engine* engine) {
void Consequent::load(const std::string& consequent, const Engine* engine, int timer) {
/**
Extracts the list of propositions from the consequent
@ -94,7 +95,7 @@ namespace fl {
int state = S_VARIABLE;
_conclusions.clear();
m_timer = timer;
Proposition* proposition = NULL;
std::stringstream tokenizer(consequent);
@ -103,6 +104,7 @@ namespace fl {
if (state bitand S_VARIABLE) {
if (engine->hasOutputVariable(token)) {
proposition = new Proposition;
proposition->timer = timer;
proposition->variable = engine->getOutputVariable(token);
_conclusions.push_back(proposition);

View file

@ -42,6 +42,8 @@ namespace fl {
std::string Rule::FL_AND = "and";
std::string Rule::FL_OR = "or";
std::string Rule::FL_WITH = "with";
std::string Rule::FL_IN = "in";
std::string Rule::FL_SET = "set";
Rule::Rule()
: _weight(1.0), _antecedent(NULL), _consequent(NULL) {
@ -136,15 +138,15 @@ namespace fl {
result->setText(rule);
std::istringstream tokenizer(rule);
std::string token;
std::ostringstream ossAntecedent, ossConsequent;
std::ostringstream ossAntecedent, ossConsequent, ossTimer;
int timer = 0;
enum FSM {
S_NONE, S_IF, S_THEN, S_WITH, S_END
S_NONE, S_IF, S_THEN, S_TIME, S_WITH, S_END
};
FSM state = S_NONE;
try {
while (tokenizer >> token) {
switch (state) {
case S_NONE:
if (token == Rule::FL_IF) state = S_IF;
@ -161,8 +163,13 @@ namespace fl {
break;
case S_THEN:
if (token == Rule::FL_WITH) state = S_WITH;
else if (token == Rule::FL_IN) state = S_TIME;
else ossConsequent << token << " ";
break;
case S_TIME:
if (token == Rule::FL_SET) state = S_THEN;
else ossTimer << token << " ";
break;
case S_WITH:
try {
result->setWeight(fl::Op::toScalar(token));
@ -193,13 +200,44 @@ namespace fl {
std::ostringstream ex;
ex << "[syntax error] expected a numeric value as the weight of the rule: " << rule;
throw fl::Exception(ex.str(), FL_AT);
} else if (state == S_TIME) {
std::ostringstream ex;
ex << "[syntax error] expected a timername/value in the rule: " << rule;
throw fl::Exception(ex.str(), FL_AT);
}
/* parse the Timer Value if it exists */
if (ossTimer.str().length() > 0) {
token = ossTimer.str();
int multiplier = 0;
if (isdigit(token[0])) {
std::stringstream(token) >> timer;
}
for (std::string::iterator it = token.begin(); it != token.end(); ++it) {
switch (*it) {
case 's':
multiplier = 0;
break;
case 'm':
multiplier = 60;
break;
case 'h':
multiplier = 3600;
break;
case 'd':
multiplier = 86400;
break;
}
}
timer = timer * multiplier;
}
result->_antecedent = new Antecedent;
result->_antecedent->load(ossAntecedent.str(), engine);
result->_consequent = new Consequent;
result->_consequent->load(ossConsequent.str(), engine);
result->_consequent->load(ossConsequent.str(), engine, timer);
} catch (fl::Exception& ex) {
delete result;
throw ex;

View file

@ -37,7 +37,8 @@ namespace fl {
_defuzzifier(NULL), _defaultValue(fl::nan),
_lastValidOutput(fl::nan),
_lockOutputRange(false),
_lockValidOutput(false) {
_lockValidOutput(false),
timer(0) {
}
OutputVariable::~OutputVariable() {
@ -98,6 +99,17 @@ namespace fl {
bool OutputVariable::isLockingValidOutput() const {
return this->_lockValidOutput;
}
void OutputVariable::setTimer(int time) {
if (time == 0)
return;
if ((this->timer == 0) || (this->timer > time)) {
this->timer = time;
}
}
int OutputVariable::getTimer() const {
return this->timer;
}
scalar OutputVariable::defuzzify() {
scalar result = fl::nan;