Rev 1221 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/********************************************************************************************
** iLand - an individual based forest landscape and disturbance model
** http://iland.boku.ac.at
** Copyright (C) 2009- Werner Rammer, Rupert Seidl
**
** This program 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.
**
** This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
********************************************************************************************/
#include "global.h"
#include "abe_global.h"
#include "fmstp.h"
#include "fmstand.h"
#include "fomescript.h"
#include "fomewrapper.h"
#include "expression.h"
namespace ABE {
/** @class FMSTP
@ingroup abe
The FMSTP class encapsulates a stand treatment program, which is defined in Javascript.
*/
// static values
bool FMSTP::mVerbose = false;
FMSTP::FMSTP()
{
mSalvage = 0;
mRotationLength[0] = 90.; // sensible defaults
mRotationLength[1] = 100.;
mRotationLength[2] = 110.;
mOptions=QJSValue(0);
}
FMSTP::~FMSTP()
{
clear();
}
Activity *FMSTP::activity(const QString &name) const
{
int idx = mActivityNames.indexOf(name);
if (idx==-1)
return 0;
return mActivities[idx];
}
bool activityScheduledEarlier(const Activity *a, const Activity *b)
{
return a->earlistSchedule() < b->earlistSchedule();
}
void FMSTP::setup(QJSValue &js_value, const QString name)
{
clear();
if(!name.isEmpty())
mName = name;
// (1) scan recursively the data structure and create
// all activites
internalSetup(js_value, 0);
// (2) create all other required meta information (such as ActivityStand)
// sort activites based on the minimum execution time
std::sort(mActivities.begin(), mActivities.end(), activityScheduledEarlier);
mActivityNames.clear();
mHasRepeatingActivities = false;
for (int i=0;i<mActivities.count();++i) {
mActivityNames.push_back(mActivities.at(i)->name());
mActivityStand.push_back(mActivities.at(i)->standFlags()); // stand = null: create a copy of the activities' base flags
mActivities.at(i)->setIndex(i);
if (mActivities.at(i)->isRepeatingActivity())
mHasRepeatingActivities = true;
if (mActivities.at(i)->standFlags().isSalvage()) {
mSalvage = dynamic_cast<ActSalvage*>(mActivities.at(i));
mHasRepeatingActivities = false;
}
}
// (3) set up top-level events
mEvents.setup(js_value, QStringList() << QStringLiteral("onInit") << QStringLiteral("onExit"));
}
bool FMSTP::executeRepeatingActivities(FMStand *stand)
{
if (mSalvage)
if (stand->totalHarvest() || stand->property("_run_salvage").toBool()) {
// at this point totalHarvest is only disturbance related harvests.
stand->executeActivity(mSalvage);
}
if (!mHasRepeatingActivities)
return false;
bool result = false;
for (int i=0;i<mActivities.count();++i)
if (mActivities.at(i)->schedule().repeat) {
if (!stand->flags(i).active() || !stand->flags(i).enabled())
continue;
if (stand->trace())
qCDebug(abe) << "running repeating activity" << mActivities.at(i)->name();
result |= stand->executeActivity(mActivities[i]);
}
return result; // return true if at least one repeating activity was executed.
}
void FMSTP::evaluateDynamicExpressions(FMStand *stand)
{
foreach(Activity *act, mActivities)
act->evaluateDyanamicExpressions(stand);
}
// read the setting from the setup-javascript object
void FMSTP::internalSetup(const QJSValue &js_value, int level)
{
// top-level
if (js_value.hasOwnProperty("schedule")) {
setupActivity(js_value, "unnamed");
return;
}
// nested objects
if (js_value.isObject()) {
QJSValueIterator it(js_value);
while (it.hasNext()) {
it.next();
// parse special properties
if (it.name()=="U" && it.value().isArray()) {
QVariantList list = it.value().toVariant().toList();
if (list.length()!=3)
throw IException("STP: the 'U'-property needs to be an array with three elements!");
for (int i=0;i<list.length();++i)
mRotationLength[i] = list.at(i).toInt();
continue;
}
if (it.name()=="options") {
mOptions = it.value();
continue;
}
if (it.value().hasOwnProperty("type")) {
// try to set up as activity
setupActivity(it.value(), it.name());
} else if (it.value().isObject() && !it.value().isCallable()) {
// try to go one level deeper
if (FMSTP::verbose())
qCDebug(abeSetup) << "entering" << it.name();
if (level<10)
internalSetup(it.value(), ++level);
else
throw IException("setup of STP: too many nested levels (>=10) - check your syntax!");
}
}
} else {
qCDebug(abeSetup) << "FMSTP::setup: not a valid javascript object.";
}
}
void FMSTP::dumpInfo()
{
if (!abe().isDebugEnabled())
return;
qCDebug(abe) << " ***************************************";
qCDebug(abe) << " **************** Program dump for:" << name();
qCDebug(abe) << " ***************************************";
foreach(Activity *act, mActivities) {
qCDebug(abe) << "******* Activity *********";
QString info = act->info().join('\n');
qCDebug(abe) << info;
}
}
void FMSTP::setupActivity(const QJSValue &js_value, const QString &name)
{
QString type = js_value.property("type").toString();
if (verbose())
qCDebug(abeSetup) << "setting up activity of type" << type << "from JS";
Activity *act = Activity::createActivity(type, this);
if (!act) return; // actually, an error is thrown in the previous call.
// use id-property if available, or the object-name otherwise
act->setName(valueFromJs(js_value, "id", name).toString());
// call the setup routine (overloaded version)
act->setup(js_value);
// call the onCreate handler:
FomeScript::bridge()->setActivity(act);
act->events().run(QStringLiteral("onCreate"),0);
mActivities.push_back(act);
}
void FMSTP::clear()
{
qDeleteAll(mActivities);
mActivities.clear();
mEvents.clear();
mActivityStand.clear();
mActivityNames.clear();
mSalvage = 0;
mOptions=QJSValue(0); // clear
mName.clear();
}
QJSValue FMSTP::valueFromJs(const QJSValue &js_value, const QString &key, const QString default_value, const QString &errorMessage)
{
if (!js_value.hasOwnProperty(key)) {
if (!errorMessage.isEmpty())
throw IException(QString("Error: required key '%1' not found. In: %2 (JS: %3)").arg(key).arg(errorMessage).arg(FomeScript::JStoString(js_value)));
else if (default_value.isEmpty())
return QJSValue();
else
return default_value;
}
return js_value.property(key);
}
bool FMSTP::boolValueFromJs(const QJSValue &js_value, const QString &key, const bool default_bool_value, const QString &errorMessage)
{
if (!js_value.hasOwnProperty(key)) {
if (!errorMessage.isEmpty())
throw IException(QString("Error: required key '%1' not found. In: %2 (JS: %3)").arg(key).arg(errorMessage).arg(FomeScript::JStoString(js_value)));
else
return default_bool_value;
}
return js_value.property(key).toBool();
}
bool FMSTP::checkObjectProperties(const QJSValue &js_value, const QStringList &allowed_properties, const QString &errorMessage)
{
QJSValueIterator it(js_value);
bool found_issues = false;
while (it.hasNext()) {
it.next();
if (!allowed_properties.contains(it.name()) && it.name()!=QLatin1String("length")) {
qCDebug(abe) << "Syntax-warning: The javascript property" << it.name() << "is not used! In:" << errorMessage;
found_issues = true;
}
}
return !found_issues;
}
} // namespace