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 | ********************************************************************************************/ |
||
19 | |||
867 | werner | 20 | #include "global.h" |
908 | werner | 21 | #include "abe_global.h" |
860 | werner | 22 | #include "fmstp.h" |
866 | werner | 23 | #include "fmstand.h" |
869 | werner | 24 | #include "fomescript.h" |
25 | #include "fomewrapper.h" |
||
26 | |||
27 | #include "expression.h" |
||
28 | |||
870 | werner | 29 | |
907 | werner | 30 | namespace ABE { |
1095 | werner | 31 | |
32 | /** @class FMSTP |
||
33 | @ingroup abe |
||
34 | The FMSTP class encapsulates a stand treatment program, which is defined in Javascript. |
||
35 | |||
36 | */ |
||
37 | |||
869 | werner | 38 | // static values |
39 | bool FMSTP::mVerbose = false; |
||
40 | |||
860 | werner | 41 | FMSTP::FMSTP() |
42 | { |
||
905 | werner | 43 | mSalvage = 0; |
942 | werner | 44 | mRotationLength[0] = 90.; // sensible defaults |
45 | mRotationLength[1] = 100.; |
||
46 | mRotationLength[2] = 110.; |
||
1208 | werner | 47 | mOptions=QJSValue(0); |
860 | werner | 48 | } |
866 | werner | 49 | |
870 | werner | 50 | FMSTP::~FMSTP() |
51 | { |
||
52 | clear(); |
||
53 | } |
||
54 | |||
884 | werner | 55 | Activity *FMSTP::activity(const QString &name) const |
56 | { |
||
57 | int idx = mActivityNames.indexOf(name); |
||
58 | if (idx==-1) |
||
59 | return 0; |
||
60 | return mActivities[idx]; |
||
61 | } |
||
62 | |||
871 | werner | 63 | bool activityScheduledEarlier(const Activity *a, const Activity *b) |
866 | werner | 64 | { |
871 | werner | 65 | return a->earlistSchedule() < b->earlistSchedule(); |
66 | } |
||
67 | |||
68 | void FMSTP::setup(QJSValue &js_value, const QString name) |
||
69 | { |
||
1207 | werner | 70 | clear(); |
71 | |||
870 | werner | 72 | if(!name.isEmpty()) |
73 | mName = name; |
||
869 | werner | 74 | |
871 | werner | 75 | // (1) scan recursively the data structure and create |
76 | // all activites |
||
77 | internalSetup(js_value, 0); |
||
78 | |||
79 | // (2) create all other required meta information (such as ActivityStand) |
||
80 | // sort activites based on the minimum execution time |
||
81 | std::sort(mActivities.begin(), mActivities.end(), activityScheduledEarlier); |
||
82 | |||
83 | mActivityNames.clear(); |
||
897 | werner | 84 | mHasRepeatingActivities = false; |
871 | werner | 85 | for (int i=0;i<mActivities.count();++i) { |
86 | mActivityNames.push_back(mActivities.at(i)->name()); |
||
911 | werner | 87 | mActivityStand.push_back(mActivities.at(i)->standFlags()); // stand = null: create a copy of the activities' base flags |
871 | werner | 88 | mActivities.at(i)->setIndex(i); |
901 | werner | 89 | if (mActivities.at(i)->isRepeatingActivity()) |
897 | werner | 90 | mHasRepeatingActivities = true; |
911 | werner | 91 | if (mActivities.at(i)->standFlags().isSalvage()) { |
905 | werner | 92 | mSalvage = dynamic_cast<ActSalvage*>(mActivities.at(i)); |
93 | mHasRepeatingActivities = false; |
||
94 | } |
||
871 | werner | 95 | } |
875 | werner | 96 | |
97 | // (3) set up top-level events |
||
887 | werner | 98 | mEvents.setup(js_value, QStringList() << QStringLiteral("onInit") << QStringLiteral("onExit")); |
871 | werner | 99 | } |
100 | |||
897 | werner | 101 | bool FMSTP::executeRepeatingActivities(FMStand *stand) |
102 | { |
||
914 | werner | 103 | if (mSalvage) |
104 | if (stand->totalHarvest() || stand->property("_run_salvage").toBool()) { |
||
905 | werner | 105 | // at this point totalHarvest is only disturbance related harvests. |
106 | stand->executeActivity(mSalvage); |
||
107 | } |
||
897 | werner | 108 | if (!mHasRepeatingActivities) |
109 | return false; |
||
110 | bool result = false; |
||
111 | for (int i=0;i<mActivities.count();++i) |
||
112 | if (mActivities.at(i)->schedule().repeat) { |
||
113 | if (!stand->flags(i).active() || !stand->flags(i).enabled()) |
||
114 | continue; |
||
115 | if (stand->trace()) |
||
909 | werner | 116 | qCDebug(abe) << "running repeating activity" << mActivities.at(i)->name(); |
897 | werner | 117 | result |= stand->executeActivity(mActivities[i]); |
118 | } |
||
119 | return result; // return true if at least one repeating activity was executed. |
||
120 | |||
121 | } |
||
122 | |||
944 | werner | 123 | void FMSTP::evaluateDynamicExpressions(FMStand *stand) |
124 | { |
||
125 | foreach(Activity *act, mActivities) |
||
126 | act->evaluateDyanamicExpressions(stand); |
||
127 | } |
||
128 | |||
871 | werner | 129 | // read the setting from the setup-javascript object |
963 | werner | 130 | void FMSTP::internalSetup(const QJSValue &js_value, int level) |
871 | werner | 131 | { |
132 | |||
870 | werner | 133 | // top-level |
134 | if (js_value.hasOwnProperty("schedule")) { |
||
871 | werner | 135 | setupActivity(js_value, "unnamed"); |
870 | werner | 136 | return; |
137 | } |
||
869 | werner | 138 | |
870 | werner | 139 | // nested objects |
140 | if (js_value.isObject()) { |
||
141 | QJSValueIterator it(js_value); |
||
142 | while (it.hasNext()) { |
||
143 | it.next(); |
||
942 | werner | 144 | // parse special properties |
145 | if (it.name()=="U" && it.value().isArray()) { |
||
146 | QVariantList list = it.value().toVariant().toList(); |
||
147 | if (list.length()!=3) |
||
148 | throw IException("STP: the 'U'-property needs to be an array with three elements!"); |
||
149 | for (int i=0;i<list.length();++i) |
||
150 | mRotationLength[i] = list.at(i).toInt(); |
||
151 | continue; |
||
152 | } |
||
1061 | werner | 153 | if (it.name()=="options") { |
154 | mOptions = it.value(); |
||
155 | continue; |
||
156 | } |
||
910 | werner | 157 | if (it.value().hasOwnProperty("type")) { |
158 | // try to set up as activity |
||
871 | werner | 159 | setupActivity(it.value(), it.name()); |
870 | werner | 160 | } else if (it.value().isObject() && !it.value().isCallable()) { |
161 | // try to go one level deeper |
||
162 | if (FMSTP::verbose()) |
||
909 | werner | 163 | qCDebug(abeSetup) << "entering" << it.name(); |
870 | werner | 164 | if (level<10) |
871 | werner | 165 | internalSetup(it.value(), ++level); |
901 | werner | 166 | else |
167 | throw IException("setup of STP: too many nested levels (>=10) - check your syntax!"); |
||
870 | werner | 168 | } |
169 | } |
||
170 | } else { |
||
909 | werner | 171 | qCDebug(abeSetup) << "FMSTP::setup: not a valid javascript object."; |
870 | werner | 172 | } |
866 | werner | 173 | } |
174 | |||
867 | werner | 175 | |
870 | werner | 176 | void FMSTP::dumpInfo() |
867 | werner | 177 | { |
909 | werner | 178 | if (!abe().isDebugEnabled()) |
884 | werner | 179 | return; |
909 | werner | 180 | qCDebug(abe) << " ***************************************"; |
181 | qCDebug(abe) << " **************** Program dump for:" << name(); |
||
182 | qCDebug(abe) << " ***************************************"; |
||
870 | werner | 183 | foreach(Activity *act, mActivities) { |
909 | werner | 184 | qCDebug(abe) << "******* Activity *********"; |
871 | werner | 185 | QString info = act->info().join('\n'); |
909 | werner | 186 | qCDebug(abe) << info; |
871 | werner | 187 | |
867 | werner | 188 | } |
189 | } |
||
190 | |||
963 | werner | 191 | void FMSTP::setupActivity(const QJSValue &js_value, const QString &name) |
867 | werner | 192 | { |
870 | werner | 193 | QString type = js_value.property("type").toString(); |
194 | if (verbose()) |
||
909 | werner | 195 | qCDebug(abeSetup) << "setting up activity of type" << type << "from JS"; |
891 | werner | 196 | Activity *act = Activity::createActivity(type, this); |
197 | if (!act) return; // actually, an error is thrown in the previous call. |
||
869 | werner | 198 | |
951 | werner | 199 | // use id-property if available, or the object-name otherwise |
200 | act->setName(valueFromJs(js_value, "id", name).toString()); |
||
870 | werner | 201 | // call the setup routine (overloaded version) |
202 | act->setup(js_value); |
||
893 | werner | 203 | |
204 | // call the onCreate handler: |
||
205 | FomeScript::bridge()->setActivity(act); |
||
206 | act->events().run(QStringLiteral("onCreate"),0); |
||
870 | werner | 207 | mActivities.push_back(act); |
869 | werner | 208 | } |
209 | |||
870 | werner | 210 | void FMSTP::clear() |
869 | werner | 211 | { |
870 | werner | 212 | qDeleteAll(mActivities); |
213 | mActivities.clear(); |
||
1207 | werner | 214 | mEvents.clear(); |
215 | mActivityStand.clear(); |
||
216 | mActivityNames.clear(); |
||
217 | mSalvage = 0; |
||
1208 | werner | 218 | mOptions=QJSValue(0); // clear |
1207 | werner | 219 | mName.clear(); |
869 | werner | 220 | } |
221 | |||
870 | werner | 222 | QJSValue FMSTP::valueFromJs(const QJSValue &js_value, const QString &key, const QString default_value, const QString &errorMessage) |
869 | werner | 223 | { |
870 | werner | 224 | if (!js_value.hasOwnProperty(key)) { |
225 | if (!errorMessage.isEmpty()) |
||
1063 | werner | 226 | throw IException(QString("Error: required key '%1' not found. In: %2 (JS: %3)").arg(key).arg(errorMessage).arg(FomeScript::JStoString(js_value))); |
888 | werner | 227 | else if (default_value.isEmpty()) |
228 | return QJSValue(); |
||
870 | werner | 229 | else |
230 | return default_value; |
||
231 | } |
||
232 | return js_value.property(key); |
||
869 | werner | 233 | } |
234 | |||
871 | werner | 235 | bool FMSTP::boolValueFromJs(const QJSValue &js_value, const QString &key, const bool default_bool_value, const QString &errorMessage) |
236 | { |
||
237 | if (!js_value.hasOwnProperty(key)) { |
||
238 | if (!errorMessage.isEmpty()) |
||
1063 | werner | 239 | throw IException(QString("Error: required key '%1' not found. In: %2 (JS: %3)").arg(key).arg(errorMessage).arg(FomeScript::JStoString(js_value))); |
871 | werner | 240 | else |
241 | return default_bool_value; |
||
242 | } |
||
243 | return js_value.property(key).toBool(); |
||
244 | |||
245 | } |
||
246 | |||
951 | werner | 247 | bool FMSTP::checkObjectProperties(const QJSValue &js_value, const QStringList &allowed_properties, const QString &errorMessage) |
248 | { |
||
249 | QJSValueIterator it(js_value); |
||
250 | bool found_issues = false; |
||
251 | while (it.hasNext()) { |
||
252 | it.next(); |
||
253 | if (!allowed_properties.contains(it.name()) && it.name()!=QLatin1String("length")) { |
||
254 | qCDebug(abe) << "Syntax-warning: The javascript property" << it.name() << "is not used! In:" << errorMessage; |
||
255 | found_issues = true; |
||
256 | } |
||
257 | } |
||
258 | return !found_issues; |
||
259 | } |
||
260 | |||
870 | werner | 261 | } // namespace |