operation.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/model/operation.cpp $
00003   version : $LastChangedRevision: 1315 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2010-07-17 18:08:53 +0200 (Sat, 17 Jul 2010) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 *
00024  * USA                                                                     *
00025  *                                                                         *
00026  ***************************************************************************/
00027 
00028 #define FREPPLE_CORE
00029 #include "frepple/model.h"
00030 
00031 namespace frepple
00032 {
00033 
00034 template<class Operation> DECLARE_EXPORT Tree utils::HasName<Operation>::st;
00035 DECLARE_EXPORT const MetaCategory* Operation::metadata;
00036 DECLARE_EXPORT const MetaClass* OperationFixedTime::metadata,
00037   *OperationTimePer::metadata,
00038   *OperationRouting::metadata,
00039   *OperationAlternate::metadata,
00040   *OperationSetup::metadata;
00041 DECLARE_EXPORT Operation::Operationlist Operation::nosubOperations;
00042 DECLARE_EXPORT const Operation* OperationSetup::setupoperation;
00043 
00044 
00045 int Operation::initialize()
00046 {
00047   // Initialize the metadata
00048   metadata = new MetaCategory("operation", "operations", reader, writer);
00049 
00050   // Initialize the Python class
00051   return FreppleCategory<Operation>::initialize();
00052 }
00053 
00054 
00055 int OperationFixedTime::initialize()
00056 {
00057   // Initialize the metadata
00058   metadata = new MetaClass("operation", "operation_fixed_time",
00059     Object::createString<OperationFixedTime>, true);
00060 
00061   // Initialize the Python class
00062   return FreppleClass<OperationFixedTime,Operation>::initialize();
00063 }
00064 
00065 
00066 int OperationTimePer::initialize()
00067 {
00068   // Initialize the metadata
00069   metadata = new MetaClass("operation", "operation_time_per",
00070     Object::createString<OperationTimePer>);
00071 
00072   // Initialize the Python class
00073   return FreppleClass<OperationTimePer,Operation>::initialize();
00074 }
00075 
00076 
00077 int OperationAlternate::initialize()
00078 {
00079   // Initialize the metadata
00080   metadata = new MetaClass("operation", "operation_alternate",
00081     Object::createString<OperationAlternate>);
00082 
00083   // Initialize the Python class
00084   FreppleClass<OperationAlternate,Operation>::getType().addMethod("addAlternate", OperationAlternate::addAlternate, METH_KEYWORDS, "add an alternate");
00085   return FreppleClass<OperationAlternate,Operation>::initialize();
00086 }
00087 
00088 
00089 int OperationRouting::initialize()
00090 {
00091   // Initialize the metadata
00092   metadata = new MetaClass("operation", "operation_routing",
00093     Object::createString<OperationRouting>);
00094 
00095   // Initialize the Python class
00096   FreppleClass<OperationRouting,Operation>::getType().addMethod("addStep", OperationRouting::addStep, METH_VARARGS , "add steps to the routing");
00097   return FreppleClass<OperationRouting,Operation>::initialize();
00098 }
00099 
00100 
00101 int OperationSetup::initialize()
00102 {
00103   // Initialize the metadata.
00104   // There is NO factory method
00105   metadata = new MetaClass("operation", "operation_setup");
00106 
00107   // Initialize the Python class
00108   int tmp = FreppleClass<OperationSetup,Operation>::initialize();
00109 
00110   // Create a generic setup operation.
00111   // This will be the only instance of this class.
00112   setupoperation = add(new OperationSetup("setup operation"));
00113 
00114   return tmp;
00115 }
00116 
00117 
00118 DECLARE_EXPORT Operation::~Operation()
00119 {
00120   // Delete all existing operationplans (even locked ones)
00121   deleteOperationPlans(true);
00122 
00123   // The Flow and Load objects are automatically deleted by the destructor
00124   // of the Association list class.
00125 
00126   // Remove the reference to this operation from all items
00127   for (Item::iterator k = Item::begin(); k != Item::end(); ++k)
00128     if (k->getOperation() == this) k->setOperation(NULL);
00129 
00130   // Remove the reference to this operation from all demands
00131   for (Demand::iterator l = Demand::begin(); l != Demand::end(); ++l)
00132     if (l->getOperation() == this) l->setOperation(NULL);
00133 
00134   // Remove the reference to this operation from all buffers
00135   for (Buffer::iterator m = Buffer::begin(); m != Buffer::end(); ++m)
00136     if (m->getProducingOperation() == this) m->setProducingOperation(NULL);
00137 
00138   // Remove the operation from its super-operations and sub-operations
00139   // Note that we are not using a for-loop since our function is actually
00140   // updating the list of super-operations at the same time as we move
00141   // through it.
00142   while (!getSuperOperations().empty())
00143     removeSuperOperation(*getSuperOperations().begin());
00144 }
00145 
00146 
00147 DECLARE_EXPORT OperationRouting::~OperationRouting()
00148 {
00149   // Note that we are not using a for-loop since our function is actually
00150   // updating the list of super-operations at the same time as we move
00151   // through it.
00152   while (!getSubOperations().empty())
00153     removeSubOperation(*getSubOperations().begin());
00154 }
00155 
00156 
00157 DECLARE_EXPORT OperationAlternate::~OperationAlternate()
00158 {
00159   // Note that we are not using a for-loop since our function is actually
00160   // updating the list of super-operations at the same time as we move
00161   // through it.
00162   while (!getSubOperations().empty())
00163     removeSubOperation(*getSubOperations().begin());
00164 }
00165 
00166 
00167 DECLARE_EXPORT OperationPlan* Operation::createOperationPlan (double q, Date s, Date e,
00168     Demand* l, OperationPlan* ow, unsigned long i,
00169     bool makeflowsloads) const
00170 {
00171   OperationPlan *opplan = new OperationPlan();
00172   initOperationPlan(opplan,q,s,e,l,ow,i,makeflowsloads);
00173   return opplan;
00174 }
00175 
00176 
00177 DECLARE_EXPORT DateRange Operation::calculateOperationTime
00178   (Date thedate, TimePeriod duration, bool forward,
00179     TimePeriod *actualduration) const
00180 {
00181   int calcount = 0;
00182   // Initial size of 10 should do for 99.99% of all cases
00183   vector<Calendar::EventIterator*> cals(10);
00184 
00185   // Default actual duration
00186   if (actualduration) *actualduration = duration;
00187 
00188   try
00189   {
00190     // Step 1: Create an iterator on each of the calendars
00191     // a) operation's location
00192     if (loc && loc->getAvailable())
00193       cals[calcount++] = new Calendar::EventIterator(loc->getAvailable(), thedate, forward);
00194     /* @todo multiple availability calendars are not implmented yet
00195       for (Operation::loadlist::const_iterator g=loaddata.begin();
00196         g!=loaddata.end(); ++g)
00197     {
00198       Resource* res = g->getResource();
00199       if (res->getMaximum())
00200         // b) resource size calendar
00201         cals[calcount++] = new Calendar::EventIterator(
00202           res->getMaximum(),
00203           thedate
00204           );
00205       if (res->getLocation() && res->getLocation()->getAvailable())
00206         // c) resource location
00207         cals[calcount++] = new Calendar::EventIterator(
00208           res->getLocation()->getAvailable(),
00209           thedate
00210           );
00211     }
00212     */
00213 
00214     // Special case: no calendars at all
00215     if (calcount == 0)
00216       return forward ?
00217         DateRange(thedate, thedate+duration) :
00218         DateRange(thedate-duration, thedate);
00219 
00220     // Step 2: Iterate over the calendar dates to find periods where all
00221     // calendars are simultaneously effective.
00222     DateRange result;
00223     Date curdate = thedate;
00224     bool status = false;
00225     TimePeriod curduration = duration;
00226     while (true)
00227     {
00228       // Check whether all calendars are available
00229       bool available = true;
00230       for (int c = 0; c < calcount && available; c++)
00231       {
00232         if (cals[c]->getBucket())
00233           available = cals[c]->getBucket()->getBool();
00234         else
00235           available = cals[c]->getCalendar()->getBool();
00236       }
00237       curdate = cals[0]->getDate();
00238 
00239       if (available && !status)
00240       {
00241         // Becoming available after unavailable period
00242         thedate = curdate;
00243         status = true;
00244         if (forward && result.getStart() == Date::infinitePast)
00245           // First available time - make operation start at this time
00246           result.setStart(curdate);
00247         else if (!forward && result.getEnd() == Date::infiniteFuture)
00248           // First available time - make operation end at this time
00249           result.setEnd(curdate);
00250       }
00251       else if (!available && status)
00252       {
00253         // Becoming unavailable after available period
00254         status = false;
00255         if (forward)
00256         {
00257           // Forward
00258           TimePeriod delta = curdate - thedate;
00259           if (delta >= curduration)
00260           {
00261             result.setEnd(thedate + curduration);
00262             break;
00263           }
00264           else
00265             curduration -= delta;
00266         }
00267         else
00268         {
00269           // Backward
00270           TimePeriod delta = thedate - curdate;
00271           if (delta >= curduration)
00272           {
00273             result.setStart(thedate - curduration);
00274             break;
00275           }
00276           else
00277             curduration -= delta;
00278         }
00279       }
00280       else if (forward && curdate == Date::infiniteFuture)
00281       {
00282         // End of forward iteration
00283         if (available)
00284         {
00285           TimePeriod delta = curdate - thedate;
00286           if (delta >= curduration)
00287             result.setEnd(thedate + curduration);
00288           else if (actualduration)
00289             *actualduration = duration - curduration;
00290         }
00291         else  if (actualduration)
00292           *actualduration = duration - curduration;
00293         break;
00294       }
00295       else if (!forward && curdate == Date::infinitePast)
00296       {
00297         // End of backward iteration
00298         if (available)
00299         {
00300           TimePeriod delta = thedate - curdate;
00301           if (delta >= curduration)
00302             result.setStart(thedate - curduration);
00303           else if (actualduration)
00304             *actualduration = duration - curduration;
00305         }
00306         else if (actualduration)
00307           *actualduration = duration - curduration;
00308         break;
00309       }
00310 
00311       // Advance to the next event
00312       if (forward) ++(*cals[0]);
00313       else --(*cals[0]);
00314     }
00315 
00316     // Step 3: Clean up
00317     while (calcount) delete cals[--calcount];
00318     return result;
00319   }
00320   catch (...)
00321   {
00322     // Clean up
00323     while (calcount) delete cals[calcount--];
00324     // Rethrow the exception
00325     throw;
00326   }
00327 }
00328 
00329 
00330 DECLARE_EXPORT DateRange Operation::calculateOperationTime
00331   (Date start, Date end, TimePeriod *actualduration) const
00332 {
00333   // Switch start and end if required
00334   if (end < start)
00335   {
00336     Date tmp = start;
00337     start = end;
00338     end = tmp;
00339   }
00340 
00341   int calcount = 0;
00342   // Initial size of 10 should do for 99.99% of all cases
00343   vector<Calendar::EventIterator*> cals(10);
00344 
00345   // Default actual duration
00346    if (actualduration) *actualduration = 0L;
00347 
00348   try
00349   {
00350     // Step 1: Create an iterator on each of the calendars
00351     // a) operation's location
00352     if (loc && loc->getAvailable())
00353       cals[calcount++] = new Calendar::EventIterator(loc->getAvailable(), start);
00354     /* @todo multiple availability calendars are not implmented yet
00355       for (Operation::loadlist::const_iterator g=loaddata.begin();
00356         g!=loaddata.end(); ++g)
00357     {
00358       Resource* res = g->getResource();
00359       if (res->getMaximum())
00360         // b) resource size calendar
00361         cals[calcount++] = new Calendar::EventIterator(
00362           res->getMaximum(),
00363           start
00364           );
00365       if (res->getLocation() && res->getLocation()->getAvailable())
00366         // c) resource location
00367         cals[calcount++] = new Calendar::EventIterator(
00368           res->getLocation()->getAvailable(),
00369           start
00370           );
00371     }
00372     */
00373 
00374     // Special case: no calendars at all
00375     if (calcount == 0)
00376     {
00377        if (actualduration) *actualduration = end - start;
00378       return DateRange(start, end);
00379     }
00380 
00381     // Step 2: Iterate over the calendar dates to find periods where all
00382     // calendars are simultaneously effective.
00383     DateRange result;
00384     Date curdate = start;
00385     bool status = false;
00386     while (true)
00387     {
00388       // Check whether all calendar are available
00389       bool available = true;
00390       for (int c = 0; c < calcount && available; c++)
00391       {
00392         if (cals[c]->getBucket())
00393           available = cals[c]->getBucket()->getBool();
00394         else
00395           available = cals[c]->getCalendar()->getBool();
00396       }
00397       curdate = cals[0]->getDate();
00398 
00399       if (available && !status)
00400       {
00401         // Becoming available after unavailable period
00402         if (curdate >= end)
00403         {
00404           // Leaving the desired date range
00405           result.setEnd(start);
00406           break;
00407         }
00408         start = curdate;
00409         status = true;
00410         if (result.getStart() == Date::infinitePast)
00411           // First available time - make operation start at this time
00412           result.setStart(curdate);
00413       }
00414       else if (!available && status)
00415       {
00416         // Becoming unavailable after available period
00417         if (curdate >= end)
00418         {
00419           // Leaving the desired date range
00420            if (actualduration) *actualduration += end - start;
00421           result.setEnd(end);
00422           break;
00423         }
00424         status = false;
00425          if (actualduration) *actualduration += curdate - start;
00426         start = curdate;
00427       }
00428       else if (curdate >= end)
00429       {
00430         // Leaving the desired date range
00431         if (available)
00432         {
00433            if (actualduration) *actualduration += end - start;
00434           result.setEnd(end);
00435           break;
00436         }
00437         else
00438           result.setEnd(start);
00439         break;
00440       }
00441 
00442       // Advance to the next event
00443       ++(*cals[0]);
00444     }
00445 
00446     // Step 3: Clean up
00447     while (calcount) delete cals[--calcount];
00448     return result;
00449   }
00450   catch (...)
00451   {
00452     // Clean up
00453     while (calcount) delete cals[calcount--];
00454     // Rethrow the exception
00455     throw;
00456   }
00457 }
00458 
00459 
00460 DECLARE_EXPORT void Operation::initOperationPlan (OperationPlan* opplan,
00461     double q, const Date& s, const Date& e, Demand* l, OperationPlan* ow,
00462     unsigned long i, bool makeflowsloads) const
00463 {
00464   opplan->oper = const_cast<Operation*>(this);
00465   opplan->setDemand(l);
00466   opplan->id = i;
00467 
00468   // Setting the owner first. Note that the order is important here!
00469   // For alternates & routings the quantity needs to be set through the owner.
00470   opplan->setOwner(ow);
00471 
00472   // Setting the dates and quantity
00473   setOperationPlanParameters(opplan,q,s,e);
00474 
00475   // Create the loadplans and flowplans, if allowed
00476   if (makeflowsloads) opplan->createFlowLoads();
00477 
00478   // Update flow and loadplans, and mark for problem detection
00479   opplan->update();
00480 }
00481 
00482 
00483 DECLARE_EXPORT void Operation::deleteOperationPlans(bool deleteLockedOpplans)
00484 {
00485   OperationPlan::deleteOperationPlans(this, deleteLockedOpplans);
00486 }
00487 
00488 
00489 DECLARE_EXPORT void Operation::writeElement(XMLOutput *o, const Keyword& tag, mode m) const
00490 {
00491   // Note that this class is abstract and never instantiated directly. There is
00492   // therefore no reason to ever write a header.
00493   assert(m == NOHEADER);
00494 
00495   // Write the fields
00496   HasDescription::writeElement(o, tag);
00497   Plannable::writeElement(o, tag);
00498   if (post_time)
00499     o->writeElement(Tags::tag_posttime, post_time);
00500   if (pre_time)
00501     o->writeElement(Tags::tag_pretime, pre_time);
00502   if (getCost() != 0.0)
00503     o->writeElement(Tags::tag_cost, getCost());
00504   if (fence)
00505     o->writeElement(Tags::tag_fence, fence);
00506   if (size_minimum != 1.0)
00507     o->writeElement(Tags::tag_size_minimum, size_minimum);
00508   if (size_multiple > 0.0)
00509     o->writeElement(Tags::tag_size_multiple, size_multiple);
00510   if (size_maximum < DBL_MAX)
00511     o->writeElement(Tags::tag_size_maximum, size_maximum);
00512   if (loc)
00513     o->writeElement(Tags::tag_location, loc);
00514 
00515   // Write extra plan information
00516   if ((o->getContentType() == XMLOutput::PLAN
00517       || o->getContentType() == XMLOutput::PLANDETAIL) && first_opplan)
00518   {
00519     o->BeginObject(Tags::tag_operationplans);
00520     for (OperationPlan::iterator i(this); i!=OperationPlan::end(); ++i)
00521       o->writeElement(Tags::tag_operationplan, *i, FULL);
00522     o->EndObject(Tags::tag_operationplans);
00523   }
00524 }
00525 
00526 
00527 DECLARE_EXPORT void Operation::beginElement(XMLInput& pIn, const Attribute& pAttr)
00528 {
00529   if (pAttr.isA(Tags::tag_flow)
00530       && pIn.getParentElement().first.isA(Tags::tag_flows))
00531   {
00532     Flow *f =
00533       dynamic_cast<Flow*>(MetaCategory::ControllerDefault(Flow::metadata,pIn.getAttributes()));
00534     if (f) f->setOperation(this);
00535     pIn.readto(f);
00536   }
00537   else if (pAttr.isA (Tags::tag_load)
00538       && pIn.getParentElement().first.isA(Tags::tag_loads))
00539   {
00540     Load* l = new Load();
00541     l->setOperation(this);
00542     pIn.readto(&*l);
00543   }
00544   else if (pAttr.isA (Tags::tag_operationplan))
00545     pIn.readto(OperationPlan::createOperationPlan(OperationPlan::metadata, pIn.getAttributes()));
00546   else if (pAttr.isA (Tags::tag_location))
00547     pIn.readto( Location::reader(Location::metadata,pIn.getAttributes()) );
00548 }
00549 
00550 
00551 DECLARE_EXPORT void Operation::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00552 {
00553   if (pAttr.isA (Tags::tag_fence))
00554     setFence(pElement.getTimeperiod());
00555   else if (pAttr.isA (Tags::tag_size_minimum))
00556     setSizeMinimum(pElement.getDouble());
00557   else if (pAttr.isA (Tags::tag_cost))
00558     setCost(pElement.getDouble());
00559   else if (pAttr.isA (Tags::tag_size_multiple))
00560     setSizeMultiple(pElement.getDouble());
00561   else if (pAttr.isA (Tags::tag_size_maximum))
00562     setSizeMaximum(pElement.getDouble());
00563   else if (pAttr.isA (Tags::tag_pretime))
00564     setPreTime(pElement.getTimeperiod());
00565   else if (pAttr.isA (Tags::tag_posttime))
00566     setPostTime(pElement.getTimeperiod());
00567   else if (pAttr.isA (Tags::tag_location))
00568   {
00569     Location *l = dynamic_cast<Location*>(pIn.getPreviousObject());
00570     if (l) setLocation(l);
00571     else throw LogicException("Incorrect object type during read operation");
00572   }
00573   else
00574   {
00575     Plannable::endElement(pIn, pAttr, pElement);
00576     HasDescription::endElement(pIn, pAttr, pElement);
00577   }
00578 }
00579 
00580 
00581 DECLARE_EXPORT OperationPlanState
00582 OperationFixedTime::setOperationPlanParameters
00583 (OperationPlan* opplan, double q, Date s, Date e, bool preferEnd, bool execute) const
00584 {
00585   // Invalid call to the function, or locked operationplan.
00586   if (!opplan || q<0)
00587     throw LogicException("Incorrect parameters for fixedtime operationplan");
00588   if (opplan->getLocked())
00589     return OperationPlanState(opplan);
00590 
00591   // All quantities are valid, as long as they are above the minimum size and 
00592   // below the maximum size
00593   if (q > 0 && q < getSizeMinimum()) q = getSizeMinimum();
00594   if (q > getSizeMaximum()) q = getSizeMaximum();
00595   if (fabs(q - opplan->getQuantity()) > ROUNDING_ERROR)
00596     q = opplan->setQuantity(q, false, false, execute);
00597 
00598   // Set the start and end date.
00599   DateRange x;
00600   TimePeriod actualduration;
00601   if (e && s)
00602   {
00603     if (preferEnd) x = calculateOperationTime(e, duration, false, &actualduration);
00604     else x = calculateOperationTime(s, duration, true, &actualduration);
00605   }
00606   else if (s) x = calculateOperationTime(s, duration, true, &actualduration);
00607   else x = calculateOperationTime(e, duration, false, &actualduration);
00608   if (!execute)
00609     // Simulation only
00610     return OperationPlanState(x, actualduration == duration ? q : 0);
00611   else if (actualduration == duration)
00612     // Update succeeded
00613     opplan->setStartAndEnd(x.getStart(), x.getEnd());
00614   else
00615     // Update failed - Not enough available time
00616     opplan->setQuantity(0);
00617 
00618   // Return value
00619   return OperationPlanState(opplan);
00620 }
00621 
00622 
00623 DECLARE_EXPORT bool OperationFixedTime::extraInstantiate(OperationPlan* o)
00624 {
00625   // See if we can consolidate this operationplan with an existing one.
00626   // Merging is possible only when all the following conditions are met:
00627   //   - id of the new opplan is not set
00628   //   - id of the old opplan is set
00629   //   - it is a fixedtime operation
00630   //   - it doesn't load any resources
00631   //   - both operationplans aren't locked
00632   //   - both operationplans have no owner
00633   //   - start and end date of both operationplans are the same
00634   //   - demand of both operationplans are the same
00635   //   - maximum operation size is not exceeded
00636   //   - alternate flowplans need to be on the same alternate
00637   if (!o->getIdentifier() && !o->getLocked() && !o->getOwner() && getLoads().empty())
00638   {
00639     // Loop through candidates
00640     OperationPlan::iterator x(this);
00641     OperationPlan *y = NULL;
00642     for (; x != OperationPlan::end() && *x < *o; ++x)
00643       y = &*x;
00644     if (y && y->getDates() == o->getDates() && !y->getOwner()
00645       && y->getDemand() == o->getDemand() && !y->getLocked() && y->getIdentifier()
00646       && y->getQuantity() + o->getQuantity() < getSizeMaximum())  // @todo ignores multiple qty
00647     {
00648       // Check that the flowplans are on identical alternates
00649       OperationPlan::FlowPlanIterator fp1 = o->beginFlowPlans();
00650       OperationPlan::FlowPlanIterator fp2 = y->beginFlowPlans();
00651       while (fp1 != o->endFlowPlans())
00652       {
00653         if (fp1->getBuffer() != fp2->getBuffer())
00654           // Different alternates - no merge possible
00655           return true;
00656         ++fp1;
00657         ++fp2;
00658       }
00659       // Merging with the 'next' operationplan
00660       y->setQuantity(y->getQuantity() + o->getQuantity());
00661       return false;
00662     }
00663     if (x!= OperationPlan::end() && x->getDates() == o->getDates() && !x->getOwner()
00664       && x->getDemand() == o->getDemand() && !x->getLocked() && x->getIdentifier()
00665       && x->getQuantity() + o->getQuantity() < getSizeMaximum()) // @todo ignores multiple qty
00666     {
00667       // Check that the flowplans are on identical alternates
00668       OperationPlan::FlowPlanIterator fp1 = o->beginFlowPlans();
00669       OperationPlan::FlowPlanIterator fp2 = x->beginFlowPlans();
00670       while (fp1 != o->endFlowPlans())
00671       {
00672         if (fp1->getBuffer() != fp2->getBuffer())
00673           // Different alternates - no merge possible
00674           return true;
00675         ++fp1;
00676         ++fp2;
00677       }
00678       // Merging with the 'previous' operationplan
00679       x->setQuantity(x->getQuantity() + o->getQuantity());
00680       return false;
00681     }
00682   }
00683   return true;
00684 }
00685 
00686 
00687 DECLARE_EXPORT void OperationFixedTime::writeElement
00688 (XMLOutput *o, const Keyword& tag, mode m) const
00689 {
00690   // Writing a reference
00691   if (m == REFERENCE)
00692   {
00693     o->writeElement
00694       (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00695     return;
00696   }
00697 
00698   // Write the complete object
00699   if (m != NOHEADER) o->BeginObject
00700     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00701 
00702   // Write the fields
00703   Operation::writeElement(o, tag, NOHEADER);
00704   if (duration) o->writeElement (Tags::tag_duration, duration);
00705   o->EndObject (tag);
00706 }
00707 
00708 
00709 DECLARE_EXPORT void OperationFixedTime::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00710 {
00711   if (pAttr.isA (Tags::tag_duration))
00712     setDuration (pElement.getTimeperiod());
00713   else
00714     Operation::endElement (pIn, pAttr, pElement);
00715 }
00716 
00717 
00718 DECLARE_EXPORT OperationPlanState
00719 OperationTimePer::setOperationPlanParameters
00720 (OperationPlan* opplan, double q, Date s, Date e, bool preferEnd, bool execute) const
00721 {
00722   // Invalid call to the function.
00723   if (!opplan || q<0)
00724     throw LogicException("Incorrect parameters for timeper operationplan");
00725   if (opplan->getLocked())
00726     return OperationPlanState(opplan);
00727 
00728   // Respect minimum and maximum size
00729   if (q > 0 && q < getSizeMinimum()) q = getSizeMinimum();
00730   if (q > getSizeMaximum()) q = getSizeMaximum();
00731 
00732   // The logic depends on which dates are being passed along
00733   DateRange x;
00734   TimePeriod actual;
00735   if (s && e)
00736   {
00737     // Case 1: Both the start and end date are specified: Compute the quantity.
00738     // Calculate the available time between those dates
00739     x = calculateOperationTime(s,e,&actual);
00740     if (actual < duration)
00741     {
00742       // Start and end aren't far enough from each other to fit the constant
00743       // part of the operation duration. This is infeasible.
00744       if (!execute) return OperationPlanState(x,0);
00745       opplan->setQuantity(0,true,false,execute);
00746       opplan->setEnd(e);  
00747     }
00748     else
00749     {
00750       // Calculate the quantity, respecting minimum, maximum and multiple size.
00751       if (duration_per)
00752       {
00753         if (q * duration_per < static_cast<double>(actual - duration) + 1)
00754           // Provided quantity is acceptable.
00755           // Note that we allow a margin of 1 second to accept.
00756           q = opplan->setQuantity(q, true, false, execute);
00757         else
00758           // Calculate the maximum operationplan that will fit in the window
00759           q = opplan->setQuantity(
00760             static_cast<double>(actual - duration) / duration_per, 
00761             true, false, execute);
00762       }
00763       else
00764         // No duration_per field given, so any quantity will go
00765         q = opplan->setQuantity(q, true, false, execute);
00766 
00767       // Updates the dates
00768       TimePeriod wanted(
00769         duration + static_cast<long>(duration_per * q)
00770         );
00771       if (preferEnd) x = calculateOperationTime(e, wanted, false, &actual);
00772       else x = calculateOperationTime(s, wanted, true, &actual);
00773       if (!execute) return OperationPlanState(x,q);
00774       opplan->setStartAndEnd(x.getStart(),x.getEnd());
00775     }
00776   }
00777   else if (e || !s)
00778   {
00779     // Case 2: Only an end date is specified. Respect the quantity and
00780     // compute the start date
00781     // Case 4: No date was given at all. Respect the quantity and the
00782     // existing end date of the operationplan.
00783     q = opplan->setQuantity(q,true,false,execute); // Round and size the quantity
00784     TimePeriod wanted(duration + static_cast<long>(duration_per * q));
00785     x = calculateOperationTime(e, wanted, false, &actual);
00786     if (actual == wanted)
00787     {
00788       // Size is as desired
00789       if (!execute) return OperationPlanState(x, q);
00790       opplan->setStartAndEnd(x.getStart(),x.getEnd());
00791     }
00792     else if (actual < duration)
00793     {
00794       // Not feasible
00795       if (!execute) return OperationPlanState(x, 0);
00796       opplan->setQuantity(0,true,false);
00797       opplan->setStartAndEnd(e,e);
00798     }
00799     else
00800     {
00801       // Resize the quantity to be feasible
00802       double max_q = duration_per ?
00803         static_cast<double>(actual-duration) / duration_per :
00804         q;
00805       q = opplan->setQuantity(q < max_q ? q : max_q, true, false, execute);
00806       wanted = duration + static_cast<long>(duration_per * q);
00807       x = calculateOperationTime(e, wanted, false, &actual);
00808       if (!execute) return OperationPlanState(x, q);
00809       opplan->setStartAndEnd(x.getStart(),x.getEnd());
00810     }
00811   }
00812   else
00813   {
00814     // Case 3: Only a start date is specified. Respect the quantity and
00815     // compute the end date
00816     q = opplan->setQuantity(q,true,false,execute); // Round and size the quantity
00817     TimePeriod wanted(
00818       duration + static_cast<long>(duration_per * q)
00819       );
00820     TimePeriod actual;
00821     x = calculateOperationTime(s, wanted, true, &actual);
00822     if (actual == wanted)
00823     {
00824       // Size is as desired
00825       if (!execute) return OperationPlanState(x, q);
00826       opplan->setStartAndEnd(x.getStart(),x.getEnd());
00827     }
00828     else if (actual < duration)
00829     {
00830       // Not feasible
00831       if (!execute) return OperationPlanState(x, 0);
00832       opplan->setQuantity(0,true,false);
00833       opplan->setStartAndEnd(s,s);
00834     }
00835     else
00836     {
00837       // Resize the quantity to be feasible
00838       double max_q = duration_per ?
00839         static_cast<double>(actual-duration) / duration_per :
00840         q;
00841       q = opplan->setQuantity(q < max_q ? q : max_q, true, false, execute);
00842       wanted = duration + static_cast<long>(duration_per * q);
00843       x = calculateOperationTime(e, wanted, false, &actual);
00844       if (!execute) return OperationPlanState(x, q);
00845       opplan->setStartAndEnd(x.getStart(),x.getEnd());
00846     }
00847   }
00848 
00849   // Return value
00850   return OperationPlanState(opplan);
00851 }
00852 
00853 
00854 DECLARE_EXPORT void OperationTimePer::writeElement
00855 (XMLOutput *o, const Keyword& tag, mode m) const
00856 {
00857   // Writing a reference
00858   if (m == REFERENCE)
00859   {
00860     o->writeElement
00861       (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00862     return;
00863   }
00864 
00865   // Write the complete object
00866   if (m != NOHEADER) o->BeginObject
00867     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00868 
00869   // Write the complete object
00870   Operation::writeElement(o, tag, NOHEADER);
00871   o->writeElement(Tags::tag_duration, duration);
00872   o->writeElement(Tags::tag_duration_per, duration_per);
00873   o->EndObject(tag);
00874 }
00875 
00876 
00877 DECLARE_EXPORT void OperationTimePer::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00878 {
00879   if (pAttr.isA (Tags::tag_duration))
00880     setDuration (pElement.getTimeperiod());
00881   else if (pAttr.isA (Tags::tag_duration_per))
00882     setDurationPer (pElement.getTimeperiod());
00883   else
00884     Operation::endElement (pIn, pAttr, pElement);
00885 }
00886 
00887 
00888 DECLARE_EXPORT void OperationRouting::writeElement
00889 (XMLOutput *o, const Keyword& tag, mode m) const
00890 {
00891   // Writing a reference
00892   if (m == REFERENCE)
00893   {
00894     o->writeElement
00895       (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00896     return;
00897   }
00898 
00899   // Write the complete object
00900   if (m != NOHEADER) o->BeginObject
00901     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00902 
00903   // Write the fields
00904   Operation::writeElement(o, tag, NOHEADER);
00905   if (steps.size())
00906   {
00907     o->BeginObject(Tags::tag_steps);
00908     for (Operationlist::const_iterator i = steps.begin(); i!=steps.end(); ++i)
00909       o->writeElement(Tags::tag_operation, *i, REFERENCE);
00910     o->EndObject(Tags::tag_steps);
00911   }
00912   o->EndObject(tag);
00913 }
00914 
00915 
00916 DECLARE_EXPORT void OperationRouting::beginElement(XMLInput& pIn, const Attribute& pAttr)
00917 {
00918   if (pAttr.isA (Tags::tag_operation))
00919     pIn.readto( Operation::reader(Operation::metadata,pIn.getAttributes()) );
00920   else
00921     Operation::beginElement(pIn, pAttr);
00922 }
00923 
00924 
00925 DECLARE_EXPORT void OperationRouting::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00926 {
00927   if (pAttr.isA (Tags::tag_operation)
00928       && pIn.getParentElement().first.isA(Tags::tag_steps))
00929   {
00930     Operation *oper = dynamic_cast<Operation*>(pIn.getPreviousObject());
00931     if (oper) addStepBack (oper);
00932     else throw LogicException("Incorrect object type during read operation");
00933   }
00934   Operation::endElement (pIn, pAttr, pElement);
00935 }
00936 
00937 
00938 DECLARE_EXPORT OperationPlanState OperationRouting::setOperationPlanParameters
00939 (OperationPlan* opplan, double q, Date s, Date e, bool preferEnd, bool execute) const
00940 {
00941   // Invalid call to the function
00942   if (!opplan || q<0)
00943     throw LogicException("Incorrect parameters for routing operationplan");
00944   if (opplan->getLocked())
00945     return OperationPlanState(opplan);
00946 
00947   if (!opplan->lastsubopplan || opplan->lastsubopplan->getOperation() == OperationSetup::setupoperation) // @todo replace with proper iterator
00948   {
00949     // No step operationplans to work with. Just apply the requested quantity
00950     // and dates.
00951     q = opplan->setQuantity(q,false,false,execute);
00952     if (!s && e) s = e;
00953     if (s && !e) e = s;
00954     if (!execute) return OperationPlanState(s, e, q);
00955     opplan->setStartAndEnd(s,e);
00956     return OperationPlanState(opplan);
00957   }
00958 
00959   // Behavior depends on the dates being passed.
00960   // Move all sub-operationplans in an orderly fashion, either starting from
00961   // the specified end date or the specified start date.
00962   OperationPlanState x;
00963   Date y;
00964   bool realfirst = true;
00965   if (e)
00966   {
00967     // Case 1: an end date is specified
00968     for (OperationPlan* i = opplan->lastsubopplan; i; i = i->prevsubopplan)
00969     {
00970       if (i->getOperation() == OperationSetup::setupoperation) continue;
00971       x = i->getOperation()->setOperationPlanParameters(i,q,Date::infinitePast,e,preferEnd,execute);
00972       e = x.start;
00973       if (realfirst) 
00974       {
00975         y = x.end;
00976         realfirst = false;
00977       }
00978     }
00979     return OperationPlanState(x.start, y, x.quantity);
00980   }
00981   else if (s)
00982   {
00983     // Case 2: a start date is specified
00984     for (OperationPlan *i = opplan->firstsubopplan; i; i = i->nextsubopplan)
00985     {
00986       if (i->getOperation() == OperationSetup::setupoperation) continue;
00987       x = i->getOperation()->setOperationPlanParameters(i,q,s,Date::infinitePast,preferEnd,execute);
00988       s = x.end;
00989       if (realfirst)
00990       {
00991         y = x.start;
00992         realfirst = false;
00993       }
00994     }
00995     return OperationPlanState(y, x.end, x.quantity);
00996   }
00997   else
00998     throw LogicException(
00999       "Updating a routing operationplan without start or end date argument"
01000     );
01001 }
01002 
01003 
01004 DECLARE_EXPORT bool OperationRouting::extraInstantiate(OperationPlan* o)
01005 {
01006   // Create step suboperationplans if they don't exist yet.
01007   if (!o->lastsubopplan || o->lastsubopplan->getOperation() == OperationSetup::setupoperation)
01008   {
01009     Date d = o->getDates().getEnd();
01010     OperationPlan *p = NULL;
01011     // @todo not possible to initialize a routing oplan based on a start date
01012     if (d != Date::infiniteFuture)
01013     {
01014       // Using the end date
01015       for (Operation::Operationlist::const_reverse_iterator e =
01016             getSubOperations().rbegin(); e != getSubOperations().rend(); ++e)
01017       {
01018         p = (*e)->createOperationPlan(o->getQuantity(), Date::infinitePast,
01019             d, NULL, o, 0, true);
01020         d = p->getDates().getStart();
01021       }
01022     }
01023     else
01024     {
01025       // Using the start date when there is no end date
01026       d = o->getDates().getStart();
01027       // Using the current date when both the start and end date are missing
01028       if (!d) d = Plan::instance().getCurrent();
01029       for (Operation::Operationlist::const_iterator e =
01030             getSubOperations().begin(); e != getSubOperations().end(); ++e)
01031       {
01032         p = (*e)->createOperationPlan(o->getQuantity(), d,
01033             Date::infinitePast, NULL, o, 0, true);
01034         d = p->getDates().getEnd();
01035       }
01036     }
01037   }
01038   return true;
01039 }
01040 
01041 
01042 DECLARE_EXPORT SearchMode decodeSearchMode(const string& c)
01043 {
01044   if (c == "PRIORITY") return PRIORITY;
01045   if (c == "MINCOST") return MINCOST;
01046   if (c == "MINPENALTY") return MINPENALTY;
01047   if (c == "MINCOSTPENALTY") return MINCOSTPENALTY;
01048   throw DataException("Invalid search mode " + c);
01049 }
01050 
01051 
01052 DECLARE_EXPORT void OperationAlternate::addAlternate
01053   (Operation* o, int prio, DateRange eff)
01054 {
01055   if (!o) return;
01056   Operationlist::iterator altIter = alternates.begin();
01057   alternatePropertyList::iterator propIter = alternateProperties.begin();
01058   while (altIter!=alternates.end() && prio >= propIter->first)
01059   {
01060     ++propIter;
01061     ++altIter;
01062   }
01063   alternateProperties.insert(propIter,alternateProperty(prio,eff));
01064   alternates.insert(altIter,o);
01065   o->addSuperOperation(this);
01066 }
01067 
01068 
01069 DECLARE_EXPORT const OperationAlternate::alternateProperty&
01070   OperationAlternate::getProperties(Operation* o) const
01071 {
01072   if (!o)
01073     throw LogicException("Null pointer passed when searching for a \
01074         suboperation of alternate operation '" + getName() + "'");
01075   Operationlist::const_iterator altIter = alternates.begin();
01076   alternatePropertyList::const_iterator propIter = alternateProperties.begin();
01077   while (altIter!=alternates.end() && *altIter != o)
01078   {
01079     ++propIter;
01080     ++altIter;
01081   }
01082   if (*altIter == o) return *propIter;
01083   throw DataException("Operation '" + o->getName() +
01084       "' isn't a suboperation of alternate operation '" + getName() + "'");
01085 }
01086 
01087 
01088 DECLARE_EXPORT void OperationAlternate::setPriority(Operation* o, int f)
01089 {
01090   if (!o) return;
01091   Operationlist::const_iterator altIter = alternates.begin();
01092   alternatePropertyList::iterator propIter = alternateProperties.begin();
01093   while (altIter!=alternates.end() && *altIter != o)
01094   {
01095     ++propIter;
01096     ++altIter;
01097   }
01098   if (*altIter == o)
01099     propIter->first = f;
01100   else
01101     throw DataException("Operation '" + o->getName() +
01102         "' isn't a suboperation of alternate operation '" + getName() + "'");
01103 }
01104 
01105 
01106 DECLARE_EXPORT void OperationAlternate::setEffective(Operation* o, DateRange dr)
01107 {
01108   if (!o) return;
01109   Operationlist::const_iterator altIter = alternates.begin();
01110   alternatePropertyList::iterator propIter = alternateProperties.begin();
01111   while (altIter!=alternates.end() && *altIter != o)
01112   {
01113     ++propIter;
01114     ++altIter;
01115   }
01116   if (*altIter == o)
01117     propIter->second = dr;
01118   else
01119     throw DataException("Operation '" + o->getName() +
01120         "' isn't a suboperation of alternate operation '" + getName() + "'");
01121 }
01122 
01123 
01124 DECLARE_EXPORT void OperationAlternate::writeElement
01125 (XMLOutput *o, const Keyword& tag, mode m) const
01126 {
01127   // Writing a reference
01128   if (m == REFERENCE)
01129   {
01130     o->writeElement
01131       (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
01132     return;
01133   }
01134 
01135   // Write the complete object
01136   if (m != NOHEADER) o->BeginObject
01137     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
01138 
01139   // Write the standard fields
01140   Operation::writeElement(o, tag, NOHEADER);
01141   if (search != PRIORITY)
01142     o->writeElement(Tags::tag_search, search);
01143 
01144   // Write the extra fields
01145   o->BeginObject(Tags::tag_alternates);
01146   alternatePropertyList::const_iterator propIter = alternateProperties.begin();
01147   for (Operationlist::const_iterator i = alternates.begin();
01148       i != alternates.end(); ++i)
01149   {
01150     o->BeginObject(Tags::tag_alternate);
01151     o->writeElement(Tags::tag_operation, *i, REFERENCE);
01152     o->writeElement(Tags::tag_priority, propIter->first);
01153     if (propIter->second.getStart() != Date::infinitePast)
01154       o->writeElement(Tags::tag_effective_start, propIter->second.getStart());
01155     if (propIter->second.getEnd() != Date::infiniteFuture)
01156       o->writeElement(Tags::tag_effective_end, propIter->second.getEnd());
01157     o->EndObject (Tags::tag_alternate);
01158     ++propIter;
01159   }
01160   o->EndObject(Tags::tag_alternates);
01161 
01162   // Ending tag
01163   o->EndObject(tag);
01164 }
01165 
01166 
01167 DECLARE_EXPORT void OperationAlternate::beginElement(XMLInput& pIn, const Attribute& pAttr)
01168 {
01169   if (pAttr.isA(Tags::tag_operation))
01170     pIn.readto( Operation::reader(Operation::metadata,pIn.getAttributes()) );
01171   else
01172     Operation::beginElement(pIn, pAttr);
01173 }
01174 
01175 
01176 DECLARE_EXPORT void OperationAlternate::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
01177 {
01178   // Saving some typing...
01179   typedef pair<Operation*,alternateProperty> tempData;
01180 
01181   // Create a temporary object
01182   if (!pIn.getUserArea())
01183     pIn.setUserArea(new tempData(static_cast<Operation*>(NULL),alternateProperty(1,DateRange())));
01184   tempData* tmp = static_cast<tempData*>(pIn.getUserArea());
01185 
01186   if (pAttr.isA(Tags::tag_alternate))
01187   {
01188     addAlternate(tmp->first, tmp->second.first, tmp->second.second);
01189     // Reset the defaults
01190     tmp->first = NULL;
01191     tmp->second.first = 1;
01192     tmp->second.second = DateRange();
01193   }
01194   else if (pAttr.isA(Tags::tag_priority))
01195     tmp->second.first = pElement.getInt();
01196   else if (pAttr.isA(Tags::tag_search))
01197     setSearch(pElement.getString());
01198   else if (pAttr.isA(Tags::tag_effective_start))
01199     tmp->second.second.setStart(pElement.getDate());
01200   else if (pAttr.isA(Tags::tag_effective_end))
01201     tmp->second.second.setEnd(pElement.getDate());
01202   else if (pAttr.isA(Tags::tag_operation)
01203       && pIn.getParentElement().first.isA(Tags::tag_alternate))
01204   {
01205     Operation * b = dynamic_cast<Operation*>(pIn.getPreviousObject());
01206     if (b) tmp->first = b;
01207     else throw LogicException("Incorrect object type during read operation");
01208   }
01209   Operation::endElement (pIn, pAttr, pElement);
01210 
01211   // Delete the temporary object
01212   if (pIn.isObjectEnd()) delete static_cast<tempData*>(pIn.getUserArea());
01213 }
01214 
01215 
01216 DECLARE_EXPORT OperationPlanState
01217 OperationAlternate::setOperationPlanParameters
01218   (OperationPlan* opplan, double q, Date s, Date e, bool preferEnd,
01219   bool execute) const
01220 {
01221   // Invalid calls to this function
01222   if (!opplan || q<0)
01223     throw LogicException("Incorrect parameters for alternate operationplan");
01224   if (opplan->getLocked())
01225     return OperationPlanState(opplan);
01226 
01227   OperationPlan *x = opplan->lastsubopplan;
01228   while (x && x->getOperation() == OperationSetup::setupoperation) 
01229     x = x->prevsubopplan;
01230   if (!x)
01231   {
01232     // Blindly accept the parameters if there is no suboperationplan
01233     if (execute)
01234     {
01235       opplan->setQuantity(q,false,false);
01236       opplan->setStartAndEnd(s, e);
01237       return OperationPlanState(opplan);
01238     }
01239     else
01240       return OperationPlanState(s, e, opplan->setQuantity(q,false,false,false));
01241   }
01242   else
01243     // Pass the call to the sub-operation
01244     return x->getOperation()
01245       ->setOperationPlanParameters(x,q,s,e,preferEnd, execute);
01246 }
01247 
01248 
01249 DECLARE_EXPORT bool OperationAlternate::extraInstantiate(OperationPlan* o)
01250 {
01251   // Create a suboperationplan if one doesn't exist yet.
01252   // We use the first effective alternate by default.
01253   if (!o->lastsubopplan || o->lastsubopplan->getOperation() == OperationSetup::setupoperation)
01254   {
01255     // Find the right operation
01256     Operationlist::const_iterator altIter = getSubOperations().begin();
01257     for (; altIter != getSubOperations().end(); )
01258     {
01259       const OperationAlternate::alternateProperty& props = getProperties(*altIter);
01260       // Filter out alternates that are not suitable
01261       if (props.first != 0.0 && props.second.within(o->getDates().getEnd()))
01262         break;
01263     }
01264     if (altIter != getSubOperations().end())
01265       // Create an operationplan instance
01266       (*altIter)->createOperationPlan(
01267         o->getQuantity(), o->getDates().getStart(),
01268         o->getDates().getEnd(), NULL, o, 0, true);
01269   }
01270   return true;
01271 }
01272 
01273 
01274 DECLARE_EXPORT void OperationAlternate::removeSubOperation(Operation *o)
01275 {
01276   Operationlist::iterator altIter = alternates.begin();
01277   alternatePropertyList::iterator propIter = alternateProperties.begin();
01278   while (altIter!=alternates.end() && *altIter != o)
01279   {
01280     ++propIter;
01281     ++altIter;
01282   }
01283   if (*altIter == o)
01284   {
01285     alternates.erase(altIter);
01286     alternateProperties.erase(propIter);
01287     o->superoplist.remove(this);
01288     setChanged();
01289   }
01290   else
01291     logger << "Warning: operation '" << *o
01292     << "' isn't a suboperation of alternate operation '" << *this
01293     << "'" << endl;
01294 }
01295 
01296 
01297 DECLARE_EXPORT OperationPlanState OperationSetup::setOperationPlanParameters
01298 (OperationPlan* opplan, double q, Date s, Date e, bool preferEnd, bool execute) const
01299 {
01300   // Find or create a loadplan
01301   OperationPlan::LoadPlanIterator i = opplan->beginLoadPlans();
01302   LoadPlan *ldplan = NULL;
01303   if (i != opplan->endLoadPlans())
01304     // Already exists
01305     ldplan = &*i;
01306   else
01307   {
01308     // Create a new one
01309     if (!opplan->getOwner())
01310       throw LogicException("Setup operationplan always must have an owner");
01311     for (loadlist::const_iterator g=opplan->getOwner()->getOperation()->getLoads().begin(); 
01312       g!=opplan->getOwner()->getOperation()->getLoads().end(); ++g)
01313       if (g->getResource()->getSetupMatrix() && !g->getSetup().empty())
01314       {
01315         ldplan = new LoadPlan(opplan, &*g);
01316         break;
01317       }
01318     if (!ldplan)
01319       throw LogicException("Can't find a setup on operation '" 
01320         + opplan->getOwner()->getOperation()->getName() + "'");
01321   }
01322 
01323   // Find the setup of the resource at the start of the conversion
01324   const Load* lastld = NULL;
01325   Date boundary = s ? s : e;
01326   if (ldplan->getDate() < boundary)
01327   {
01328     for (TimeLine<LoadPlan>::const_iterator i = ldplan->getResource()->getLoadPlans().begin(ldplan);
01329       i != ldplan->getResource()->getLoadPlans().end() && i->getDate() <= boundary; ++i)
01330     {
01331       const LoadPlan *l = dynamic_cast<const LoadPlan*>(&*i);
01332       if (l && i->getQuantity() != 0.0 
01333         && l->getOperationPlan() != opplan
01334         && l->getOperationPlan() != opplan->getOwner()
01335         && !l->getLoad()->getSetup().empty())
01336           lastld = l->getLoad();
01337     }
01338   }
01339   if (!lastld)
01340   {
01341     for (TimeLine<LoadPlan>::const_iterator i = ldplan->getResource()->getLoadPlans().begin(ldplan);
01342       i != ldplan->getResource()->getLoadPlans().end(); --i)
01343     {
01344       const LoadPlan *l = dynamic_cast<const LoadPlan*>(&*i);
01345       if (l && i->getQuantity() != 0.0 
01346         && l->getOperationPlan() != opplan
01347         && l->getOperationPlan() != opplan->getOwner()
01348         && !l->getLoad()->getSetup().empty()
01349         && l->getDate() <= boundary)
01350         {
01351           lastld = l->getLoad();
01352           break;
01353         }
01354     }
01355   }
01356   string lastsetup = lastld ? lastld->getSetup() : ldplan->getResource()->getSetup();
01357 
01358   TimePeriod duration(0L);
01359   if (lastsetup != ldplan->getLoad()->getSetup())
01360   {
01361     // Calculate the setup time
01362     SetupMatrix::Rule *conversionrule = ldplan->getLoad()->getResource()->getSetupMatrix()
01363       ->calculateSetup(lastsetup, ldplan->getLoad()->getSetup());
01364     duration = conversionrule ? conversionrule->getDuration() : TimePeriod(365L*86400L);
01365   }
01366 
01367   // Set the start and end date.
01368   DateRange x;
01369   TimePeriod actualduration;
01370   if (e && s)
01371   {
01372     if (preferEnd) x = calculateOperationTime(e, duration, false, &actualduration);
01373     else x = calculateOperationTime(s, duration, true, &actualduration);
01374   }
01375   else if (s) x = calculateOperationTime(s, duration, true, &actualduration);
01376   else x = calculateOperationTime(e, duration, false, &actualduration);
01377   if (!execute)
01378     // Simulation only
01379     return OperationPlanState(x, actualduration == duration ? q : 0);
01380   else if (actualduration == duration)
01381   {
01382     // Update succeeded
01383     opplan->setStartAndEnd(x.getStart(), x.getEnd());
01384     if (opplan->getOwner()->getDates().getStart() != opplan->getDates().getEnd())
01385       opplan->getOwner()->setStart(opplan->getDates().getEnd());
01386   }
01387   else
01388     // Update failed - Not enough available time @todo setting the qty to 0 is not enough
01389     opplan->setQuantity(0);
01390 
01391   return OperationPlanState(opplan);
01392 }
01393 
01394 
01395 DECLARE_EXPORT PyObject* Operation::getattro(const Attribute& attr)
01396 {
01397   if (attr.isA(Tags::tag_name))
01398     return PythonObject(getName());
01399   if (attr.isA(Tags::tag_description))
01400     return PythonObject(getDescription());
01401   if (attr.isA(Tags::tag_category))
01402     return PythonObject(getCategory());
01403   if (attr.isA(Tags::tag_subcategory))
01404     return PythonObject(getSubCategory());
01405   if (attr.isA(Tags::tag_location))
01406     return PythonObject(getLocation());
01407   if (attr.isA(Tags::tag_fence))
01408     return PythonObject(getFence());
01409   if (attr.isA(Tags::tag_size_minimum))
01410     return PythonObject(getSizeMinimum());
01411   if (attr.isA(Tags::tag_size_multiple))
01412     return PythonObject(getSizeMultiple());
01413   if (attr.isA(Tags::tag_size_maximum))
01414     return PythonObject(getSizeMaximum());
01415   if (attr.isA(Tags::tag_cost))
01416     return PythonObject(getCost());
01417   if (attr.isA(Tags::tag_pretime))
01418     return PythonObject(getPreTime());
01419   if (attr.isA(Tags::tag_posttime))
01420     return PythonObject(getPostTime());
01421   if (attr.isA(Tags::tag_hidden))
01422     return PythonObject(getHidden());
01423   if (attr.isA(Tags::tag_loads))
01424     return new LoadIterator(this);
01425   if (attr.isA(Tags::tag_flows))
01426     return new FlowIterator(this);
01427   if (attr.isA(Tags::tag_operationplans))
01428     return new OperationPlanIterator(this);
01429   if (attr.isA(Tags::tag_level))
01430     return PythonObject(getLevel());
01431   if (attr.isA(Tags::tag_cluster))
01432     return PythonObject(getCluster());
01433   return NULL;
01434 }
01435 
01436 
01437 DECLARE_EXPORT int Operation::setattro(const Attribute& attr, const PythonObject& field)
01438 {
01439   if (attr.isA(Tags::tag_name))
01440     setName(field.getString());
01441   else if (attr.isA(Tags::tag_description))
01442     setDescription(field.getString());
01443   else if (attr.isA(Tags::tag_category))
01444     setCategory(field.getString());
01445   else if (attr.isA(Tags::tag_subcategory))
01446     setSubCategory(field.getString());
01447   else if (attr.isA(Tags::tag_location))
01448   {
01449     if (!field.check(Location::metadata))
01450     {
01451       PyErr_SetString(PythonDataException, "buffer location must be of type location");
01452       return -1;
01453     }
01454     Location* y = static_cast<Location*>(static_cast<PyObject*>(field));
01455     setLocation(y);
01456   }
01457   else if (attr.isA(Tags::tag_fence))
01458     setFence(field.getTimeperiod());
01459   else if (attr.isA(Tags::tag_size_minimum))
01460     setSizeMinimum(field.getDouble());
01461   else if (attr.isA(Tags::tag_size_multiple))
01462     setSizeMultiple(field.getDouble());
01463   else if (attr.isA(Tags::tag_size_maximum))
01464     setSizeMaximum(field.getDouble());
01465   else if (attr.isA(Tags::tag_cost))
01466     setCost(field.getDouble());
01467   else if (attr.isA(Tags::tag_pretime))
01468     setPreTime(field.getTimeperiod());
01469   else if (attr.isA(Tags::tag_posttime))
01470     setPostTime(field.getTimeperiod());
01471   else if (attr.isA(Tags::tag_hidden))
01472     setHidden(field.getBool());
01473   else
01474     return -1;  // Error
01475   return 0;  // OK
01476 }
01477 
01478 
01479 DECLARE_EXPORT PyObject* OperationFixedTime::getattro(const Attribute& attr)
01480 {
01481   if (attr.isA(Tags::tag_duration))
01482     return PythonObject(getDuration());
01483   return Operation::getattro(attr);
01484 }
01485 
01486 
01487 DECLARE_EXPORT int OperationFixedTime::setattro(const Attribute& attr, const PythonObject& field)
01488 {
01489   if (attr.isA(Tags::tag_duration))
01490     setDuration(field.getTimeperiod());
01491   else
01492     return Operation::setattro(attr, field);
01493   return 0;
01494 }
01495 
01496 
01497 DECLARE_EXPORT PyObject* OperationTimePer::getattro(const Attribute& attr)
01498 {
01499   if (attr.isA(Tags::tag_duration))
01500     return PythonObject(getDuration());
01501   if (attr.isA(Tags::tag_duration))
01502     return PythonObject(getDurationPer());
01503   return Operation::getattro(attr);
01504 }
01505 
01506 
01507 DECLARE_EXPORT int OperationTimePer::setattro(const Attribute& attr, const PythonObject& field)
01508 {
01509   if (attr.isA(Tags::tag_duration))
01510     setDuration(field.getTimeperiod());
01511   else if (attr.isA(Tags::tag_duration_per))
01512     setDurationPer(field.getTimeperiod());
01513   else
01514     return Operation::setattro(attr, field);
01515   return 0;
01516 }
01517 
01518 
01519 DECLARE_EXPORT PyObject* OperationAlternate::getattro(const Attribute& attr)
01520 {
01521   if (attr.isA(Tags::tag_alternates))
01522   {
01523     PyObject* result = PyTuple_New(getSubOperations().size());
01524     int count = 0;
01525     for (Operation::Operationlist::const_iterator i = getSubOperations().begin(); i != getSubOperations().end(); ++i)
01526       PyTuple_SetItem(result, count++, PythonObject(*i));
01527     return result;
01528   }
01529   if (attr.isA(Tags::tag_search))
01530   {
01531     ostringstream ch;
01532     ch << getSearch();
01533     return PythonObject(ch.str());
01534   }
01535   return Operation::getattro(attr);
01536 }
01537 
01538 
01539 DECLARE_EXPORT int OperationAlternate::setattro(const Attribute& attr, const PythonObject& field)
01540 {
01541   if (attr.isA(Tags::tag_search))
01542     setSearch(field.getString());
01543   else
01544     return Operation::setattro(attr, field);
01545   return 0;
01546 }
01547 
01548 
01549 DECLARE_EXPORT PyObject* OperationAlternate::addAlternate(PyObject* self, PyObject* args, PyObject* kwdict)
01550 {
01551   try
01552   {
01553     // Pick up the alternate operation
01554     OperationAlternate *altoper = static_cast<OperationAlternate*>(self);
01555     if (!altoper) throw LogicException("Can't add alternates to NULL alternate");
01556 
01557     // Parse the arguments
01558     PyObject *oper = NULL;
01559     int prio = 1;
01560     PyObject *eff_start = NULL;
01561     PyObject *eff_end = NULL;
01562     static const char *kwlist[] = {"operation", "priority", "effective_start", "effective_end", NULL};
01563     if (!PyArg_ParseTupleAndKeywords(args, kwdict,
01564       "O|iOO:addAlternate",
01565       const_cast<char**>(kwlist), &oper, &prio, &eff_start, &eff_end))
01566         return NULL;
01567     if (!PyObject_TypeCheck(oper, Operation::metadata->pythonClass))
01568       throw DataException("alternate operation must be of type operation");
01569     DateRange eff;
01570     if (eff_start)
01571     {
01572       PythonObject d(eff_start);
01573       eff.setStart(d.getDate());
01574     }
01575     if (eff_end)
01576     {
01577       PythonObject d(eff_end);
01578       eff.setEnd(d.getDate());
01579     }
01580 
01581     // Add the alternate
01582     altoper->addAlternate(static_cast<Operation*>(oper), prio, eff);
01583  }
01584   catch(...)
01585   {
01586     PythonType::evalException();
01587     return NULL;
01588   }
01589   return Py_BuildValue("");
01590 }
01591 
01592 
01593 DECLARE_EXPORT PyObject* OperationRouting::getattro(const Attribute& attr)
01594 {
01595   if (attr.isA(Tags::tag_steps))
01596   {
01597     PyObject* result = PyTuple_New(getSubOperations().size());
01598     int count = 0;
01599     for (Operation::Operationlist::const_iterator i = getSubOperations().begin(); i != getSubOperations().end(); ++i)
01600       PyTuple_SetItem(result, count++, PythonObject(*i));
01601     return result;
01602   }
01603   return Operation::getattro(attr);
01604 }
01605 
01606 
01607 PyObject *OperationRouting::addStep(PyObject *self, PyObject *args)
01608 {
01609   try
01610   {
01611     // Pick up the routing operation
01612     OperationRouting *oper = static_cast<OperationRouting*>(self);
01613     if (!oper) throw LogicException("Can't add steps to NULL routing");
01614 
01615     // Parse the arguments
01616     PyObject *steps[4];
01617     for (unsigned int i=0; i<4; ++i) steps[i] = NULL;
01618     if (PyArg_UnpackTuple(args, "addStep", 1, 4, &steps[0], &steps[1], &steps[2], &steps[3]))
01619       for (unsigned int i=0; i<4 && steps[i]; ++i)
01620       {
01621         if (!PyObject_TypeCheck(steps[i], Operation::metadata->pythonClass))
01622           throw DataException("routing steps must be of type operation");
01623         oper->addStepBack(static_cast<Operation*>(steps[i]));
01624       }
01625   }
01626   catch(...)
01627   {
01628     PythonType::evalException();
01629     return NULL;
01630   }
01631   return Py_BuildValue("");
01632 }
01633 
01634 } // end namespace

Documentation generated for frePPLe by  doxygen