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 | ********************************************************************************************/ |
||
873 | werner | 19 | #include "global.h" |
908 | werner | 20 | #include "abe_global.h" |
811 | werner | 21 | #include "agenttype.h" |
938 | werner | 22 | #include "agent.h" |
873 | werner | 23 | #include "fmstp.h" |
24 | #include "forestmanagementengine.h" |
||
25 | #include "fmunit.h" |
||
26 | #include "fmstand.h" |
||
874 | werner | 27 | #include "fomescript.h" |
811 | werner | 28 | |
873 | werner | 29 | #include <QJSEngine> |
907 | werner | 30 | namespace ABE { |
870 | werner | 31 | |
1095 | werner | 32 | /** @class AgentType |
33 | @ingroup abe |
||
34 | AgentType implements an abstract agent type (e.g., farmer or forest company). The class defines basic behavior of |
||
35 | agents. |
||
870 | werner | 36 | |
1095 | werner | 37 | */ |
38 | |||
811 | werner | 39 | AgentType::AgentType() |
40 | { |
||
41 | } |
||
870 | werner | 42 | |
876 | werner | 43 | void AgentType::setupSTP(QJSValue agent_code, const QString agent_name) |
873 | werner | 44 | { |
45 | mName = agent_name; |
||
46 | mSTP.clear(); |
||
876 | werner | 47 | mJSObj = agent_code; |
48 | if (!agent_code.isObject()) |
||
909 | werner | 49 | throw IException(QString("ABE:AgentType:setup: the javascript object for agent '%1' could not be found.").arg(agent_name)); |
876 | werner | 50 | QJSValue stps = agent_code.property("stp"); |
873 | werner | 51 | if (!stps.isObject()) |
909 | werner | 52 | throw IException(QString("ABE:AgentType:setup: the javascript definition of agent '%1' does not have a section for 'stp'.").arg(agent_name)); |
873 | werner | 53 | QJSValueIterator it(stps); |
54 | while (it.hasNext()) { |
||
55 | it.next(); |
||
56 | FMSTP *stp = ForestManagementEngine::instance()->stp(it.value().toString()); |
||
57 | if (!stp) |
||
909 | werner | 58 | throw IException(QString("ABE:AgentType:setup: definition of agent '%1': the STP for mixture type '%2': '%3' is not available.").arg(agent_name).arg(it.name()).arg(it.value().toString())); |
873 | werner | 59 | mSTP[it.name()] = stp; |
60 | } |
||
874 | werner | 61 | |
873 | werner | 62 | if (FMSTP::verbose()) |
939 | werner | 63 | qCDebug(abeSetup) << "setup of agent" << agent_name << mSTP.size() << "links to STPs established."; |
870 | werner | 64 | |
904 | werner | 65 | |
873 | werner | 66 | } |
67 | |||
1208 | werner | 68 | void AgentType::addSTP(QString stp_name) |
69 | { |
||
70 | FMSTP *stp = ForestManagementEngine::instance()->stp(stp_name); |
||
71 | if (!stp) |
||
72 | throw IException(QString("AgentType::addSTP: definition of agent '%1': the STP '%2' is not available.").arg(mName).arg(stp_name)); |
||
73 | mSTP[stp_name] = stp; |
||
873 | werner | 74 | |
1208 | werner | 75 | } |
76 | |||
77 | |||
938 | werner | 78 | Agent *AgentType::createAgent(QString agent_name) |
79 | { |
||
80 | // call the newAgent function in the javascript object assigned to this agent type |
||
81 | QJSValue func = mJSObj.property("newAgent"); |
||
82 | if (!func.isCallable()) |
||
83 | throw IException(QString("The agent type '%1' does not have a valid 'newAgent' function.").arg(name())); |
||
84 | QJSValue result = func.callWithInstance(mJSObj); |
||
85 | if (result.isError()) |
||
86 | throw IException(QString("calling the 'newAgent' function of agent type '%1' returned with the following error: %2").arg(name()).arg(result.toString())); |
||
87 | Agent *agent = new Agent(this, result); |
||
88 | if (!agent_name.isEmpty()) { |
||
89 | agent->setName(agent_name); |
||
90 | } else { |
||
91 | if (result.property("name").isUndefined()) |
||
92 | result.setProperty("name", agent->name()); // set the auto-generated name also for the JS world |
||
93 | else |
||
94 | agent->setName(result.property("name").toString()); // set the JS-name also internally |
||
95 | } |
||
96 | ForestManagementEngine::instance()->addAgent(agent); |
||
97 | |||
98 | return agent; |
||
99 | |||
100 | } |
||
101 | |||
958 | werner | 102 | void AgentType::addAgentUpdate(const AgentUpdate &update, FMUnit *unit) |
942 | werner | 103 | { |
104 | |||
944 | werner | 105 | // clear agent updates... |
106 | QMultiHash<const FMUnit*, AgentUpdate>::iterator hi= mAgentChanges.begin(); |
||
107 | while (hi != mAgentChanges.end()) { |
||
108 | if (!hi.value().isValid()) |
||
958 | werner | 109 | hi = mAgentChanges.erase(hi); |
944 | werner | 110 | else |
111 | ++hi; |
||
112 | } |
||
113 | |||
114 | |||
115 | AgentUpdate &rUpdate = mAgentChanges.insertMulti(unit, update).value(); |
||
116 | rUpdate.setCounter( unit->numberOfStands() ); |
||
117 | |||
958 | werner | 118 | // set default unit value |
119 | switch (update.type()) { |
||
120 | case AgentUpdate::UpdateU: unit->setU( update.value().toInt() ); break; |
||
121 | case AgentUpdate::UpdateThinning: unit->setThinningIntensity(update.value().toInt()); break; |
||
122 | case AgentUpdate::UpdateSpecies: break; |
||
1032 | werner | 123 | default: break; |
958 | werner | 124 | } |
125 | |||
942 | werner | 126 | if (update.age()==-1) |
127 | return; |
||
128 | |||
944 | werner | 129 | // check stands that should be updated immediateley |
942 | werner | 130 | const QMultiMap<FMUnit*, FMStand*> &stands = ForestManagementEngine::instance()->stands(); |
131 | QMultiMap<FMUnit*, FMStand*>::const_iterator it = stands.constFind(const_cast<FMUnit*>(unit)); |
||
132 | while (it != stands.constEnd() && it.key()==unit) { |
||
133 | FMStand *stand = it.value(); |
||
973 | werner | 134 | if (stand->trace()) |
135 | qCDebug(abe) << stand->context() << "Agent-update: update if stand-age: " << stand->age() << " < update-age: " << update.age(); |
||
944 | werner | 136 | if (stand->age() <= update.age()) |
973 | werner | 137 | agentUpdateForStand(stand, QString(), stand->age()); |
944 | werner | 138 | ++it; |
942 | werner | 139 | } |
140 | |||
141 | } |
||
142 | |||
944 | werner | 143 | bool AgentType::agentUpdateForStand(FMStand *stand, QString after_activity, int age) |
942 | werner | 144 | { |
145 | // |
||
146 | QMultiHash<const FMUnit*, AgentUpdate>::iterator uit = mAgentChanges.find(stand->unit()); |
||
147 | bool action = false; |
||
148 | while (uit != mAgentChanges.end() && uit.key()==stand->unit()) { |
||
149 | AgentUpdate &update = uit.value(); |
||
150 | |||
958 | werner | 151 | if (!update.isValid()) { |
152 | ++uit; |
||
944 | werner | 153 | continue; |
958 | werner | 154 | } |
942 | werner | 155 | // timing of update |
156 | if (!after_activity.isEmpty() && update.afterActivity()==after_activity) { |
||
157 | // do something |
||
158 | action = true; |
||
159 | } |
||
958 | werner | 160 | if (update.age()>-1 && age<update.age()) { |
942 | werner | 161 | // do something |
162 | action = true; |
||
163 | } |
||
164 | |||
165 | // update the stand |
||
166 | if (action) { |
||
944 | werner | 167 | update.decrease(); |
942 | werner | 168 | switch (update.type()) { |
169 | case AgentUpdate::UpdateU: { |
||
958 | werner | 170 | int current_u = stand->U(); // stand->stp()->rotationLengthType(stand->U()); |
942 | werner | 171 | int new_u = update.value().toInt(); |
172 | if (current_u == new_u) { |
||
173 | if (stand->trace()) |
||
174 | qCDebug(abe) << stand->context() << "AgentUpdate: update of U to" << new_u << " not done (value already set)."; |
||
175 | break; |
||
176 | } |
||
958 | werner | 177 | stand->setU( new_u ); |
178 | // stand->setU( stand->stp()->rotationLengthOfType(new_u) ); |
||
942 | werner | 179 | qCDebug(abe) << stand->context() << "AgentUpdate: changed to U" << stand->U(); |
944 | werner | 180 | // QML like dynamic expressions |
181 | stand->stp()->evaluateDynamicExpressions(stand); |
||
942 | werner | 182 | break; |
183 | } |
||
184 | case AgentUpdate::UpdateThinning: { |
||
185 | int current_th = stand->thinningIntensity(); |
||
186 | int new_th = update.value().toInt(); |
||
187 | if (current_th == new_th) { |
||
188 | if (stand->trace()) |
||
189 | qCDebug(abe) << stand->context() << "AgentUpdate: update of thinningIntensity class to" << new_th << " not done (value already set)."; |
||
190 | break; |
||
191 | } |
||
192 | stand->setThinningIntensity(new_th ); |
||
193 | qCDebug(abe) << stand->context() << "AgentUpdate: changed to thinningIntensity class:" << stand->thinningIntensity(); |
||
944 | werner | 194 | stand->stp()->evaluateDynamicExpressions(stand); |
942 | werner | 195 | break; |
196 | } |
||
1032 | werner | 197 | default: break; // TODO: UpdateSpecies??? |
944 | werner | 198 | |
942 | werner | 199 | } |
200 | } |
||
944 | werner | 201 | |
202 | ++uit; |
||
942 | werner | 203 | } |
204 | return action; |
||
205 | } |
||
206 | |||
890 | werner | 207 | FMSTP *AgentType::stpByName(const QString &name) |
208 | { |
||
209 | if (mSTP.contains(name)) |
||
210 | return mSTP[name]; |
||
211 | else |
||
212 | return 0; |
||
213 | } |
||
873 | werner | 214 | |
940 | werner | 215 | int AgentType::speciesCompositionIndex(const QString &key) |
216 | { |
||
217 | for (int i=0;i<mSpeciesCompositions.size(); ++i) |
||
218 | if (mSpeciesCompositions[i] == key) |
||
219 | return i; |
||
220 | return -1; |
||
221 | } |
||
890 | werner | 222 | |
940 | werner | 223 | QString AgentType::speciesCompositionName(const int index) |
224 | { |
||
225 | if (index>=0 && index < mSpeciesCompositions.count()) |
||
226 | return mSpeciesCompositions[index]; |
||
227 | return QString(); |
||
228 | } |
||
229 | |||
942 | werner | 230 | AgentUpdate::UpdateType AgentUpdate::label(const QString &name) |
231 | { |
||
232 | if (name=="U") return UpdateU; |
||
233 | if (name=="thinningIntensity") return UpdateThinning; |
||
234 | if (name=="species") return UpdateSpecies; |
||
235 | return UpdateInvalid; |
||
236 | } |
||
940 | werner | 237 | |
944 | werner | 238 | QString AgentUpdate::dump() |
239 | { |
||
240 | QString line; |
||
241 | switch (type()) { |
||
242 | case UpdateU: line= QString("AgentUpdate: update U to '%1'.").arg(mValue); break; |
||
243 | case UpdateThinning: line= QString("AgentUpdate: update thinning interval to '%1'.").arg(mValue); break; |
||
244 | case UpdateSpecies: line= QString("AgentUpdate: update species composition to '%1'.").arg(mValue); break; |
||
1032 | werner | 245 | default: break; |
944 | werner | 246 | } |
247 | if (!mAfterActivity.isEmpty()) |
||
248 | return line + QString("Update after activity '%1'.").arg(mAfterActivity); |
||
249 | return line + QString("Update (before) age '%1'.").arg(mAge); |
||
250 | } |
||
942 | werner | 251 | |
944 | werner | 252 | |
870 | werner | 253 | } // namespace |