forecast.cpp
Go to the documentation of this file.00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/modules/forecast/forecast.cpp $ 00003 version : $LastChangedRevision: 829 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2008-11-09 16:51:17 +0100 (Sun, 09 Nov 2008) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007 by Johan De Taeye * 00010 * * 00011 * This library is free software; you can redistribute it and/or modify it * 00012 * under the terms of the GNU Lesser General Public License as published * 00013 * by the Free Software Foundation; either version 2.1 of the License, or * 00014 * (at your option) any later version. * 00015 * * 00016 * This library is distributed in the hope that it will be useful, * 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * 00019 * General Public License for more details. * 00020 * * 00021 * You should have received a copy of the GNU Lesser General Public * 00022 * License along with this library; if not, write to the Free Software * 00023 * Foundation Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA * 00024 * * 00025 ***************************************************************************/ 00026 00027 00028 #include "forecast.h" 00029 00030 namespace module_forecast 00031 { 00032 00033 const Keyword Forecast::tag_total("total"); 00034 const Keyword Forecast::tag_net("net"); 00035 const Keyword Forecast::tag_consumed("consumed"); 00036 00037 00038 bool Forecast::callback(Calendar* l, const Signal a) 00039 { 00040 // This function is called when a calendar is about to be deleted. 00041 // If that calendar is being used for a forecast we reset the calendar 00042 // pointer to null. 00043 for (MapOfForecasts::iterator x = ForecastDictionary.begin(); 00044 x != ForecastDictionary.end(); ++x) 00045 if (x->second->calptr == l) 00046 // Calendar in use for this forecast 00047 x->second->calptr = NULL; 00048 return true; 00049 } 00050 00051 00052 Forecast::~Forecast() 00053 { 00054 // Update the dictionary 00055 for (MapOfForecasts::iterator x= 00056 ForecastDictionary.lower_bound(make_pair(&*getItem(),&*getCustomer())); 00057 x != ForecastDictionary.end(); ++x) 00058 if (x->second == this) 00059 { 00060 ForecastDictionary.erase(x); 00061 return; 00062 } 00063 00064 // Delete all children demands 00065 for(memberIterator i = beginMember(); i != endMember(); i = beginMember()) 00066 delete &*i; 00067 } 00068 00069 00070 void Forecast::initialize() 00071 { 00072 if (!calptr) throw DataException("Missing forecast calendar"); 00073 00074 // Create a demand for every bucket. The weight value depends on the 00075 // calendar type: double, integer, bool or other 00076 const CalendarDouble* c = dynamic_cast<const CalendarDouble*>(calptr); 00077 ForecastBucket* prev = NULL; 00078 Date prevDate; 00079 double prevValue(0.0); 00080 if (c) 00081 // Double calendar 00082 for (CalendarDouble::EventIterator i(c); i.getDate()<=Date::infiniteFuture; ++i) 00083 { 00084 if ((prevDate || i.getDate() == Date::infiniteFuture) && prevValue > 0.0) 00085 { 00086 prev = new ForecastBucket 00087 (this, prevDate, i.getDate(), prevValue, prev); 00088 Demand::add(prev); 00089 } 00090 if (i.getDate() == Date::infiniteFuture) break; 00091 prevDate = i.getDate(); 00092 prevValue = i.getValue(); 00093 } 00094 else 00095 { 00096 const CalendarInt* c = dynamic_cast<const CalendarInt*>(calptr); 00097 if (c) 00098 // Integer calendar 00099 for (CalendarInt::EventIterator i(c); i.getDate()<=Date::infiniteFuture; ++i) 00100 { 00101 if ((prevDate || i.getDate() == Date::infiniteFuture) && prevValue > 0) 00102 { 00103 prev = new ForecastBucket 00104 (this, prevDate, i.getDate(), prevValue, prev); 00105 Demand::add(prev); 00106 } 00107 if (i.getDate() == Date::infiniteFuture) break; 00108 prevDate = i.getDate(); 00109 prevValue = static_cast<double>(i.getValue()); 00110 } 00111 else 00112 { 00113 const CalendarBool* c = dynamic_cast<const CalendarBool*>(calptr); 00114 bool prevValueBool = false; 00115 if (c) 00116 // Boolean calendar 00117 for (CalendarBool::EventIterator i(c); i.getDate()<=Date::infiniteFuture; ++i) 00118 { 00119 if ((prevDate || i.getDate() == Date::infiniteFuture) && prevValueBool) 00120 { 00121 prev = new ForecastBucket 00122 (this, prevDate, i.getDate(), 1.0, prev); 00123 Demand::add(prev); 00124 } 00125 if (i.getDate() == Date::infiniteFuture) break; 00126 prevDate = i.getDate(); 00127 prevValueBool = i.getValue(); 00128 } 00129 else 00130 { 00131 // Other calendar 00132 for (Calendar::EventIterator i(calptr); i.getDate()<=Date::infiniteFuture; ++i) 00133 { 00134 if (prevDate || i.getDate() == Date::infiniteFuture) 00135 { 00136 prev = new ForecastBucket(this, prevDate, i.getDate(), 1.0, prev); 00137 Demand::add(prev); 00138 if (i.getDate() == Date::infiniteFuture) break; 00139 } 00140 prevDate = i.getDate(); 00141 } 00142 } 00143 } 00144 } 00145 } 00146 00147 00148 void Forecast::setDiscrete(const bool b) 00149 { 00150 // Update the flag 00151 discrete = b; 00152 00153 // Round down any forecast demands that may already exist. 00154 if (discrete) 00155 for (memberIterator m = beginMember(); m!=endMember(); ++m) 00156 m->setQuantity(floor(m->getQuantity())); 00157 } 00158 00159 00160 void Forecast::setTotalQuantity(const DateRange& d, double f) 00161 { 00162 // Initialize, if not done yet 00163 if (!isGroup()) initialize(); 00164 00165 // Find all forecast demands, and sum their weights 00166 double weights = 0.0; 00167 for (memberIterator m = beginMember(); m!=endMember(); ++m) 00168 { 00169 ForecastBucket* x = dynamic_cast<ForecastBucket*>(&*m); 00170 if (!x) 00171 throw DataException("Invalid subdemand of forecast '" + getName() +"'"); 00172 if (d.intersect(x->getDueRange())) 00173 { 00174 // Bucket intersects with daterange 00175 if (!d.getDuration()) 00176 { 00177 // Single date provided. Update that one bucket. 00178 x->setTotal(f); 00179 return; 00180 } 00181 weights += x->getWeight() * static_cast<long>(x->getDueRange().overlap(d)); 00182 } 00183 } 00184 00185 // Expect to find at least one non-zero weight... 00186 if (!weights) 00187 throw DataException("No valid forecast date in range " 00188 + string(d) + " of forecast '" + getName() +"'"); 00189 00190 // Update the forecast quantity, respecting the weights 00191 f /= weights; 00192 double carryover = 0.0; 00193 for (memberIterator m = beginMember(); m!=endMember(); ++m) 00194 { 00195 ForecastBucket* x = dynamic_cast<ForecastBucket*>(&*m); 00196 if (d.intersect(x->getDueRange())) 00197 { 00198 // Bucket intersects with daterange 00199 TimePeriod o = x->getDueRange().overlap(d); 00200 double percent = x->getWeight() * static_cast<long>(o); 00201 if (getDiscrete()) 00202 { 00203 // Rounding to discrete numbers 00204 carryover += f * percent; 00205 int intdelta = static_cast<int>(ceil(carryover - 0.5)); 00206 carryover -= intdelta; 00207 if (o < x->getDueRange().getDuration()) 00208 // The bucket is only partially updated 00209 x->incTotal(static_cast<double>(intdelta)); 00210 else 00211 // The bucket is completely updated 00212 x->setTotal(static_cast<double>(intdelta)); 00213 } 00214 else 00215 { 00216 // No rounding 00217 if (o < x->getDueRange().getDuration()) 00218 // The bucket is only partially updated 00219 x->incTotal(f * percent); 00220 else 00221 // The bucket is completely updated 00222 x->setTotal(f * percent); 00223 } 00224 } 00225 } 00226 } 00227 00228 00229 void Forecast::writeElement(XMLOutput *o, const Keyword &tag, mode m) const 00230 { 00231 // Writing a reference 00232 if (m == REFERENCE) 00233 { 00234 o->writeElement 00235 (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type); 00236 return; 00237 } 00238 00239 // Write the complete object 00240 if (m != NOHEADER) o->BeginObject 00241 (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type); 00242 00243 o->writeElement(Tags::tag_item, &*getItem()); 00244 if (getPriority()) o->writeElement(Tags::tag_priority, getPriority()); 00245 o->writeElement(Tags::tag_calendar, calptr); 00246 if (!getDiscrete()) o->writeElement(Tags::tag_discrete, getDiscrete()); 00247 o->writeElement(Tags::tag_operation, &*getOperation()); 00248 00249 // Write all entries 00250 o->BeginObject (Tags::tag_buckets); 00251 for (memberIterator i = beginMember(); i != endMember(); ++i) 00252 { 00253 ForecastBucket* f = dynamic_cast<ForecastBucket*>(&*i); 00254 o->BeginObject(Tags::tag_bucket, Tags::tag_start, string(f->getDue())); 00255 o->writeElement(tag_total, f->getTotal()); 00256 o->writeElement(Tags::tag_quantity, f->getQuantity()); 00257 o->writeElement(tag_consumed, f->getConsumed()); 00258 o->EndObject(Tags::tag_bucket); 00259 } 00260 o->EndObject(Tags::tag_buckets); 00261 00262 o->EndObject(tag); 00263 } 00264 00265 00266 void Forecast::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00267 { 00268 // While reading forecast buckets, we use the userarea field on the input 00269 // to cache the data. The temporary object is deleted when the bucket 00270 // tag is closed. 00271 if (pAttr.isA(Tags::tag_calendar)) 00272 { 00273 Calendar *b = dynamic_cast<Calendar*>(pIn.getPreviousObject()); 00274 if (b) setCalendar(b); 00275 else throw LogicException("Incorrect object type during read operation"); 00276 } 00277 else if (pAttr.isA(Tags::tag_discrete)) 00278 setDiscrete(pElement.getBool()); 00279 else if (pAttr.isA(Tags::tag_bucket)) 00280 { 00281 pair<DateRange,double> *d = 00282 static_cast< pair<DateRange,double>* >(pIn.getUserArea()); 00283 if (d) 00284 { 00285 // Update the forecast quantities 00286 setTotalQuantity(d->first, d->second); 00287 // Clear the read buffer 00288 d->first.setStart(Date()); 00289 d->first.setEnd(Date()); 00290 d->second = 0; 00291 } 00292 } 00293 else if (pIn.getParentElement().first.isA(Tags::tag_bucket)) 00294 { 00295 pair<DateRange,double> *d = 00296 static_cast< pair<DateRange,double>* >(pIn.getUserArea()); 00297 if (pAttr.isA(tag_total)) 00298 { 00299 if (d) d->second = pElement.getDouble(); 00300 else pIn.setUserArea( 00301 new pair<DateRange,double>(DateRange(),pElement.getDouble()) 00302 ); 00303 } 00304 else if (pAttr.isA(Tags::tag_start)) 00305 { 00306 Date x = pElement.getDate(); 00307 if (d) 00308 { 00309 if (!d->first.getStart()) d->first.setStartAndEnd(x,x); 00310 else d->first.setStart(x); 00311 } 00312 else pIn.setUserArea(new pair<DateRange,double>(DateRange(x,x),0)); 00313 } 00314 else if (pAttr.isA(Tags::tag_end)) 00315 { 00316 Date x = pElement.getDate(); 00317 if (d) 00318 { 00319 if (!d->first.getStart()) d->first.setStartAndEnd(x,x); 00320 else d->first.setEnd(x); 00321 } 00322 else pIn.setUserArea(new pair<DateRange,double>(DateRange(x,x),0)); 00323 } 00324 } 00325 else 00326 Demand::endElement(pIn, pAttr, pElement); 00327 00328 if (pIn.isObjectEnd()) 00329 { 00330 // Delete dynamically allocated temporary read object 00331 if (pIn.getUserArea()) 00332 delete static_cast< pair<DateRange,double>* >(pIn.getUserArea()); 00333 } 00334 } 00335 00336 00337 void Forecast::beginElement(XMLInput& pIn, const Attribute& pAttr) 00338 { 00339 if (pAttr.isA(Tags::tag_calendar)) 00340 pIn.readto( Calendar::reader(Calendar::metadata, pIn.getAttributes()) ); 00341 else 00342 Demand::beginElement(pIn, pAttr); 00343 } 00344 00345 00346 void Forecast::setCalendar(Calendar* c) 00347 { 00348 if (isGroup()) 00349 throw DataException( 00350 "Changing the calendar of an initialized forecast isn't allowed."); 00351 calptr = c; 00352 } 00353 00354 00355 void Forecast::setItem(Item* i) 00356 { 00357 // No change 00358 if (getItem() == i) return; 00359 00360 // Update the dictionary 00361 for (MapOfForecasts::iterator x = 00362 ForecastDictionary.lower_bound(make_pair( 00363 &*getItem(),&*getCustomer() 00364 )); 00365 x != ForecastDictionary.end(); ++x) 00366 if (x->second == this) 00367 { 00368 ForecastDictionary.erase(x); 00369 break; 00370 } 00371 ForecastDictionary.insert(make_pair(make_pair(i,&*getCustomer()),this)); 00372 00373 // Update data field 00374 Demand::setItem(i); 00375 00376 // Update the item for all buckets/subdemands 00377 for (memberIterator m = beginMember(); m!=endMember(); ++m) 00378 m->setItem(i); 00379 } 00380 00381 00382 void Forecast::setCustomer(Customer* i) 00383 { 00384 // No change 00385 if (getCustomer() == i) return; 00386 00387 // Update the dictionary 00388 for (MapOfForecasts::iterator x = 00389 ForecastDictionary.lower_bound(make_pair( 00390 getItem(), getCustomer() 00391 )); 00392 x != ForecastDictionary.end(); ++x) 00393 if (x->second == this) 00394 { 00395 ForecastDictionary.erase(x); 00396 break; 00397 } 00398 ForecastDictionary.insert(make_pair(make_pair(&*getItem(),i),this)); 00399 00400 // Update data field 00401 Demand::setCustomer(i); 00402 00403 // Update the customer for all buckets/subdemands 00404 for (memberIterator m = beginMember(); m!=endMember(); ++m) 00405 m->setCustomer(i); 00406 } 00407 00408 00409 void Forecast::setMaxLateness(TimePeriod i) 00410 { 00411 Demand::setMaxLateness(i); 00412 // Update the maximum lateness for all buckets/subdemands 00413 for (memberIterator m = beginMember(); m!=endMember(); ++m) 00414 m->setMaxLateness(i); 00415 } 00416 00417 00418 void Forecast::setMinShipment(double i) 00419 { 00420 Demand::setMinShipment(i); 00421 // Update the minimum shipment for all buckets/subdemands 00422 for (memberIterator m = beginMember(); m!=endMember(); ++m) 00423 m->setMinShipment(i); 00424 } 00425 00426 00427 void Forecast::setPriority(int i) 00428 { 00429 Demand::setPriority(i); 00430 // Update the priority for all buckets/subdemands 00431 for (memberIterator m = beginMember(); m!=endMember(); ++m) 00432 m->setPriority(i); 00433 } 00434 00435 00436 void Forecast::setOperation(Operation *o) 00437 { 00438 Demand::setOperation(o); 00439 // Update the priority for all buckets/subdemands 00440 for (memberIterator m = beginMember(); m!=endMember(); ++m) 00441 m->setOperation(o); 00442 } 00443 00444 } // end namespace
Documentation generated by
