Rev 1221 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1033 | werner | 1 | /******************************************************************************************** |
2 | ** iLand - an individual based forest landscape and disturbance model |
||
3 | ** http://iland.boku.ac.at |
||
4 | ** Copyright (C) 2009- Werner Rammer, Rupert Seidl |
||
5 | ** |
||
6 | ** This program is free software: you can redistribute it and/or modify |
||
7 | ** it under the terms of the GNU General Public License as published by |
||
8 | ** the Free Software Foundation, either version 3 of the License, or |
||
9 | ** (at your option) any later version. |
||
10 | ** |
||
11 | ** This program is distributed in the hope that it will be useful, |
||
12 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
14 | ** GNU General Public License for more details. |
||
15 | ** |
||
16 | ** You should have received a copy of the GNU General Public License |
||
17 | ** along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
18 | ********************************************************************************************/ |
||
908 | werner | 19 | #include "abe_global.h" |
808 | werner | 20 | #include <QJSValueIterator> |
807 | werner | 21 | |
22 | #include "activity.h" |
||
811 | werner | 23 | #include "fmstand.h" |
870 | werner | 24 | #include "fmstp.h" |
25 | #include "fomescript.h" |
||
26 | #include "fomewrapper.h" |
||
875 | werner | 27 | #include "forestmanagementengine.h" |
811 | werner | 28 | |
870 | werner | 29 | #include "expression.h" |
901 | werner | 30 | #include "debugtimer.h" |
870 | werner | 31 | |
901 | werner | 32 | |
891 | werner | 33 | // include derived activity types |
34 | #include "actgeneral.h" |
||
35 | #include "actscheduled.h" |
||
902 | werner | 36 | #include "actplanting.h" |
923 | werner | 37 | #include "actthinning.h" |
884 | werner | 38 | |
907 | werner | 39 | namespace ABE { |
870 | werner | 40 | |
1095 | werner | 41 | /** @class Activity |
42 | @ingroup abe |
||
43 | An activity is the basic silvicultural building block; it holds state information and defines basic capabilities |
||
44 | of all activities (such as having a given Schedule, or Events). |
||
45 | |||
46 | */ |
||
47 | |||
951 | werner | 48 | // statics |
49 | QStringList Activity::mAllowedProperties = QStringList() << "schedule" << "constraint" << "type"; |
||
50 | |||
870 | werner | 51 | /***************************************************************************/ |
52 | /*************************** Schedule ***********************************/ |
||
53 | /***************************************************************************/ |
||
54 | |||
55 | |||
963 | werner | 56 | void Schedule::setup(const QJSValue &js_value) |
870 | werner | 57 | { |
58 | clear(); |
||
59 | if (js_value.isObject()) { |
||
60 | tmin = FMSTP::valueFromJs(js_value, "min", "-1").toInt(); |
||
61 | tmax = FMSTP::valueFromJs(js_value, "max", "-1").toInt(); |
||
62 | topt = FMSTP::valueFromJs(js_value, "opt", "-1").toInt(); |
||
63 | tminrel = FMSTP::valueFromJs(js_value, "minRel", "-1").toNumber(); |
||
64 | tmaxrel = FMSTP::valueFromJs(js_value, "maxRel", "-1").toNumber(); |
||
65 | toptrel = FMSTP::valueFromJs(js_value, "optRel", "-1").toNumber(); |
||
871 | werner | 66 | repeat_interval = FMSTP::valueFromJs(js_value, "repeatInterval", "1").toInt(); |
67 | // switches |
||
68 | force_execution = FMSTP::boolValueFromJs(js_value, "force", false); |
||
69 | repeat = FMSTP::boolValueFromJs(js_value, "repeat", false); |
||
872 | werner | 70 | absolute = FMSTP::boolValueFromJs(js_value, "absolute", false); |
871 | werner | 71 | if (!repeat) { |
1063 | werner | 72 | |
871 | werner | 73 | if (tmin>-1 && tmax>-1 && topt==-1) |
74 | topt = (tmax+tmin) / 2; |
||
75 | if (tmin>-1 && tmax>-1 && topt>-1 && (topt<tmin || topt>tmax)) |
||
1063 | werner | 76 | throw IException(QString("Error in setting up schedule: 'opt' either missing or out of range: %1").arg(FomeScript::JStoString(js_value))); |
871 | werner | 77 | if (tminrel>-1 && tmaxrel>-1 && toptrel>-1 && (toptrel<tminrel || toptrel>tmaxrel)) |
1063 | werner | 78 | throw IException(QString("Error in setting up schedule: 'opt' either missing or out of range: %1").arg(FomeScript::JStoString(js_value))); |
871 | werner | 79 | if (tminrel*tmaxrel < 0. || tmin*tmax<0.) |
1063 | werner | 80 | throw IException(QString("Error in setting up schedule: min and max required: %1").arg(FomeScript::JStoString(js_value))); |
870 | werner | 81 | |
871 | werner | 82 | if (topt==-1 && toptrel==-1.) |
1063 | werner | 83 | throw IException(QString("Error in setting up schedule: neither 'opt' nor 'optRel' point can be derived in: %1").arg(FomeScript::JStoString(js_value))); |
871 | werner | 84 | } |
85 | |||
870 | werner | 86 | } else if (js_value.isNumber()) { |
87 | topt = js_value.toNumber(); |
||
88 | } else { |
||
1063 | werner | 89 | throw IException(QString("Error in setting up schedule/timing. Invalid javascript object: %1").arg(FomeScript::JStoString(js_value))); |
870 | werner | 90 | } |
91 | } |
||
92 | |||
93 | QString Schedule::dump() const |
||
94 | { |
||
871 | werner | 95 | if (repeat) |
896 | werner | 96 | return QString("schedule: Repeating every %1 years.").arg(repeat_interval); |
871 | werner | 97 | else |
902 | werner | 98 | return QString("schedule: tmin/topt/tmax %1/%2/%3\nrelative: min/opt/max %4/%5/%6\nforce: %7").arg(tmin).arg(topt).arg(tmax) |
871 | werner | 99 | .arg(tminrel).arg(toptrel).arg(tmaxrel).arg(force_execution); |
870 | werner | 100 | } |
101 | |||
955 | werner | 102 | double Schedule::value(const FMStand *stand, const int specific_year) |
870 | werner | 103 | { |
942 | werner | 104 | double U = stand->U(); |
897 | werner | 105 | double current; |
106 | double current_year = ForestManagementEngine::instance()->currentYear(); |
||
955 | werner | 107 | if (specific_year>=0) |
108 | current_year = specific_year; |
||
902 | werner | 109 | // absolute age: years since the start of the rotation |
110 | current = stand->absoluteAge(); |
||
897 | werner | 111 | |
872 | werner | 112 | if (absolute) |
897 | werner | 113 | current = current_year; |
885 | werner | 114 | |
115 | double current_rel = current / U; |
||
897 | werner | 116 | if (repeat) { |
117 | // handle special case of repeating activities. |
||
118 | // we execute the repeating activity if repeatInterval years elapsed |
||
119 | // since the last execution. |
||
120 | if (int(current_year) % repeat_interval == 0) |
||
121 | return 1.; // yes, execute this year |
||
122 | else |
||
123 | return 0.; // do not execute this year. |
||
124 | |||
125 | } |
||
870 | werner | 126 | // force execution: if age already higher than max, then always evaluate to 1. |
903 | werner | 127 | if (tmax>-1. && current >= tmax && force_execution) |
870 | werner | 128 | return 1; |
903 | werner | 129 | if (tmaxrel>-1. && current_rel >= tmaxrel && force_execution) |
870 | werner | 130 | return 1; |
131 | |||
872 | werner | 132 | if (tmin>-1. && current < tmin) return 0.; |
897 | werner | 133 | if (tmax>-1. && current > tmax) return -1.; // expired |
885 | werner | 134 | if (tminrel>-1. && current_rel < tminrel) return 0.; |
897 | werner | 135 | if (tmaxrel>-1. && current_rel > tmaxrel) return -1.; // expired |
870 | werner | 136 | |
885 | werner | 137 | // optimal time |
138 | if (topt > -1. && fabs(current-topt) <= 0.5) |
||
139 | return 1; |
||
902 | werner | 140 | if (topt > -1. && current > topt) { |
141 | if (force_execution) |
||
142 | return 1.; |
||
143 | else |
||
144 | return -1.; // expired |
||
145 | } |
||
885 | werner | 146 | |
870 | werner | 147 | if (tmin>-1. && tmax > -1.) { |
148 | if (topt > -1.) { |
||
885 | werner | 149 | // linear interpolation |
872 | werner | 150 | if (current<=topt) |
151 | return topt==tmin?1.:(current-tmin)/(topt-tmin); |
||
892 | werner | 152 | if (force_execution) |
153 | return 1.; // keep the high probability. |
||
870 | werner | 154 | else |
892 | werner | 155 | return topt==tmax?1.:(tmax-current)/(tmax-topt); // decreasing probabilitiy again |
870 | werner | 156 | } else { |
157 | return 1.; // no optimal time: everything between min and max is fine! |
||
158 | } |
||
159 | } |
||
887 | werner | 160 | // there is an optimal absoulte point in time defined, but not reached |
161 | if (topt > -1) |
||
162 | return 0.; |
||
163 | |||
885 | werner | 164 | // optimal time |
165 | if (toptrel>-1. && fabs(current_rel-toptrel)*U <= 0.5) |
||
166 | return 1.; |
||
167 | |||
168 | // min/max relative time |
||
870 | werner | 169 | if (tminrel>-1. && tmaxrel>-1.) { |
170 | if (toptrel > -1.) { |
||
885 | werner | 171 | // linear interpolation |
172 | if (current_rel<=toptrel) |
||
173 | return toptrel==tminrel?1.:(current_rel-tminrel)/(toptrel-tminrel); |
||
870 | werner | 174 | else |
885 | werner | 175 | return toptrel==tmaxrel?1.:(tmaxrel-current_rel)/(tmaxrel-toptrel); |
870 | werner | 176 | } else { |
177 | return 1.; // no optimal time: everything between min and max is fine! |
||
178 | } |
||
179 | } |
||
887 | werner | 180 | // there is an optimal relative point in time defined, but not reached yet. |
181 | if (toptrel>-1.) |
||
182 | return 0.; |
||
183 | |||
909 | werner | 184 | qCDebug(abe) << "Schedule::value: unexpected combination. U" << U << "age" << current << ", schedule:" << this->dump(); |
870 | werner | 185 | return 0.; |
186 | } |
||
187 | |||
875 | werner | 188 | double Schedule::minValue(const double U) const |
871 | werner | 189 | { |
955 | werner | 190 | if (absolute) return tmin; |
901 | werner | 191 | if (repeat) return 100000000.; |
871 | werner | 192 | if (tmin>-1) return tmin; |
875 | werner | 193 | if (tminrel>-1.) return tminrel * U; // assume a fixed U of 100yrs |
871 | werner | 194 | if (repeat) return -1.; // repeating executions are treated specially |
195 | if (topt>-1) return topt; |
||
875 | werner | 196 | return toptrel * U; |
871 | werner | 197 | } |
198 | |||
875 | werner | 199 | double Schedule::maxValue(const double U) const |
200 | { |
||
955 | werner | 201 | if (absolute) return tmax; |
875 | werner | 202 | if (tmax>-1) return tmax; |
203 | if (tmaxrel>-1.) return tmaxrel * U; // assume a fixed U of 100yrs |
||
204 | if (repeat) return -1.; // repeating executions are treated specially |
||
205 | if (topt>-1) return topt; |
||
206 | return toptrel * U; |
||
207 | |||
208 | } |
||
209 | |||
910 | werner | 210 | double Schedule::optimalValue(const double U) const |
211 | { |
||
212 | if (topt>-1) return topt; |
||
213 | if (toptrel>-1) return toptrel*U; |
||
214 | if (tmin>-1 && tmax>-1) return (tmax+tmin)/2.; |
||
215 | if (tminrel>-1 && tmaxrel>-1) return (tmaxrel+tminrel)/2. * U; |
||
216 | if (force_execution) return tmax; |
||
217 | return toptrel*U; |
||
218 | } |
||
219 | |||
870 | werner | 220 | /***************************************************************************/ |
221 | /************************** Events ************************************/ |
||
222 | /***************************************************************************/ |
||
223 | |||
224 | void Events::clear() |
||
225 | { |
||
226 | mEvents.clear(); |
||
227 | } |
||
228 | |||
229 | void Events::setup(QJSValue &js_value, QStringList event_names) |
||
230 | { |
||
887 | werner | 231 | mInstance = js_value; // save the object that contains the events |
870 | werner | 232 | foreach (QString event, event_names) { |
888 | werner | 233 | QJSValue val = FMSTP::valueFromJs(js_value, event); |
876 | werner | 234 | if (val.isCallable()) { |
887 | werner | 235 | mEvents[event] = js_value; // save the event functions (and the name of the property that the function is assigned to) |
876 | werner | 236 | } |
870 | werner | 237 | } |
238 | } |
||
239 | |||
1070 | werner | 240 | QJSValue Events::run(const QString event, FMStand *stand, QJSValueList *params) |
870 | werner | 241 | { |
242 | if (mEvents.contains(event)) { |
||
893 | werner | 243 | if (stand) |
244 | FomeScript::setExecutionContext(stand); |
||
876 | werner | 245 | QJSValue func = mEvents[event].property(event); |
246 | QJSValue result; |
||
887 | werner | 247 | if (func.isCallable()) { |
909 | werner | 248 | DebugTimer t("ABE:JSEvents:run"); |
901 | werner | 249 | |
1070 | werner | 250 | if (params) |
251 | result = func.callWithInstance(mInstance, *params); |
||
252 | else |
||
253 | result = func.callWithInstance(mInstance); |
||
1032 | werner | 254 | if (FMSTP::verbose() || (stand && stand->trace())) |
909 | werner | 255 | qCDebug(abe) << (stand?stand->context():QString("<no stand>")) << "invoking javascript event" << event << " result: " << result.toString(); |
887 | werner | 256 | } |
876 | werner | 257 | |
258 | //qDebug() << "event called:" << event << "result:" << result.toString(); |
||
870 | werner | 259 | if (result.isError()) { |
893 | werner | 260 | throw IException(QString("%3 Javascript error in event %1: %2").arg(event).arg(result.toString()).arg(stand?stand->context():"----")); |
870 | werner | 261 | } |
1061 | werner | 262 | return result; |
870 | werner | 263 | } |
1061 | werner | 264 | return QJSValue(); |
870 | werner | 265 | } |
266 | |||
891 | werner | 267 | bool Events::hasEvent(const QString &event) const |
268 | { |
||
269 | return mEvents.contains(event); |
||
270 | } |
||
271 | |||
870 | werner | 272 | QString Events::dump() |
273 | { |
||
274 | QString event_list = "Registered events: "; |
||
275 | foreach (QString event, mEvents.keys()) |
||
276 | event_list.append(event).append(" "); |
||
277 | return event_list; |
||
278 | } |
||
279 | |||
280 | /***************************************************************************/ |
||
281 | /************************* Constraints ***********************************/ |
||
282 | /***************************************************************************/ |
||
283 | |||
284 | void Constraints::setup(QJSValue &js_value) |
||
285 | { |
||
286 | mConstraints.clear(); |
||
287 | if ((js_value.isArray() || js_value.isObject()) && !js_value.isCallable()) { |
||
288 | QJSValueIterator it(js_value); |
||
289 | while (it.hasNext()) { |
||
290 | it.next(); |
||
902 | werner | 291 | if (it.name()==QStringLiteral("length")) |
292 | continue; |
||
944 | werner | 293 | mConstraints.append(DynamicExpression()); |
294 | DynamicExpression &item = mConstraints.last(); |
||
870 | werner | 295 | item.setup(it.value()); |
296 | } |
||
297 | } else { |
||
944 | werner | 298 | mConstraints.append(DynamicExpression()); |
299 | DynamicExpression &item = mConstraints.last(); |
||
870 | werner | 300 | item.setup(js_value); |
301 | |||
302 | } |
||
303 | } |
||
304 | |||
889 | werner | 305 | double Constraints::evaluate(FMStand *stand) |
870 | werner | 306 | { |
307 | if (mConstraints.isEmpty()) |
||
889 | werner | 308 | return 1.; // no constraints to evaluate |
309 | double p; |
||
310 | double p_min = 1; |
||
311 | for (int i=0;i<mConstraints.count();++i) { |
||
312 | p = mConstraints.at(i).evaluate(stand); |
||
313 | if (p == 0.) { |
||
887 | werner | 314 | if (stand->trace()) |
909 | werner | 315 | qCDebug(abe) << stand->context() << "constraint" << mConstraints.at(i).dump() << "did not pass."; |
889 | werner | 316 | return 0.; // one constraint failed |
317 | } else { |
||
318 | // save the lowest value... |
||
319 | p_min = std::min(p, p_min); |
||
887 | werner | 320 | } |
889 | werner | 321 | } |
322 | return p_min; // all constraints passed, return the lowest returned value... |
||
870 | werner | 323 | } |
324 | |||
325 | QStringList Constraints::dump() |
||
326 | { |
||
327 | QStringList info; |
||
328 | for (int i=0;i<mConstraints.count();++i){ |
||
896 | werner | 329 | info << QString("constraint: %1").arg(mConstraints[i].dump()); |
870 | werner | 330 | } |
331 | return info; |
||
332 | } |
||
333 | |||
334 | |||
944 | werner | 335 | DynamicExpression::~DynamicExpression() |
870 | werner | 336 | { |
337 | if (expr) |
||
338 | delete expr; |
||
339 | } |
||
340 | |||
963 | werner | 341 | void DynamicExpression::setup(const QJSValue &js_value) |
870 | werner | 342 | { |
343 | filter_type = ftInvalid; |
||
344 | if (expr) delete expr; |
||
345 | expr=0; |
||
346 | |||
347 | if (js_value.isCallable()) { |
||
348 | func = js_value; |
||
349 | filter_type = ftJavascript; |
||
350 | return; |
||
351 | } |
||
352 | if (js_value.isString()) { |
||
353 | // we assume this is an expression |
||
354 | |||
355 | QString exprstr = js_value.toString(); |
||
356 | // replace "." with "__" in variables (our Expression engine is |
||
357 | // not able to cope with the "."-notation |
||
358 | exprstr = exprstr.replace("activity.", "activity__"); |
||
359 | exprstr = exprstr.replace("stand.", "stand__"); |
||
360 | exprstr = exprstr.replace("site.", "site__"); |
||
944 | werner | 361 | exprstr = exprstr.replace("unit.", "unit__"); |
870 | werner | 362 | // add .... |
363 | expr = new Expression(exprstr); |
||
364 | filter_type = ftExpression; |
||
365 | return; |
||
366 | |||
367 | } |
||
368 | } |
||
369 | |||
944 | werner | 370 | bool DynamicExpression::evaluate(FMStand *stand) const |
870 | werner | 371 | { |
372 | switch (filter_type) { |
||
373 | case ftInvalid: return true; // message? |
||
374 | case ftExpression: { |
||
375 | FOMEWrapper wrapper(stand); |
||
376 | double result; |
||
377 | try { |
||
929 | werner | 378 | result = expr->execute(0, &wrapper); // using execute, we're in strict mode, i.e. wrong variables are reported. |
379 | //result = expr->calculate(wrapper); |
||
870 | werner | 380 | } catch (IException &e) { |
381 | // throw a nicely formatted error message |
||
382 | e.add(QString("in filter (expr: '%2') for stand %1."). |
||
383 | arg(stand->id()). |
||
384 | arg(expr->expression()) ); |
||
385 | throw; |
||
386 | } |
||
387 | |||
388 | if (FMSTP::verbose()) |
||
909 | werner | 389 | qCDebug(abe) << stand->context() << "evaluate constraint (expr:" << expr->expression() << ") for stand" << stand->id() << ":" << result; |
870 | werner | 390 | return result > 0.; |
391 | |||
392 | } |
||
393 | case ftJavascript: { |
||
394 | // call javascript function |
||
395 | // provide the execution context |
||
396 | FomeScript::setExecutionContext(stand); |
||
397 | QJSValue result = const_cast<QJSValue&>(func).call(); |
||
398 | if (result.isError()) { |
||
399 | throw IException(QString("Erron in evaluating constraint (JS) for stand %1: %2"). |
||
400 | arg(stand->id()). |
||
401 | arg(result.toString())); |
||
402 | } |
||
403 | if (FMSTP::verbose()) |
||
909 | werner | 404 | qCDebug(abe) << "evaluate constraint (JS) for stand" << stand->id() << ":" << result.toString(); |
934 | werner | 405 | // convert boolean result to 1 - 0 |
406 | if (result.isBool()) |
||
407 | return result.toBool()==true?1.:0; |
||
408 | else |
||
409 | return result.toNumber(); |
||
870 | werner | 410 | } |
411 | |||
412 | } |
||
413 | return true; |
||
414 | } |
||
415 | |||
944 | werner | 416 | QString DynamicExpression::dump() const |
870 | werner | 417 | { |
418 | switch (filter_type){ |
||
419 | case ftInvalid: return "Invalid"; |
||
420 | case ftExpression: return expr->expression(); |
||
421 | case ftJavascript: return func.toString(); |
||
422 | default: return "invalid filter type!"; |
||
423 | } |
||
424 | } |
||
425 | |||
426 | |||
427 | |||
428 | /***************************************************************************/ |
||
429 | /*************************** Activity ************************************/ |
||
430 | /***************************************************************************/ |
||
431 | |||
432 | Activity::Activity(const FMSTP *parent) |
||
433 | { |
||
434 | mProgram = parent; |
||
871 | werner | 435 | mIndex = 0; |
875 | werner | 436 | mBaseActivity = ActivityFlags(this); |
878 | werner | 437 | mBaseActivity.setActive(true); |
438 | mBaseActivity.setEnabled(true); |
||
870 | werner | 439 | } |
440 | |||
441 | Activity::~Activity() |
||
442 | { |
||
443 | |||
444 | } |
||
445 | |||
891 | werner | 446 | Activity *Activity::createActivity(const QString &type, FMSTP *stp) |
447 | { |
||
448 | Activity *act = 0; |
||
449 | |||
450 | if (type=="general") |
||
451 | act = new ActGeneral(stp); |
||
452 | |||
453 | if (type=="scheduled") |
||
454 | act = new ActScheduled(stp); |
||
455 | |||
902 | werner | 456 | if (type=="planting") |
457 | act = new ActPlanting(stp); |
||
458 | |||
905 | werner | 459 | if (type=="salvage") |
460 | act = new ActSalvage(stp); |
||
461 | |||
923 | werner | 462 | if (type=="thinning") |
463 | act = new ActThinning(stp); |
||
464 | |||
891 | werner | 465 | if (!act) { |
466 | throw IException(QString("Error: the activity type '%1' is not a valid type.").arg(type)); |
||
467 | } |
||
468 | |||
469 | return act; |
||
470 | } |
||
471 | |||
871 | werner | 472 | QString Activity::type() const |
870 | werner | 473 | { |
474 | return "base"; |
||
475 | } |
||
476 | |||
477 | void Activity::setup(QJSValue value) |
||
478 | { |
||
479 | mSchedule.setup(FMSTP::valueFromJs(value, "schedule", "", "setup activity")); |
||
890 | werner | 480 | if (FMSTP::verbose()) |
909 | werner | 481 | qCDebug(abeSetup) << mSchedule.dump(); |
870 | werner | 482 | |
483 | // setup of events |
||
484 | mEvents.clear(); |
||
914 | werner | 485 | mEvents.setup(value, QStringList() << "onCreate" << "onSetup" << "onEnter" << "onExit" << "onExecute" << "onExecuted" << "onCancel"); |
890 | werner | 486 | if (FMSTP::verbose()) |
909 | werner | 487 | qCDebug(abeSetup) << "Events: " << mEvents.dump(); |
870 | werner | 488 | |
489 | // setup of constraints |
||
888 | werner | 490 | QJSValue constraints = FMSTP::valueFromJs(value, "constraint"); |
870 | werner | 491 | if (!constraints.isUndefined()) |
492 | mConstraints.setup(constraints); |
||
493 | |||
944 | werner | 494 | // enabledIf property |
495 | QJSValue enabled_if = FMSTP::valueFromJs(value, "enabledIf"); |
||
496 | if (!enabled_if.isUndefined()) |
||
497 | mEnabledIf.setup(enabled_if); |
||
870 | werner | 498 | } |
499 | |||
955 | werner | 500 | double Activity::scheduleProbability(FMStand *stand, const int specific_year) |
875 | werner | 501 | { |
897 | werner | 502 | // return a value between 0 and 1; return -1 if the activity is expired. |
955 | werner | 503 | return schedule().value(stand, specific_year); |
875 | werner | 504 | } |
505 | |||
889 | werner | 506 | double Activity::execeuteProbability(FMStand *stand) |
875 | werner | 507 | { |
508 | // check the standard constraints and return true when all constraints are fulfilled (or no constraints set) |
||
509 | return constraints().evaluate(stand); |
||
510 | } |
||
511 | |||
870 | werner | 512 | bool Activity::execute(FMStand *stand) |
513 | { |
||
514 | // execute the "onExecute" event |
||
887 | werner | 515 | events().run(QStringLiteral("onExecute"), stand); |
870 | werner | 516 | return true; |
517 | } |
||
518 | |||
891 | werner | 519 | bool Activity::evaluate(FMStand *stand) |
520 | { |
||
904 | werner | 521 | // execute the "onEvaluate" event: the execution is canceled, if the function returns false. |
1061 | werner | 522 | bool cancel = events().run(QStringLiteral("onEvaluate"), stand).toBool(); |
904 | werner | 523 | return !cancel; |
891 | werner | 524 | } |
525 | |||
944 | werner | 526 | void Activity::evaluateDyanamicExpressions(FMStand *stand) |
527 | { |
||
528 | // evaluate the enabled-if property and set the enabled flag of the stand (i.e. the ActivityFlags) |
||
529 | if (mEnabledIf.isValid()) { |
||
530 | bool result = mEnabledIf.evaluate(stand); |
||
531 | stand->flags(mIndex).setEnabled(result); |
||
532 | } |
||
533 | } |
||
534 | |||
870 | werner | 535 | QStringList Activity::info() |
536 | { |
||
537 | QStringList lines; |
||
896 | werner | 538 | lines << QString("Activity '%1': type '%2'").arg(name()).arg(type()); |
539 | lines << "Events" << "-" << events().dump() << "/-"; |
||
540 | lines << "Schedule" << "-" << schedule().dump() << "/-"; |
||
541 | lines << "Constraints" << "-" << constraints().dump() << "/-"; |
||
870 | werner | 542 | return lines; |
543 | } |
||
544 | |||
545 | |||
871 | werner | 546 | ActivityFlags &Activity::standFlags(FMStand *stand) |
547 | { |
||
548 | // use the base data item if no specific stand is provided |
||
549 | if (!stand) |
||
550 | return mBaseActivity; |
||
551 | |||
552 | // return the flags associated with the specific stand |
||
553 | return stand->flags(mIndex); |
||
554 | } |
||
555 | |||
556 | |||
870 | werner | 557 | } // namespace |
558 |