(root)/src/abe/fomescript.cpp - Rev 1217
Rev 1208 |
Rev 1218 |
Go to most recent revision |
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 "fomescript.h"
#include "forestmanagementengine.h"
#include "fmstp.h"
#include "agenttype.h"
#include "agent.h"
#include "fmtreelist.h"
#include "scheduler.h"
#include "fmstp.h"
#include "actplanting.h"
// iLand main includes
#include "species.h"
namespace ABE {
/** @class FomeScript
@ingroup abe
The FomeScript class is visible to Javascript via the 'fmengine' object. The main functions of ABE are available through this class.
*/
QString FomeScript::mInvalidContext = "S---";
ActivityFlags ActivityObj::mEmptyFlags;
FomeScript::FomeScript(QObject *parent) :
QObject(parent)
{
mStandObj = 0;
mUnitObj = 0;
mSimulationObj = 0;
mActivityObj = 0;
mSchedulerObj = 0;
mTrees = 0;
mStand = 0;
}
FomeScript::~FomeScript()
{
// all objects have script ownership - do not delete here.
// if (mStandObj) {
// delete mStandObj;
// delete mSiteObj;
// delete mSimulationObj;
// delete mTrees;
// }
}
void FomeScript::setupScriptEnvironment()
{
// create javascript objects in the script engine
// these objects can be accessed from Javascript code representing forest management activities
// or agents.
// stand variables
mStandObj = new StandObj;
QJSValue stand_value = ForestManagementEngine::scriptEngine()->newQObject(mStandObj);
ForestManagementEngine::scriptEngine()->globalObject().setProperty("stand", stand_value);
// site variables
mUnitObj = new UnitObj;
QJSValue site_value = ForestManagementEngine::scriptEngine()->newQObject(mUnitObj);
ForestManagementEngine::scriptEngine()->globalObject().setProperty("unit", site_value);
// general simulation variables (mainly scenariolevel)
mSimulationObj = new SimulationObj;
QJSValue simulation_value = ForestManagementEngine::scriptEngine()->newQObject(mSimulationObj);
ForestManagementEngine::scriptEngine()->globalObject().setProperty("simulation", simulation_value);
//access to the current activity
mActivityObj = new ActivityObj;
QJSValue activity_value = ForestManagementEngine::scriptEngine()->newQObject(mActivityObj);
ForestManagementEngine::scriptEngine()->globalObject().setProperty("activity", activity_value);
// general simulation variables (mainly scenariolevel)
mTrees = new FMTreeList;
QJSValue treelist_value = ForestManagementEngine::scriptEngine()->newQObject(mTrees);
ForestManagementEngine::scriptEngine()->globalObject().setProperty("trees", treelist_value);
// options of the STP
mSTPObj = new STPObj;
QJSValue stp_value = ForestManagementEngine::scriptEngine()->newQObject(mSTPObj);
ForestManagementEngine::scriptEngine()->globalObject().setProperty("stp", stp_value);
// scheduler options
mSchedulerObj = new SchedulerObj;
QJSValue scheduler_value = ForestManagementEngine::scriptEngine()->newQObject(mSchedulerObj);
ForestManagementEngine::scriptEngine()->globalObject().setProperty("scheduler", scheduler_value);
// the script object itself
QJSValue script_value = ForestManagementEngine::scriptEngine()->newQObject(this);
ForestManagementEngine::scriptEngine()->globalObject().setProperty("fmengine", script_value);
}
void FomeScript::setExecutionContext(FMStand *stand, bool add_agent)
{
FomeScript *br = bridge();
br->mStand = stand;
br->mStandObj->setStand(stand);
br->mTrees->setStand(stand);
br->mUnitObj->setStand(stand);
br->mActivityObj->setStand(stand);
br->mSchedulerObj->setStand(stand);
br->mSTPObj->setSTP(stand);
if (stand && stand->trace())
qCDebug(abe) << br->context() << "Prepared execution context (thread" << QThread::currentThread() << ").";
if (add_agent) {
const Agent *ag = stand->unit()->agent();
ForestManagementEngine::instance()->scriptEngine()->globalObject().setProperty("agent", ag->jsAgent());
}
}
void FomeScript::setActivity(Activity *act)
{
FomeScript *br = bridge();
setExecutionContext(0);
br->mActivityObj->setActivity(act);
}
FomeScript *FomeScript::bridge()
{
// get the right bridge object (for the right thread??)
return ForestManagementEngine::instance()->scriptBridge();
}
QString FomeScript::JStoString(QJSValue value)
{
if (value.isArray() || value.isObject()) {
QJSValue fun = ForestManagementEngine::scriptEngine()->evaluate("(function(a) { return JSON.stringify(a); })");
QJSValue result = fun.call(QJSValueList() << value);
return result.toString();
} else
return value.toString();
}
bool FomeScript::verbose() const
{
return FMSTP::verbose();
}
void FomeScript::setVerbose(bool arg)
{
FMSTP::setVerbose(arg);
qCDebug(abe) << "setting verbose property of ABE to" << arg;
}
int FomeScript::standId() const
{
if (mStand)
return mStand->id();
return -1;
}
void FomeScript::setStandId(int new_stand_id)
{
FMStand *stand = ForestManagementEngine::instance()->stand(new_stand_id);
if (!stand) {
qCDebug(abe) << bridge()->context() << "invalid stand id" << new_stand_id;
return;
}
setExecutionContext(stand);
}
void FomeScript::log(QJSValue value)
{
QString msg = JStoString(value);
qCDebug(abe) << bridge()->context() << msg;
}
void FomeScript::abort(QJSValue message)
{
log(message);
ForestManagementEngine::instance()->abortExecution(QString("%1: %2").arg(context()).arg(message.toString()));
}
bool FomeScript::addManagement(QJSValue program, QString name)
{
try {
FMSTP *stp = new FMSTP();
stp->setup(program, name);
ForestManagementEngine::instance()->addSTP(stp);
return true;
} catch (const IException &e) {
qCWarning(abe) << e.message();
ForestManagementEngine::instance()->abortExecution(QString("Error in adding management.\n%1").arg(e.message()));
return false;
}
}
bool FomeScript::updateManagement(QJSValue program, QString name)
{
try {
FMSTP *stp = ForestManagementEngine::instance()->stp(name);
if (!stp) {
qCWarning(abe) << "updateManagement: STP" << name << "not found. No program updated.";
return false;
}
stp->setup(program, name);
return true;
} catch (const IException &e) {
qCWarning(abe) << e.message();
ForestManagementEngine::instance()->abortExecution(QString("Error in updating management '%2':\n%1").arg(e.message(), name));
return false;
}
}
bool FomeScript::addManagementToAgentType(QString name, QString agentname)
{
FMSTP *stp = ForestManagementEngine::instance()->stp(name);
if (!stp) {
qCWarning(abe) << "addManagementToAgentType: STP" << name << "not found!";
return false;
}
AgentType *at = ForestManagementEngine::instance()->agentType(agentname);
if (!at) {
qCWarning(abe) << "addManagementToAgentType: agenttype" << agentname << "not found!";
return false;
}
at->addSTP(name);
}
bool FomeScript::addAgentType(QJSValue program, QString name)
{
try {
AgentType *at = new AgentType();
at->setupSTP(program, name);
ForestManagementEngine::instance()->addAgentType(at);
return true;
} catch (const IException &e) {
qCWarning(abe) << e.message();
ForestManagementEngine::instance()->abortExecution(QString("Error in adding agent type definition.\n%1").arg(e.message()));
return false;
}
}
QJSValue FomeScript::addAgent(QString agent_type, QString agent_name)
{
try {
// find the agent type
AgentType *at = ForestManagementEngine::instance()->agentType(agent_type);
if (!at) {
abort(QString("fmengine.addAgent: invalid 'agent_type': '%1'").arg(agent_type));
return QJSValue();
}
Agent *ag = at->createAgent(agent_name);
return ag->jsAgent();
} catch (const IException &e) {
qCWarning(abe) << e.message();
ForestManagementEngine::instance()->abortExecution(QString("Error in adding agent definition.\n%1").arg(e.message()));
return false;
}
}
/// force execution of an activity (outside of the usual execution context, e.g. for debugging)
bool FomeScript::runActivity(int stand_id, QString activity)
{
// find stand
FMStand *stand = ForestManagementEngine::instance()->stand(stand_id);
if (!stand)
return false;
if (!stand->stp())
return false;
Activity *act = stand->stp()->activity(activity);
if (!act)
return false;
// run the activity....
qCDebug(abe) << "running activity" << activity << "for stand" << stand_id;
return act->execute(stand);
}
bool FomeScript::runActivityEvaluate(int stand_id, QString activity)
{
// find stand
FMStand *stand = ForestManagementEngine::instance()->stand(stand_id);
if (!stand)
return false;
if (!stand->stp())
return false;
Activity *act = stand->stp()->activity(activity);
if (!act)
return false;
// run the activity....
qCDebug(abe) << "running evaluate of activity" << activity << "for stand" << stand_id;
return act->evaluate(stand);
}
bool FomeScript::runAgent(int stand_id, QString function)
{
// find stand
FMStand *stand = ForestManagementEngine::instance()->stand(stand_id);
if (!stand)
return false;
setExecutionContext(stand, true); // true: add also agent as 'agent'
QJSValue val;
QJSValue agent_type = stand->unit()->agent()->type()->jsObject();
if (agent_type.property(function).isCallable()) {
val = agent_type.property(function).callWithInstance(agent_type);
qCDebug(abe) << "running agent-function" << function << "for stand" << stand_id << ":" << val.toString();
} else {
qCDebug(abe) << "function" << function << "is not a valid function of agent-type" << stand->unit()->agent()->type()->name();
}
return true;
}
bool FomeScript::isValidStand(int stand_id)
{
FMStand *stand = ForestManagementEngine::instance()->stand(stand_id);
if (stand)
return true;
return false;
}
QStringList FomeScript::standIds()
{
return ForestManagementEngine::instance()->standIds();
}
QJSValue FomeScript::activity(QString stp_name, QString activity_name)
{
FMSTP *stp = ForestManagementEngine::instance()->stp(stp_name);
if (!stp) {
qCDebug(abe) << "fmengine.activty: invalid stp" << stp_name;
return QJSValue();
}
Activity *act = stp->activity(activity_name);
if (!act) {
qCDebug(abe) << "fmengine.activty: activity" << activity_name << "not found in stp:" << stp_name;
return QJSValue();
}
int idx = stp->activityIndex(act);
ActivityObj *ao = new ActivityObj(0, act, idx);
QJSValue value = ForestManagementEngine::scriptEngine()->newQObject(ao);
return value;
}
void FomeScript::runPlanting(int stand_id, QJSValue planting_item)
{
FMStand *stand = ForestManagementEngine::instance()->stand(stand_id);
if (!stand) {
qCWarning(abe) << "runPlanting: stand not found" << stand_id;
return;
}
ActPlanting::runSinglePlantingItem(stand, planting_item);
}
int FomeScript::levelIndex(const QString &level_label)
{
if (level_label=="low") return 1;
if (level_label=="medium") return 2;
if (level_label=="high") return 3;
return -1;
}
const QString FomeScript::levelLabel(const int level_index)
{
switch (level_index) {
case 1: return QStringLiteral("low");
case 2: return QStringLiteral("medium");
case 3: return QStringLiteral("high");
}
return QStringLiteral("invalid");
}
QString StandObj::speciesId(int index) const
{
if (index>=0 && index<nspecies()) return mStand->speciesData(index).species->id(); else return "error";
}
QJSValue StandObj::activity(QString name)
{
Activity *act = mStand->stp()->activity(name);
if (!act)
return QJSValue();
int idx = mStand->stp()->activityIndex(act);
ActivityObj *ao = new ActivityObj(mStand, act, idx);
QJSValue value = ForestManagementEngine::scriptEngine()->newQObject(ao);
return value;
}
QJSValue StandObj::agent()
{
if (mStand && mStand->unit()->agent())
return mStand->unit()->agent()->jsAgent();
else
throwError("get agent of the stand failed.");
return QJSValue();
}
void StandObj::setAbsoluteAge(double arg)
{
if (!mStand) {
throwError("set absolute age"); return; }
mStand->setAbsoluteAge(arg);
}
void StandObj::reset()
{
if (!mStand) {
throwError("reset"); return; }
mStand->initialize();
}
bool StandObj::trace() const
{
if (!mStand) { throwError("trace"); return false; }
return mStand->trace();
}
void StandObj::setTrace(bool do_trace)
{
if (!mStand) { throwError("trace"); }
mStand->setProperty("trace", QJSValue(do_trace));
}
int StandObj::timeSinceLastExecution() const
{
if (mStand)
return ForestManagementEngine::instance()->currentYear() - mStand->lastExecution();
throwError("timeSinceLastExecution");
return -1;
}
QString StandObj::lastActivity() const
{
if (mStand->lastExecutedActivity())
return mStand->lastExecutedActivity()->name();
return QString();
}
double StandObj::rotationLength() const
{
if (mStand)
return mStand->U();
throwError("U");
return -1.;
}
QString StandObj::speciesComposition() const
{
int index = mStand->targetSpeciesIndex();
return mStand->unit()->agent()->type()->speciesCompositionName(index);
}
QString StandObj::thinningIntensity() const
{
int t = mStand->thinningIntensity();
return FomeScript::levelLabel(t);
}
void StandObj::throwError(QString msg) const
{
FomeScript::bridge()->abort(QString("Error while accessing 'stand': no valid execution context. Message: %1").arg(msg));
}
/* ******************** */
bool ActivityObj::enabled() const
{
return flags().enabled();
}
QString ActivityObj::name() const
{
return mActivity? mActivity->name() : QStringLiteral("undefined");
}
void ActivityObj::setEnabled(bool do_enable)
{
flags().setEnabled(do_enable);
}
ActivityFlags &ActivityObj::flags() const
{
// refer to a specific activity of the stand (as returned by stand.activity("xxx") )
if (mStand && mActivityIndex>-1)
return mStand->flags(mActivityIndex);
// refer to the *current* activity (the "activity" variable)
if (mStand && !mActivity)
return mStand->currentFlags();
// during setup of activites (onCreate-handler)
if (!mStand && mActivity)
return mActivity->mBaseActivity;
qCDebug(abe) << "ActivityObj:flags: invalid access of flags! stand: " << mStand << "activity-index:" << mActivityIndex;
return mEmptyFlags;
}
bool UnitObj::agentUpdate(QString what, QString how, QString when)
{
AgentUpdate::UpdateType type = AgentUpdate::label(what);
if (type == AgentUpdate::UpdateInvalid)
qCDebug(abe) << "unit.agentUpdate: invalid 'what':" << what;
AgentUpdate update;
update.setType( type );
// how
int idx = FomeScript::levelIndex(how);
if (idx>-1)
update.setValue( QString::number(idx));
else
update.setValue( how );
// when
bool ok;
int age = when.toInt(&ok);
if (ok)
update.setTimeAge(age);
else
update.setTimeActivity(when);
mStand->unit()->agent()->type()->addAgentUpdate( update, const_cast<FMUnit*>(mStand->unit()) );
qCDebug(abe) << "Unit::agentUpdate:" << update.dump();
return true;
}
QString UnitObj::harvestMode() const
{
return mStand->unit()->harvestMode();
}
QString UnitObj::speciesComposition() const
{
int index = mStand->unit()->targetSpeciesIndex();
return mStand->unit()->agent()->type()->speciesCompositionName(index);
}
double UnitObj::U() const
{
return mStand->U();
}
QString UnitObj::thinningIntensity() const
{
int t = mStand->unit()->thinningIntensity();
return FomeScript::levelLabel(t);
}
double UnitObj::MAIChange() const
{
// todo
return mStand->unit()->annualIncrement();
}
double UnitObj::MAILevel() const
{
return mStand->unit()->averageMAI();
}
double UnitObj::landscapeMAI() const
{
// hacky way of getting a MAI on landscape level
double total_area = 0.;
double total_mai = 0.;
const QVector<FMUnit*> &units = ForestManagementEngine::instance()->units();
for (int i=0;i<units.size();++i) {
total_area += units[i]->area();
total_mai += units[i]->annualIncrement()*units[i]->area();
}
if (total_area>0.)
return total_mai / total_area;
else
return 0.;
}
double UnitObj::mortalityChange() const
{
return 1; // todo
}
double UnitObj::mortalityLevel() const
{
return 1; // todo
}
double UnitObj::regenerationChange() const
{
return 1; // todo
}
double UnitObj::regenerationLevel() const
{
return 1; // todo
}
void SchedulerObj::dump() const
{
if (!mStand || !mStand->unit() || !mStand->unit()->constScheduler())
return;
mStand->unit()->constScheduler()->dump();
}
bool SchedulerObj::enabled()
{
if (!mStand) return false;
const SchedulerOptions &opt = mStand->unit()->agent()->schedulerOptions();
return opt.useScheduler;
}
void SchedulerObj::setEnabled(bool is_enabled)
{
if (!mStand)
return;
SchedulerOptions &opt = const_cast<SchedulerOptions &>(mStand->unit()->agent()->schedulerOptions());
opt.useScheduler = is_enabled;
}
double SchedulerObj::harvestIntensity()
{
if (!mStand) return false;
const SchedulerOptions &opt = mStand->unit()->agent()->schedulerOptions();
return opt.harvestIntensity;
}
void SchedulerObj::setHarvestIntensity(double new_intensity)
{
if (!mStand)
return;
SchedulerOptions &opt = const_cast<SchedulerOptions &>(mStand->unit()->agent()->schedulerOptions());
opt.harvestIntensity = new_intensity;
}
double SchedulerObj::useSustainableHarvest()
{
if (!mStand) return false;
const SchedulerOptions &opt = mStand->unit()->agent()->schedulerOptions();
return opt.useSustainableHarvest;
}
void SchedulerObj::setUseSustainableHarvest(double new_level)
{
if (!mStand)
return;
SchedulerOptions &opt = const_cast<SchedulerOptions &>(mStand->unit()->agent()->schedulerOptions());
opt.useSustainableHarvest = new_level;
}
double SchedulerObj::maxHarvestLevel()
{
if (!mStand) return false;
const SchedulerOptions &opt = mStand->unit()->agent()->schedulerOptions();
return opt.maxHarvestLevel;
}
void SchedulerObj::setMaxHarvestLevel(double new_harvest_level)
{
if (!mStand)
return;
SchedulerOptions &opt = const_cast<SchedulerOptions &>(mStand->unit()->agent()->schedulerOptions());
opt.maxHarvestLevel = new_harvest_level;
}
void STPObj::setSTP(FMStand *stand)
{
if (stand && stand->stp()) {
mSTP = stand->stp();
mOptions = mSTP->JSoptions();
} else {
mOptions = QJSValue();
mSTP = 0;
}
}
QString STPObj::name()
{
if (mSTP)
return mSTP->name();
else
return "undefined";
}
} // namespace