operationplan.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.8.0/src/model/operationplan.cpp $
00003   version : $LastChangedRevision: 1176 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2010-02-14 22:27:13 +0100 (Sun, 14 Feb 2010) $
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., 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 DECLARE_EXPORT const MetaClass* OperationPlan::metadata;
00035 DECLARE_EXPORT const MetaCategory* OperationPlan::metacategory;
00036 DECLARE_EXPORT unsigned long OperationPlan::counter = 1;
00037 
00038 
00039 int OperationPlan::initialize()
00040 {
00041   // Initialize the metadata
00042   OperationPlan::metacategory = new MetaCategory("operationplan", "operationplans",
00043     OperationPlan::createOperationPlan, OperationPlan::writer);
00044   OperationPlan::metadata = new MetaClass("operationplan", "operationplan");
00045 
00046   // Initialize the Python type
00047   PythonType& x = FreppleCategory<OperationPlan>::getType();
00048   x.setName("operationplan");
00049   x.setDoc("frePPLe operationplan");
00050   x.supportgetattro();
00051   x.supportsetattro();
00052   x.supportstr();
00053   x.supportcreate(create);
00054   x.addMethod("toXML", toXML, METH_VARARGS, "return a XML representation");
00055   const_cast<MetaClass*>(metadata)->pythonClass = x.type_object();
00056   return x.typeReady();
00057 }
00058 
00059 
00060 void DECLARE_EXPORT OperationPlan::setChanged(bool b)
00061 {
00062   if (owner)
00063     owner->setChanged(b);
00064   else
00065   {
00066     oper->setChanged(b);
00067     if (dmd) dmd->setChanged();
00068   }
00069 }
00070 
00071 
00072 DECLARE_EXPORT Object* OperationPlan::createOperationPlan
00073 (const MetaClass* cat, const AttributeList& in)
00074 {
00075   // Pick up the action attribute
00076   Action action = MetaClass::decodeAction(in);
00077 
00078   // Decode the attributes
00079   const DataElement* opnameElement = in.get(Tags::tag_operation);
00080   if (!*opnameElement && action!=REMOVE)
00081     throw DataException("Missing operation attribute");
00082   string opname = *opnameElement ? opnameElement->getString() : "";
00083 
00084   // Decode the operationplan identifier
00085   unsigned long id = 0;
00086   const DataElement* idfier = in.get(Tags::tag_id);
00087   if (*idfier) id = idfier->getUnsignedLong();
00088 
00089   // If an ID is specified, we look up this operation plan
00090   OperationPlan* opplan = NULL;
00091   if (idfier)
00092   {
00093     opplan = OperationPlan::findId(id);
00094     if (opplan && !opname.empty()
00095         && opplan->getOperation()->getName()==opname)
00096     {
00097       // Previous and current operations don't match.
00098       ostringstream ch;
00099       ch << "Operationplan id " << id
00100       << " defined multiple times with different operations: '"
00101       << opplan->getOperation() << "' & '" << opname << "'";
00102       throw DataException(ch.str());
00103     }
00104   }
00105 
00106   // Execute the proper action
00107   switch (action)
00108   {
00109     case REMOVE:
00110       if (opplan)
00111       {
00112         // Send out the notification to subscribers
00113         if (opplan->getType().raiseEvent(opplan, SIG_REMOVE))
00114           // Delete it
00115           delete opplan;
00116         else
00117         {
00118           // The callbacks disallowed the deletion!
00119           ostringstream ch;
00120           ch << "Can't delete operationplan with id " << id;
00121           throw DataException(ch.str());
00122         }
00123       }
00124       else
00125       {
00126         ostringstream ch;
00127         ch << "Can't find operationplan with identifier "
00128         << id << " for removal";
00129         throw DataException(ch.str());
00130       }
00131       return NULL;
00132     case ADD:
00133       if (opplan)
00134       {
00135         ostringstream ch;
00136         ch << "Operationplan with identifier " << id
00137         << " already exists and can't be added again";
00138         throw DataException(ch.str());
00139       }
00140       if (opname.empty())
00141         throw DataException
00142           ("Operation name missing for creating an operationplan");
00143       break;
00144     case CHANGE:
00145       if (!opplan)
00146       {
00147         ostringstream ch;
00148         ch << "Operationplan with identifier " << id << " doesn't exist";
00149         throw DataException(ch.str());
00150       }
00151       break;
00152     case ADD_CHANGE: ;
00153   }
00154 
00155   // Return the existing operationplan
00156   if (opplan) return opplan;
00157 
00158   // Create a new operation plan
00159   Operation* oper = Operation::find(opname);
00160   if (!oper)
00161   {
00162     // Can't create operationplan because the operation doesn't exist
00163     throw DataException("Operation '" + opname + "' doesn't exist");
00164   }
00165   else
00166   {
00167     // Create an operationplan
00168     opplan = oper->createOperationPlan(0.0,Date::infinitePast,Date::infinitePast,NULL,NULL,id,false);
00169     if (!opplan->getType().raiseEvent(opplan, SIG_ADD))
00170     {
00171       delete opplan;
00172       throw DataException("Can't create operationplan");
00173     }
00174     return opplan;
00175   }
00176 }
00177 
00178 
00179 DECLARE_EXPORT OperationPlan* OperationPlan::findId(unsigned long l)
00180 {
00181   // We are garantueed that there are no operationplans that have an id equal
00182   // or higher than the current counter. This is garantueed by the
00183   // instantiate() method.
00184   if (l >= counter) return NULL;
00185 
00186   // Loop through all operationplans.
00187   for (OperationPlan::iterator i = begin(); i != end(); ++i)
00188     if (i->id == l) return &*i;
00189 
00190   // This ID was not found
00191   return NULL;
00192 }
00193 
00194 
00195 DECLARE_EXPORT bool OperationPlan::instantiate()
00196 {
00197   // At least a valid operation pointer must exist
00198   if (!oper) throw LogicException("Initializing an invalid operationplan");
00199 
00200   // Avoid zero quantity on top-operationplans
00201   if (getQuantity() <= 0.0 && !owner)
00202   {
00203     delete this;
00204     return false;
00205   }
00206 
00207   // Call any operation specific initialisation logic
00208   if (!oper->extraInstantiate(this))
00209   {
00210     delete this;
00211     return false;
00212   }
00213 
00214   // Instantiate all suboperationplans as well
00215   for (OperationPlan::iterator x(this); x != end(); ++x)
00216     x->instantiate();
00217 
00218   // Create unique identifier
00219   // Having an identifier assigned is an important flag.
00220   // Only operation plans with an id :
00221   //   - can be linked in the global operation plan list.
00222   //   - can have problems (this results from the previous point).
00223   //   - can be linked with a demand.
00224   // These properties allow us to delete operation plans without an id faster.
00225   static Mutex onlyOne;
00226   {
00227   ScopeMutexLock l(onlyOne);  // Need to assure that ids are unique!
00228   if (id)
00229   {
00230     // An identifier was read in from input
00231     if (id < counter)
00232     {
00233       // The assigned id potentially clashes with an existing operationplan.
00234       // Check whether it clashes with existing operationplans
00235       OperationPlan* opplan = findId(id);
00236       if (opplan && opplan->getOperation()!=oper)
00237       {
00238         ostringstream ch;
00239         ch << "Operationplan id " << id
00240           << " defined multiple times with different operations: '"
00241           << opplan->getOperation() << "' & '" << oper << "'";
00242         delete this;
00243         throw DataException(ch.str());
00244       }
00245     }
00246     else
00247       // The new operationplan definately doesn't clash with existing id's.
00248       // The counter need updating to garantuee that counter is always
00249       // a safe starting point for tagging new operationplans.
00250       counter = id+1;
00251   }
00252   else
00253     // Fresh operationplan with blank id
00254     id = counter++;
00255   }
00256 
00257   // Insert into the doubly linked list of operationplans.
00258   insertInOperationplanList();
00259 
00260   // If we used the lazy creator, the flow- and loadplans have not been
00261   // created yet. We do it now...
00262   createFlowLoads();
00263 
00264   // Extra registration step if this is a delivery operation
00265   if (getDemand() && getDemand()->getDeliveryOperation() == oper)
00266     dmd->addDelivery(this);
00267 
00268   // Mark the operation to detect its problems
00269   // Note that a single operationplan thus retriggers the problem computation
00270   // for all operationplans of this operation. For models with 1) a large
00271   // number of operationplans per operation and 2) very frequent problem
00272   // detection, this could constitute a scalability problem. This combination
00273   // is expected to be unusual and rare, justifying this design choice.
00274   oper->setChanged();
00275 
00276   // The operationplan is valid
00277   return true;
00278 }
00279 
00280 
00281 DECLARE_EXPORT void OperationPlan::insertInOperationplanList()
00282 {
00283 
00284   // Check if already linked
00285   if (prev || oper->first_opplan == this) return;
00286 
00287   if (!oper->first_opplan)
00288   {
00289     // First operationplan in the list
00290     oper->first_opplan = this;
00291     oper->last_opplan = this;
00292   }
00293   else if (*this < *(oper->first_opplan))
00294   {
00295     // First in the list
00296     next = oper->first_opplan;
00297     next->prev = this;
00298     oper->first_opplan = this;
00299   }
00300   else if (*(oper->last_opplan) < *this)
00301   {
00302     // Last in the list
00303     prev = oper->last_opplan;
00304     prev->next = this;
00305     oper->last_opplan = this;
00306   }
00307   else
00308   {
00309     // Insert in the middle of the list
00310     OperationPlan *x = oper->last_opplan;
00311     OperationPlan *y = NULL;
00312     while (!(*x < *this))
00313     {
00314       y = x;
00315       x = x->prev;
00316     }
00317     next = y;
00318     prev = x;
00319     if (x) x->next = this;
00320     if (y) y->prev = this;
00321   }
00322 }
00323 
00324 
00325 DECLARE_EXPORT void OperationPlan::addSubOperationPlan(OperationPlan* o)
00326 {
00327   // Check
00328   if (!o) throw LogicException("Adding null suboperationplan");
00329 
00330   // Adding a suboperationplan that was already added
00331   if (o->owner == this)  return;
00332 
00333   // Clear the previous owner, if there is one
00334   if (o->owner) o->owner->eraseSubOperationPlan(o);
00335 
00336   // Link in the list, keeping the right ordering
00337   if (!firstsubopplan)
00338   {
00339     // First element
00340     firstsubopplan = o;
00341     lastsubopplan = o;
00342   }
00343   else if (firstsubopplan->getOperation() != OperationSetup::setupoperation)    
00344   {
00345     // New head
00346     o->nextsubopplan = firstsubopplan;
00347     firstsubopplan->prevsubopplan = o;
00348     firstsubopplan = o;
00349   }
00350   else
00351   {
00352     // Insert right after the setup operationplan
00353     OperationPlan *s = firstsubopplan->nextsubopplan;
00354     o->nextsubopplan = s;
00355     if (s) s->nextsubopplan = o;
00356     else lastsubopplan = o;
00357   }
00358 
00359   o->owner = this;
00360 
00361   // Update the flow and loadplans
00362   update();
00363 }
00364 
00365 
00366 DECLARE_EXPORT void OperationPlan::eraseSubOperationPlan(OperationPlan* o)
00367 {
00368   // Check
00369   if (!o) return;
00370 
00371   // Adding a suboperationplan that was already added
00372   if (o->owner != this)
00373     throw LogicException("Operationplan isn't a suboperationplan");
00374 
00375   // Clear owner field
00376   o->owner = NULL;
00377 
00378   // Remove from the list
00379   if (o->prevsubopplan)
00380     o->prevsubopplan->nextsubopplan = o->nextsubopplan;
00381   else
00382     firstsubopplan = o->nextsubopplan;
00383   if (o->nextsubopplan)
00384     o->nextsubopplan->prevsubopplan = o->prevsubopplan;
00385   else
00386     lastsubopplan = o->prevsubopplan;
00387 };
00388 
00389 
00390 DECLARE_EXPORT bool OperationPlan::operator < (const OperationPlan& a) const
00391 {
00392   // Different operations
00393   if (oper != a.oper)
00394     return *oper < *(a.oper);
00395 
00396   // Different start date
00397   if (dates.getStart() != a.dates.getStart())
00398     return dates.getStart() < a.dates.getStart();
00399 
00400   // Sort based on quantity
00401   return quantity >= a.quantity;
00402 }
00403 
00404 
00405 DECLARE_EXPORT void OperationPlan::createFlowLoads()
00406 {
00407   // Has been initialized already, it seems
00408   if (firstflowplan || firstloadplan) return;
00409 
00410   // Create setup suboperationplans and loadplans
00411   for (Operation::loadlist::const_iterator g=oper->getLoads().begin();
00412       g!=oper->getLoads().end(); ++g)
00413     if (!g->getAlternate())
00414     {
00415       new LoadPlan(this, &*g);
00416       if (!g->getSetup().empty() && g->getResource()->getSetupMatrix())
00417         OperationSetup::setupoperation->createOperationPlan(
00418           1, getDates().getStart(), getDates().getStart(), NULL, this);
00419     }
00420 
00421   // Create flowplans for flows that are not alternates of another one
00422   for (Operation::flowlist::const_iterator h=oper->getFlows().begin();
00423       h!=oper->getFlows().end(); ++h)
00424     if (!h->getAlternate()) new FlowPlan(this, &*h);
00425 }
00426 
00427 
00428 DECLARE_EXPORT OperationPlan::~OperationPlan()
00429 {
00430   // Initialize
00431   FlowPlanIterator e = beginFlowPlans();
00432   LoadPlanIterator f = beginLoadPlans(); 
00433   OperationPlan *x = firstsubopplan;
00434 
00435   // Reset to avoid extra updates during the destruction
00436   firstflowplan = NULL;
00437   firstloadplan = NULL;
00438   firstsubopplan = NULL;
00439   lastsubopplan = NULL;
00440 
00441   // Delete the flowplans and loadplan
00442   while (e != endFlowPlans()) delete &*(e++);
00443   while (f != endLoadPlans()) delete &*(f++);
00444 
00445   // Delete the sub operationplans
00446   while (x)
00447   {
00448     OperationPlan *y = x->nextsubopplan;
00449     x->owner = NULL; // Need to clear before destroying the suboperationplan
00450     delete x;
00451     x = y;
00452   }
00453 
00454   // Delete also the owner
00455   if (owner)
00456   {
00457     const OperationPlan* o = owner;
00458     setOwner(NULL);
00459     delete o;
00460   }
00461 
00462   // Delete from the list of deliveries
00463   if (id && dmd) dmd->removeDelivery(this);
00464 
00465   // Delete from the operationplan list
00466   if (prev)
00467     // In the middle
00468     prev->next = next;
00469   else if (oper->first_opplan == this)
00470     // First opplan in the list of this operation
00471     oper->first_opplan = next;
00472   if (next)
00473     // In the middle
00474     next->prev = prev;
00475   else if (oper->last_opplan == this)
00476     // Last opplan in the list of this operation
00477     oper->last_opplan = prev;
00478 }
00479 
00480 
00481 void DECLARE_EXPORT OperationPlan::setOwner(OperationPlan* o)
00482 {
00483   // Special case: the same owner is set twice
00484   if (owner == o) return;
00485   // Erase the previous owner if there is one
00486   if (owner) owner->eraseSubOperationPlan(this);
00487   // Register with the new owner
00488   if (o) o->addSubOperationPlan(this);
00489 }
00490 
00491 
00492 void DECLARE_EXPORT OperationPlan::setStart (Date d)
00493 {
00494   // Locked opplans don't move
00495   if (getLocked()) return;
00496 
00497   if (!lastsubopplan || lastsubopplan->getOperation() == OperationSetup::setupoperation)
00498     // No sub operationplans
00499     oper->setOperationPlanParameters(this,quantity,d,Date::infinitePast);
00500   else
00501   {
00502     // Move all sub-operationplans in an orderly fashion
00503     bool firstMove = true;
00504     for (OperationPlan* i = firstsubopplan; i; i = i->nextsubopplan)
00505     {
00506       if (i->getOperation() == OperationSetup::setupoperation) continue;
00507       if (i->getDates().getStart() < d || firstMove)
00508       {
00509         i->setStart(d);
00510         //xxx todo firstMove = false;
00511         d = i->getDates().getEnd();
00512       }
00513       else
00514         // There is sufficient slack between the suboperation plans
00515         break;
00516     }
00517   }
00518 
00519   // Update flow and loadplans
00520   update();
00521 }
00522 
00523 
00524 void DECLARE_EXPORT OperationPlan::setEnd(Date d)
00525 {
00526   // Locked opplans don't move
00527   if (getLocked()) return;
00528 
00529   if (!lastsubopplan || lastsubopplan->getOperation() == OperationSetup::setupoperation)
00530     // No sub operationplans
00531     oper->setOperationPlanParameters(this,quantity,Date::infinitePast,d);
00532   else
00533   {
00534     // Move all sub-operationplans in an orderly fashion
00535     bool firstMove = true;
00536     for (OperationPlan* i = lastsubopplan; i; i = i->prevsubopplan)
00537     {
00538       if (i->getOperation() == OperationSetup::setupoperation) break;
00539       if (i->getDates().getEnd() > d || firstMove)
00540       {
00541         i->setEnd(d);
00542         //xxx todo firstMove = false;
00543         d = i->getDates().getStart();
00544       }
00545       else
00546         // There is sufficient slack between the suboperations
00547         break;
00548     }
00549   }
00550 
00551   // Update flow and loadplans
00552   update();
00553 }
00554 
00555 
00556 DECLARE_EXPORT double OperationPlan::setQuantity (double f, bool roundDown, bool upd, bool execute)
00557 {
00558   // No impact on locked operationplans
00559   if (getLocked()) return quantity;
00560 
00561   // Invalid operationplan: the quantity must be >= 0.
00562   if (f < 0)
00563     throw DataException("Operationplans can't have negative quantities");
00564 
00565   // Setting a quantity is only allowed on a top operationplan.
00566   // One exception: on alternate operations the sizing on the sub-operations is
00567   // respected.
00568   if (owner && owner->getOperation()->getType() != *OperationAlternate::metadata)
00569     return owner->setQuantity(f,roundDown,upd,execute);
00570 
00571   // Compute the correct size for the operationplan
00572   if (f!=0.0 && getOperation()->getSizeMinimum()>0.0
00573       && f < getOperation()->getSizeMinimum())
00574   {
00575     if (roundDown)
00576     {
00577       // Smaller than the minimum quantity, rounding down means... nothing
00578       if (!execute) return 0.0;
00579       quantity = 0.0;
00580       // Update the flow and loadplans, and mark for problem detection
00581       if (upd) update();
00582       return 0.0;
00583     }
00584     f = getOperation()->getSizeMinimum();
00585   }
00586   if (f!=0.0 && f > getOperation()->getSizeMaximum())
00587   {
00588     roundDown = true; // force rounddown to stay below the limit
00589     f = getOperation()->getSizeMaximum();
00590   }
00591   if (f!=0.0 && getOperation()->getSizeMultiple()>0.0)
00592   {
00593     int mult = static_cast<int> (f / getOperation()->getSizeMultiple()
00594         + (roundDown ? 0.0 : 0.99999999));
00595     if (!execute) return mult * getOperation()->getSizeMultiple();
00596     quantity = mult * getOperation()->getSizeMultiple();
00597   }
00598   else
00599   {
00600     if (!execute) return f;
00601     quantity = f;
00602   }
00603 
00604   // Update the parent of an alternate operationplan
00605   if (execute && owner
00606     && owner->getOperation()->getType() == *OperationAlternate::metadata)
00607   {
00608     owner->quantity = quantity;
00609     if (upd) owner->resizeFlowLoadPlans();
00610   }
00611 
00612   // Apply the same size also to its children
00613   if (execute && firstsubopplan)
00614     for (OperationPlan *i = firstsubopplan; i; i = i->nextsubopplan)
00615       if (i->getOperation() != OperationSetup::setupoperation)
00616       {
00617         i->quantity = quantity;
00618         if (upd) i->resizeFlowLoadPlans();
00619       }
00620 
00621   // Update the flow and loadplans, and mark for problem detection
00622   if (upd) update();
00623   return quantity;
00624 }
00625 
00626 
00627 DECLARE_EXPORT void OperationPlan::resizeFlowLoadPlans()
00628 {
00629   // Update all flowplans
00630   for (FlowPlanIterator ee = beginFlowPlans(); ee != endFlowPlans(); ++ee)
00631     ee->update();
00632 
00633   // Update all loadplans
00634   for (LoadPlanIterator e = beginLoadPlans(); e != endLoadPlans(); ++e)
00635     e->update();
00636 
00637   // Align the end of the setup operationplan with the start of the operation
00638   if (firstsubopplan && firstsubopplan->getOperation() == OperationSetup::setupoperation
00639     && firstsubopplan->getDates().getEnd() != getDates().getStart()) 
00640     firstsubopplan->setEnd(getDates().getStart());
00641   else if (getOperation() == OperationSetup::setupoperation
00642     && getDates().getEnd() != getOwner()->getDates().getStart())
00643     getOwner()->setStart(getDates().getEnd());
00644 
00645   // Allow the operation length to be changed now that the quantity has changed
00646   // Note that we assume that the end date remains fixed. This assumption makes
00647   // sense if the operationplan was created to satisfy a demand.
00648   // It is not valid though when the purpose of the operationplan was to push
00649   // some material downstream.
00650         
00651   // Resize children 
00652   for (OperationPlan *j = firstsubopplan; j; j = j->nextsubopplan)
00653     if (j->getOperation() != OperationSetup::setupoperation)
00654     {
00655       j->quantity = quantity;
00656       j->resizeFlowLoadPlans();
00657     }
00658 
00659   // Notify the demand of the changed delivery
00660   if (dmd) dmd->setChanged();
00661 }
00662 
00663 
00664 DECLARE_EXPORT void OperationPlan::update()
00665 {
00666   if (lastsubopplan && lastsubopplan->getOperation() != OperationSetup::setupoperation)  
00667   {
00668     // Inherit the start and end date of the child operationplans
00669     OperationPlan *tmp = firstsubopplan;
00670     if (tmp->getOperation() == OperationSetup::setupoperation)
00671       tmp = tmp->nextsubopplan;
00672     dates.setStartAndEnd(
00673       tmp->getDates().getStart(),
00674       lastsubopplan->getDates().getEnd()
00675     );
00676     // If at least 1 sub-operationplan is locked, the parent must be locked
00677     flags &= ~IS_LOCKED; // Clear is_locked flag
00678     for (OperationPlan* i = firstsubopplan; i; i = i->nextsubopplan)
00679       if (i->flags & IS_LOCKED)
00680       {
00681         flags |= IS_LOCKED;  // Set is_locked flag
00682         break;
00683       }
00684   }
00685 
00686   // Update the flow and loadplans
00687   resizeFlowLoadPlans();
00688 
00689   // Notify the owner operationplan
00690   if (owner) owner->update();
00691 
00692   // Mark as changed
00693   setChanged();
00694 }
00695 
00696 
00697 DECLARE_EXPORT void OperationPlan::deleteOperationPlans(Operation* o, bool deleteLockedOpplans)
00698 {
00699   if (!o) return;
00700   for (OperationPlan *opplan = o->first_opplan; opplan; )
00701   {
00702     OperationPlan *tmp = opplan;
00703     opplan = opplan->next;
00704     // Note that the deletion of the operationplan also updates the opplan list
00705     if (deleteLockedOpplans || !tmp->getLocked()) delete tmp;
00706   }
00707 }
00708 
00709 
00710 DECLARE_EXPORT double OperationPlan::getPenalty() const
00711 {
00712   double penalty = 0;
00713   for (OperationPlan::LoadPlanIterator i = beginLoadPlans();
00714     i != endLoadPlans(); ++i)
00715     if (i->isStart() && !i->getLoad()->getSetup().empty() && i->getResource()->getSetupMatrix())
00716     {
00717       SetupMatrix::Rule *rule = i->getResource()->getSetupMatrix()
00718         ->calculateSetup(i->getSetup(false), i->getSetup(true));
00719       if (rule) penalty += rule->getCost();
00720     }
00721   return penalty;
00722 }
00723 
00724 
00725 DECLARE_EXPORT TimePeriod OperationPlan::getUnavailable() const
00726 {
00727   TimePeriod x;
00728   DateRange y = getOperation()->calculateOperationTime(dates.getStart(), dates.getEnd(), &x);
00729   return dates.getDuration() - x;
00730 }
00731 
00732 
00733 DECLARE_EXPORT void OperationPlan::writer(const MetaCategory* c, XMLOutput* o)
00734 {
00735   if (!empty())
00736   {
00737     o->BeginObject(*c->grouptag);
00738     for (iterator i=begin(); i!=end(); ++i)
00739       o->writeElement(*c->typetag, *i);
00740     o->EndObject(*c->grouptag);
00741   }
00742 }
00743 
00744 
00745 DECLARE_EXPORT void OperationPlan::writeElement(XMLOutput *o, const Keyword& tag, mode m) const
00746 {
00747   // Don't export operationplans of hidden operations
00748   if (oper->getHidden()) return;
00749 
00750   // Writing a reference
00751   if (m == REFERENCE)
00752   {
00753     o->writeElement
00754       (tag, Tags::tag_id, id, Tags::tag_operation, oper->getName());
00755     return;
00756   }
00757 
00758   if (m != NOHEADER)
00759     o->BeginObject(tag, Tags::tag_id, id, Tags::tag_operation,oper->getName());
00760 
00761   // The demand reference is only valid for delivery operationplans,
00762   // and it should only be written if this tag is not being written
00763   // as part of a demand+delivery tag.
00764   if (dmd && !dynamic_cast<Demand*>(o->getPreviousObject()))
00765     o->writeElement(Tags::tag_demand, dmd);
00766 
00767   o->writeElement(Tags::tag_start, dates.getStart());
00768   o->writeElement(Tags::tag_end, dates.getEnd());
00769   o->writeElement(Tags::tag_quantity, quantity);
00770   if (getLocked()) o->writeElement (Tags::tag_locked, getLocked());
00771   o->writeElement(Tags::tag_owner, owner);
00772 
00773   // Write out the flowplans and their pegging
00774   if (o->getContentType() == XMLOutput::PLANDETAIL)
00775   {
00776     o->BeginObject(Tags::tag_flowplans);
00777     for (FlowPlanIterator qq = beginFlowPlans(); qq != endFlowPlans(); ++qq)
00778       qq->writeElement(o, Tags::tag_flowplan);
00779     o->EndObject(Tags::tag_flowplans);
00780   }
00781 
00782   o->EndObject(tag);
00783 }
00784 
00785 
00786 DECLARE_EXPORT void OperationPlan::beginElement(XMLInput& pIn, const Attribute& pAttr)
00787 {
00788   if (pAttr.isA (Tags::tag_demand))
00789     pIn.readto( Demand::reader(Demand::metadata,pIn.getAttributes()) );
00790   else if (pAttr.isA(Tags::tag_owner))
00791     pIn.readto(createOperationPlan(metadata,pIn.getAttributes()));
00792   else if (pAttr.isA(Tags::tag_flowplans))
00793     pIn.IgnoreElement();
00794 }
00795 
00796 
00797 DECLARE_EXPORT void OperationPlan::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00798 {
00799   // Note that the fields have been ordered more or less in the order
00800   // of their expected frequency.
00801   // Note that id and operation are handled already during the
00802   // operationplan creation. They don't need to be handled here...
00803   if (pAttr.isA(Tags::tag_quantity))
00804     pElement >> quantity;
00805   else if (pAttr.isA(Tags::tag_start))
00806     dates.setStart(pElement.getDate());
00807   else if (pAttr.isA(Tags::tag_end))
00808     dates.setEnd(pElement.getDate());
00809   else if (pAttr.isA(Tags::tag_owner) && !pIn.isObjectEnd())
00810   {
00811     OperationPlan* o = dynamic_cast<OperationPlan*>(pIn.getPreviousObject());
00812     if (o) setOwner(o);
00813   }
00814   else if (pIn.isObjectEnd())
00815   {
00816     // Initialize the operationplan
00817     if (!instantiate())
00818       // Initialization failed and the operationplan is deleted
00819       pIn.invalidateCurrentObject();
00820   }
00821   else if (pAttr.isA (Tags::tag_demand))
00822   {
00823     Demand * d = dynamic_cast<Demand*>(pIn.getPreviousObject());
00824     if (d) d->addDelivery(this);
00825     else throw LogicException("Incorrect object type during read operation");
00826   }
00827   else if (pAttr.isA(Tags::tag_locked))
00828     setLocked(pElement.getBool());
00829 }
00830 
00831 
00832 DECLARE_EXPORT void OperationPlan::setLocked(bool b)
00833 {
00834   if (b)
00835     flags |= IS_LOCKED;
00836   else
00837     flags &= ~IS_LOCKED;
00838   for (OperationPlan *x = firstsubopplan; x; x = x->nextsubopplan)
00839     x->setLocked(b);
00840   update();
00841 }
00842 
00843 
00844 DECLARE_EXPORT void OperationPlan::setDemand(Demand* l)
00845 {
00846   // No change
00847   if (l==dmd) return;
00848 
00849   // Unregister from previous lot
00850   if (dmd) dmd->removeDelivery(this);
00851 
00852   // Register the new demand and mark it changed
00853   dmd = l;
00854   if (l) l->setChanged();
00855 }
00856 
00857 
00858 PyObject* OperationPlan::create(PyTypeObject* pytype, PyObject* args, PyObject* kwds)
00859 {
00860   try
00861   {
00862     // Find or create the C++ object
00863     PythonAttributeList atts(kwds);
00864     Object* x = createOperationPlan(OperationPlan::metadata,atts);
00865 
00866     // Iterate over extra keywords, and set attributes.   @todo move this responsability to the readers...
00867     if (x)
00868     {
00869       PyObject *key, *value;
00870       Py_ssize_t pos = 0;
00871       while (PyDict_Next(kwds, &pos, &key, &value))
00872       {
00873         PythonObject field(value);
00874         Attribute attr(PyString_AsString(key));
00875         if (!attr.isA(Tags::tag_operation) && !attr.isA(Tags::tag_id) && !attr.isA(Tags::tag_action))
00876         {
00877           int result = x->setattro(attr, field);
00878           if (result && !PyErr_Occurred())
00879             PyErr_Format(PyExc_AttributeError,
00880               "attribute '%s' on '%s' can't be updated",
00881               PyString_AsString(key), x->ob_type->tp_name);
00882         }
00883       };
00884     }
00885 
00886     if (x && !static_cast<OperationPlan*>(x)->instantiate())
00887       return NULL;
00888     return x;
00889   }
00890   catch (...)
00891   {
00892     PythonType::evalException();
00893     return NULL;
00894   }
00895 }
00896 
00897 
00898 DECLARE_EXPORT PyObject* OperationPlan::getattro(const Attribute& attr)
00899 {
00900   if (attr.isA(Tags::tag_id))
00901     return PythonObject(getIdentifier());
00902   if (attr.isA(Tags::tag_operation))
00903     return PythonObject(getOperation());
00904   if (attr.isA(Tags::tag_flowplans))
00905     return new frepple::FlowPlanIterator(this);
00906   if (attr.isA(Tags::tag_loadplans))
00907     return new frepple::LoadPlanIterator(this);
00908   if (attr.isA(Tags::tag_quantity))
00909     return PythonObject(getQuantity());
00910   if (attr.isA(Tags::tag_start))
00911     return PythonObject(getDates().getStart());
00912   if (attr.isA(Tags::tag_end))
00913     return PythonObject(getDates().getEnd());
00914   if (attr.isA(Tags::tag_demand))
00915     return PythonObject(getDemand());
00916   if (attr.isA(Tags::tag_locked))
00917     return PythonObject(getLocked());
00918   if (attr.isA(Tags::tag_owner))
00919     return PythonObject(getOwner());
00920   if (attr.isA(Tags::tag_operationplans))
00921     return new OperationPlanIterator(this);
00922   if (attr.isA(Tags::tag_hidden))
00923     return PythonObject(getHidden());
00924   if (attr.isA(Tags::tag_unavailable))
00925     return PythonObject(getUnavailable());
00926   return NULL;
00927 }
00928 
00929 
00930 DECLARE_EXPORT int OperationPlan::setattro(const Attribute& attr, const PythonObject& field)
00931 {
00932   if (attr.isA(Tags::tag_quantity))
00933     setQuantity(field.getDouble());
00934   else if (attr.isA(Tags::tag_start))
00935     setStart(field.getDate());
00936   else if (attr.isA(Tags::tag_end))
00937     setEnd(field.getDate());
00938   else if (attr.isA(Tags::tag_locked))
00939     setLocked(field.getBool());
00940   else if (attr.isA(Tags::tag_demand))
00941   {
00942     if (!field.check(Demand::metadata))
00943     {
00944       PyErr_SetString(PythonDataException, "operationplan demand must be of type demand");
00945       return -1;
00946     }
00947     Demand* y = static_cast<Demand*>(static_cast<PyObject*>(field));
00948     setDemand(y);
00949   }
00950   else if (attr.isA(Tags::tag_owner))
00951   {
00952     if (!field.check(OperationPlan::metadata))
00953     {
00954       PyErr_SetString(PythonDataException, "operationplan demand must be of type demand");
00955       return -1;
00956     }
00957     OperationPlan* y = static_cast<OperationPlan*>(static_cast<PyObject*>(field));
00958     setOwner(y);
00959   }
00960   else
00961     return -1;
00962   return 0;
00963 }
00964 
00965 } // end namespace
Generated by  doxygen 1.6.2-20100208