// // $Id: Exchange.cpp,v 1.65 2008/12/24 01:39:42 philw Exp $ // #ifndef ExchangeINCLUDED # include "Exchange.hpp" #endif #ifndef AccountMgrINCLUDED # include "AccountMgr.hpp" #endif #ifndef PaybackINCLUDED # include "Payback.hpp" #endif #ifndef SupplyINCLUDED # include "Supply.hpp" #endif #ifndef ObjListINCLUDED # include "ObjList.hpp" #endif #ifndef EditListINCLUDED # include "EditList.hpp" #endif #ifndef AccountingCBDataINCLUDED # include "AccountingCBData.hpp" #endif #ifndef libAccountingMsgsINCLUDED # include "libAccountingMsgs.hpp" #endif #ifndef SystemINCLUDED # include "System.hpp" #endif #ifndef SimWSINCLUDED # include "SimWS.hpp" #endif #ifndef RunInfoINCLUDED # include "RunInfo.hpp" #endif #ifndef TimeIntervalINCLUDED # include "TimeInterval.hpp" #endif #ifndef ContextINCLUDED # include "Context.hpp" #endif #ifndef rwErrorINCLUDED # include "rwError.hpp" #endif #ifndef cwExceptINCLUDED # include "cwExcept.hpp" #endif #include "cwiostream.hpp" #include #ifndef AccountDebugINCLUDED #include "AccountDebug.hpp" #endif #ifndef CallbackReceiverMgrINCLUDED #include "CallbackReceiverMgr.hpp" #endif // // private callback macros // // CallbackType, AccountingCBData::CallbackSubType, void * #undef exchange_callback #define exchange_callback(t, s, d) \ { \ if (numCallbacks() != 0) \ { \ ExchangeCBData _data_(this, (t), AccountingCBData::s, (d)); \ callCallbacks((t), &_data_); \ } \ } // AccountingCBData::CallbackSubType, void * #undef exchange_changed_callback #define exchange_changed_callback(s, d) \ \ exchange_callback(EXCHANGE_CHANGED, s, (d)) // // constructors and destructor // Exchange::Exchange (const RWCString &name) : // base classes Root(), EditObj(), // members _name (""), _borrowSupply (NULL), _inputBorrow (NULL), _destSupply (NULL), _paybacks (_name + " Payback"), _editList (NULL), _borrowCb (NULL), _destCb (NULL), _acctMgrCb (NULL), _sourceBalance (SourceBalance, VOLUME, NOUNITS, "", Slot::ExchangeSlotBit), _destBalance (DestBalance, VOLUME, NOUNITS, "", Slot::ExchangeSlotBit), _slotList() { static const char (*mname) ("Exchange ctor"); std::cout << mname << ": " << name << std::endl; RWCString compositeName (name); AccountMgrObj->createUniqueExchangeName (compositeName); _name = compositeName; _sourceBalance.setManagedByExchange(1); _sourceBalance.setExchange(this); _destBalance.setManagedByExchange(1); _destBalance.setExchange(this); _slotList.append (&_sourceBalance); _slotList.append (&_destBalance); addAccountMgrCallback(); } // virtual Exchange::~Exchange() { deleteAccountMgrCallback(); // issue the callback first, while the exchange is still intact. // Note: it's not really intact at this point. Polymorphism doesn't // work, for one thing. ExchangeCBData data(this, EXCHANGE_DELETED, AccountingCBData::no_subtype, NULL); callCallbacks(EXCHANGE_DELETED, &data); { // Tell the CallbackReceiverMgr about this as well. // We do this with an explicit call so that we don't // have to allocate a callback struct on every object, which // is inevitably what would happen because so many root selections // will be on "all" objects. callbackReceiverMgr.invoke(this, EXCHANGE_DELETED, (CallbackData *)(&data)); } #ifdef CWDEBUG // We should never be able to link slots on exchanges // except as supplies rwAssert(_sourceBalance.numOutLinks() == 0); rwAssert(_destBalance.numOutLinks() == 0); #endif // clear the borrow and destination supplies and supply callbacks. setBorrowSupply (NULL); setDestSupply (NULL); // delete the input borrow supply. if (_inputBorrow != NULL) { delete _inputBorrow; _inputBorrow = NULL; } _slotList.clear(); } // // EditObj: init, apply, cancel, and revert the edit // // private, virtual Exchange *Exchange::init_edit() throw (cwException) { Exchange (*copy)(NULL); // edit copy copy = new Exchange; // initialize the name. copy->_name = _name; // initialize the borrow and destination supplies. copy->_borrowSupply = _borrowSupply; copy->_destSupply = _destSupply; // initialize the payback list. _editList = _paybacks.getEditList(); Payback* payback; foreach_EditList (Payback, *_editList, payback) { payback->setExchange(copy); } copy->_editList = _editList; // initialize the slot configuration. _sourceBalance.copyConfigTo (©->_sourceBalance); copy->_slotList = _slotList; return copy; } // private, virtual void Exchange::apply_edit(Exchange *copy) throw (cwException) { // apply the name. setName(copy->_name); // apply the borrow and destination supplies. setBorrowSupply (copy->_borrowSupply); if ((_borrowSupply == NULL) && (_inputBorrow == NULL)) { // guarantee the input borrow supply name is unique. _inputBorrow = new Supply(_name + "+Input Borrow+", this); } setDestSupply(copy->_destSupply); // apply the payback list. _paybacks.applyEditList(_editList); const Payback* payback; foreach_EditObjList (Payback, _paybacks, payback) { const_cast(payback)->setExchange(this); } // update the slot dates. updateSlotDates(); // apply the slot configuration. applySlotConfig(©->_sourceBalance); _sourceBalance.setAllValues(); _destBalance.setAllValues(); exchange_changed_callback(all_changed, NULL); } // private, virtual void Exchange::cancel_edit(Exchange *copy) throw (cwException) { // cancel the payback list. _paybacks.cancelEditList(_editList); copy->_editList = NULL; } // private, virtual void Exchange::revert_edit(Exchange * /*orig*/) throw (cwException) { throw cwLogicError("Exchange::revert_edit() is not implemented."); // todo: when revert_edit() is implemented, issue an "exchange all // changed" callback (keeping in mind this is the edit copy). } // // EditObj: is-modified // // private, virtual bool Exchange::is_modified(const Exchange *orig) const { // compare the name, the borrow supply, the destination supply, // the payback list, and the slot configuration. const bool modified = (_name != orig->_name) || (_borrowSupply != orig->_borrowSupply) || (_destSupply != orig->_destSupply) || (_editList->isModified()) || !_sourceBalance.hasSameConfig(&orig->_sourceBalance); return modified; } // // EditObj: return the edit id // // private, virtual RWCString Exchange::editobj_id() const { return _name; } // // save and load // bool Exchange::save(cwostream &os) const { const Payback* payback; // set o "$mgr.exchange name" // set the variable for all subsequent exchange commands. os << "set o \"$mgr." << _name << "\"" << endl; // for each slot... // // 1. "$o" {slot type} {slot name} // the exchange creates a slot of the appropriate type and // with the appropriate name. // // 2. save the slot. // // note: the slot dump methods ought to be const, but they aren't. // the casting away of const corrects for that. os << "\"$o\" {" << _sourceBalance.getType() << "} {" << _sourceBalance.getName() << "}" << endl; ((SeriesSlot *) &_sourceBalance)->dumpEnt(os); os << "\"$o\" {" << _destBalance.getType() << "} {" << _destBalance.getName() << "}" << endl; ((SeriesSlot *) &_destBalance)->dumpEnt(os); // "$o" borrow {borrow supply name} // the exchange sets the borrow supply flag and the borrow supply. os << "\"$o\" borrow " << hasBorrowSupply() << " {" << (_borrowSupply ? _borrowSupply->getName() : RWCString("")) << "}" << endl; // "$o" input borrow {supply name} // the exchange creates the supply and sets the input borrow // supply. if (_inputBorrow != NULL) { os << "\"$o\" input borrow {" << _inputBorrow->getName() << "}" << endl; } // "$o" dest {destination supply name} // the exchange sets the destination supply flag and the // destination supply. os << "\"$o\" dest " << hasDestSupply() << " {" << (_destSupply ? _destSupply->getName() : RWCString("")) << "}" << endl; // for each payback... foreach_EditObjList (Payback, _paybacks, payback) { // "$o" payback {payback supply name} // the exchange creates a payback with the specified payback // supply. os << "\"$o\" payback {" << payback->getPaybackSupply()->getName() << "}" << endl; } // note: the input borrow supply and the paybacks must be saved // last. (they change the value of $o.) if (_inputBorrow != NULL) { if (_inputBorrow->save(os) == false) { return false; } } // for each payback... foreach_EditObjList (Payback, _paybacks, payback) { // save the payback. if (payback->save(os) == false) { return false; } } return true; } int exchangeCmd(ClientData data, Tcl_Interp *interp, int argc, char **argv) { Exchange *exchange; // exchange pointer char first; // first command character // the client data is the exchange pointer. exchange = (Exchange *) data; // // valid commands: // // borrow {borrow supply name} // input borrow {supply name} // dest {destination supply name} // payback {payback supply name} // {slot type} {slot name} // first = argv[1][0]; if (first == 'b' && strcmp(argv[1], "borrow") == 0) { // borrow {borrow supply name} return exchange->borrow_cmd(interp, argc, argv); } else if (first == 'i' && strcmp(argv[1], "input") == 0) { // input borrow {supply name} return exchange->input_cmd(interp, argc, argv); } else if (first == 'd' && strcmp(argv[1], "dest") == 0) { // dest {destination supply name} return exchange->dest_cmd(interp, argc, argv); } else if (first == 'p' && strcmp(argv[1], "payback") == 0) { // payback {payback supply name} return exchange->payback_cmd(interp, argc, argv); } else if (rwWorkspace->isValidSlotType(argv[1]) == true) { // {slot type} {slot name} return exchange->slot_cmd(interp, argc, argv); } else { // invalid command TclSetResult(EXCHANGE_CMD_INVALID, argv[0], argv[1], "borrow, dest, payback, {slot type}"); return TCL_ERROR; } // NOTREACHED } // // load commands // // private int Exchange::borrow_cmd(Tcl_Interp * /*interp*/, int argc, char **argv) { if (argc != 4) { TclSetResult(EXCHANGE_CMD_SYNTAX, argv[1], " {borrow supply name}"); return TCL_ERROR; } try { // Note: The _hasBorrowSupply field has been removed, but that value // is still supported in the Exchange serialization. To insure // that older models load properly, we must drop the Borrow Supply // reference if it was disabled with the removed flag field. // //-- REMOVED: //-- setHasBorrowSupply(toBool(atoi(argv[2]))); [No longer used] const bool hasBorrowSply (toBool(atoi(argv[2]))); if (hasBorrowSply) { setBorrowSupply(argv[3]); } } catch (const cwException &except) { TclSetResult(except.what()); return TCL_ERROR; } return TCL_OK; } // private int Exchange::dest_cmd(Tcl_Interp * /*interp*/, int argc, char **argv) { if (argc != 4) { TclSetResult(EXCHANGE_CMD_SYNTAX, argv[1], " {destination supply name}"); return TCL_ERROR; } try { // Note: The _hasDestSupply field has been removed, but that value // is still supported in the Exchange serialization. To insure // that older models load properly, we must drop the Dest Supply // reference if it was disabled with the removed flag field. // //-- REMOVED: //-- hasDestSupply(toBool(atoi(argv[2]))); [No longer used] const bool hasDestSply (toBool(atoi(argv[2]))); if (hasDestSply) { setDestSupply(argv[3]); } } catch (const cwException &except) { TclSetResult(except.what()); return TCL_ERROR; } return TCL_OK; } // private int Exchange::payback_cmd(Tcl_Interp *interp, int argc, char **argv) { if (argc != 3) { TclSetResult(EXCHANGE_CMD_SYNTAX, argv[1], "{payback supply name}"); return TCL_ERROR; } Payback *payback; // payback pointer try { payback = addPayback(argv[2]); } catch (const cwException &except) { TclSetResult(except.what()); return TCL_ERROR; } // create the tcl command for the payback. payback->createTclCmd(interp, argv[0]); return TCL_OK; } // private int Exchange::input_cmd(Tcl_Interp *interp, int argc, char **argv) { if (argc != 4 || strcmp(argv[2], "borrow") != 0) { TclSetResult(EXCHANGE_CMD_SYNTAX, argv[1], " {input supply name}"); return TCL_ERROR; } try { // create the input supply. Supply *supply = new Supply(argv[3], this); // create the tcl command for the input supply. // "$mgr" is the account manager's tcl command. RWCString cmd(Tcl_GetVar(rwInterp, "mgr", TCL_GLOBAL_ONLY)); supply->createTclCmd(interp, cmd, supply->getName()); // set the input supply. _inputBorrow = supply; } catch (const cwException &except) { TclSetResult(except.what()); return TCL_ERROR; } return TCL_OK; } // private int Exchange::slot_cmd(Tcl_Interp *interp, int argc, char **argv) { Slot *slot; // slot pointer if (argc != 3) { TclSetResult(EXCHANGE_CMD_SYNTAX, argv[1], "{slot name}"); return TCL_ERROR; } // get the slot. slot = getSlot(argv[2]); // todo: handle null (old) slots in a better way. if (slot != NULL) { const char *cmd; // parent exchange's tcl command // create the tcl command for the slot. // "$o" is the parent exchange's tcl command. cmd = Tcl_GetVar(interp, "o", TCL_GLOBAL_ONLY); Tcl_CreateCommand( interp, (char *) (RWCString(cmd) + "." + slot->getName()).data(), slot->getTclCmd(), (ClientData) slot, NULL); } return TCL_OK; } // // tcl commands // bool Exchange::createTclCmd(Tcl_Interp *interp, const RWCString &cmd) const { // todo: check the return value. Tcl_CreateCommand(interp, (char *) (cmd + "." + _name).data(), exchangeCmd, (ClientData) this, NULL); return true; } // // callback support // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // private void Exchange::addBorrowSupplyCallback() { static const char (*mname) ("Exchange::addBorrowSupplyCallback"); // First delete the previously added callback on the borrow supply, if any. deleteBorrowSupplyCallback(); if (_borrowSupply) { std::cout << mname << " [" << _name << "] " << std::endl; _borrowCb = new MethodCb( this, &Exchange::supplyCbHandler, SUPPLY_DELETED); _borrowSupply->addCCallback (SUPPLY_DELETED, _borrowCb); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // private void Exchange::deleteBorrowSupplyCallback() { static const char (*mname) ("Exchange::deleteBorrowSupplyCallback"); if (_borrowCb) { std::cout << mname << " [" << _name << "] " << std::endl; if (_borrowSupply) { _borrowSupply->deleteCallback (_borrowCb); } _borrowCb = NULL; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // private void Exchange::addDestSupplyCallback() { static const char (*mname) ("Exchange::addDestSupplyCallback"); // First delete the previously added callback on the borrow supply, if any. deleteDestSupplyCallback(); if (_destSupply) { std::cout << mname << " [" << _name << "] " << std::endl; _destCb = new MethodCb( this, &Exchange::supplyCbHandler, SUPPLY_DELETED); _destSupply->addCCallback (SUPPLY_DELETED, _destCb); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // private void Exchange::deleteDestSupplyCallback() { static const char (*mname) ("Exchange::deleteDestSupplyCallback"); if (_destCb) { std::cout << mname << " [" << _name << "] " << std::endl; if (_destSupply) { _destSupply->deleteCallback (_destCb); } _destCb = NULL; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // private void Exchange::addAccountMgrCallback() { static const char (*mname) ("Exchange::addAccountMgrCallback"); AccountMgr* mgr = AccountMgrObj; if (mgr && (_acctMgrCb == NULL)) { std::cout << mname << " [" << _name << "] " << std::endl; _acctMgrCb = new MethodCb( this, &Exchange::accountMgrCbHandler, SUPPLY_DELETED); mgr->addCCallback (SUPPLY_DELETED, _acctMgrCb); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // private void Exchange::deleteAccountMgrCallback() { static const char (*mname) ("Exchange::deleteAccountMgrCallback"); if (_acctMgrCb) { std::cout << mname << " [" << _name << "] " << std::endl; AccountMgr* mgr = AccountMgrObj; if (mgr) { mgr->deleteCallback (_acctMgrCb); } _acctMgrCb = NULL; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // private int Exchange::supplyCbHandler (CallbackType cbType, CallbackData* cbData, void* /*clientData*/) { static const char (*mname) ("Exchange::supplyCbHandler"); std::cout << mname << " [" << _name << "] " << callbackTypeStr (cbType) << std::endl; // note: currently the only callback registered on supplies is // SUPPLY_DELETED; if this changes, this handler should // probably be decomposed into callback-specific handlers. if (rwAssert (cbType == SUPPLY_DELETED)) { // get the supply from the callback data. const Supply* supply = ((SupplyCBData*) cbData)->getSupply(); const Supply::Role role (supply->getRole()); if (role == Supply::ExchangeBorrow && rwAssert(supply == _borrowSupply)) { setBorrowSupply (NULL, true); } else if (role == Supply::ExchangeDest && rwAssert(supply == _destSupply)) { setDestSupply (NULL, true); } else if (role == Supply::ExchangePayback) { Payback (*payback)(supply->getPayback()); // remove the payback from the payback list, and delete it. _paybacks.removeObj (payback); payback->setPaybackSupply (NULL, true); delete payback; } } return (1); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // private int Exchange::accountMgrCbHandler (CallbackType cbType, CallbackData* cbData, void* /*clientData*/) { static const char (*mname) ("Exchange::accountMgrCbHandler"); std::cout << mname << " [" << _name << "] " << callbackTypeStr (cbType) << std::endl; bool abortEditNeeded (false); // tentative try { if (cbType == SUPPLY_DELETED) { SupplyCBData* splyDat = dynamic_cast (cbData); if (rwAssert (splyDat != NULL)) { const Supply* sply = splyDat->getSupply(); const Supply::Role role = (sply ? sply->getRole() : Supply::Standalone); if (sply == _borrowSupply) { // clear borrow supply and callback setBorrowSupply (NULL); abortEditNeeded = true; } else if (sply == _destSupply) { // clear destination supply and callback setDestSupply (NULL); abortEditNeeded = true; } else if ( (role == Supply::ExchangePayback) && // paybacks (_editList != NULL) ) { Payback* payback (NULL); foreach_EditList (Payback, *_editList, payback) { Supply* paybackSupply = payback->getPaybackSupply(); if (sply == paybackSupply) { // remove the payback from the payback list, // and delete it. _paybacks.removeObj (payback); payback->setPaybackSupply (NULL, true); delete payback; // Force an edit abort, if active. abortEditNeeded = true; break; } } } } } } catch (const cwException &except) { abortEditNeeded = true; } if (abortEditNeeded) { std::cout << mname << " ABORT EDIT NEEDED" << std::endl; ExchangeCBData data (this, EXCHANGE_ABORT_EDIT_NEEDED, AccountingCBData::no_subtype, NULL); callCallbacks (EXCHANGE_ABORT_EDIT_NEEDED, &data); } return (1); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // // set and return the name // void Exchange::setName(const RWCString &name) throw (cwException) { // check for no change. if (name == _name) { return; //------>> } // if this isn't an edit copy, check... if (!isEditCopy()) { // for invalid characters in the name. if (name.index(SimWorkspace::illegalCharactersRE) != RW_NPOS) { // todo: finish this. throw cwInvalidArgument("Exchange name contains " "invalid characters"); } // todo: additional error checking. } // set the name. _name = name; exchange_changed_callback(name_changed, NULL); } // // set the borrow supply by name (when loading a model file) // // private void Exchange::setBorrowSupply (const RWCString &name) throw (cwException) { Supply (*supply)(NULL); // borrow supply if (!name.isNull()) { if ((supply = (Supply *) AccountMgrObj->getSupply(name)) == NULL) { IFDEBUG(dbs) { dbs.os() << "Exchange::setBorrowSupply " << name << endl; } throw cwInvalidArgument(SUPPLY_NAME_NOEXIST, name.data()); } } setBorrowSupply (supply); } // // set and return the borrow supply // void Exchange::setBorrowSupply (Supply *supply, bool origDeleted /*=false*/) throw (cwException) { // if this is an edit copy, simply set the borrow supply and return. if (isEditCopy()) { _borrowSupply = supply; return; //----->> } // check for no change. if (supply == _borrowSupply) { return; //----->> } // check for a supply which is already part of an exchange. if ((supply != NULL) && (supply->getRole() != Supply::Standalone)) { throw cwInvalidArgument(EXCHANGE_SUPPLY_ROLE, supply->getName().data()); } deleteBorrowSupplyCallback(); // remove the original borrow supply. if (_borrowSupply != NULL) { // if the original borrow supply is being deleted, don't set // its role. if (!origDeleted) { _borrowSupply->setStandaloneRole(); } } // set the borrow supply. _borrowSupply = supply; if (_borrowSupply != NULL) { addBorrowSupplyCallback(); _borrowSupply->setExchangeBorrowRole (this); } // todo: resolve the entire time series. exchange_changed_callback (borrow_supply_changed, _borrowSupply); } // // return the borrow value at the specified timestep and offset // // todo: fix this. #include "UnitMgr.hpp" double flowToVolume(const Date_Time *when, int offset, const DeltaTime &delta, double flow) { if (!isValid(flow)) { return 0.0; } double volume; Date_Time dt(*when); if (offset != 0) { dt += DeltaTime(offset * delta.time(), delta.units()); } seconds_t secs = dt.getStepSeconds(delta,0); unitMgr->convertType(flow, 1.0, FLOW, volume, 1.0, VOLUME, secs , dt); return volume; } double volumeToFlow(const Date_Time *when, int offset, const DeltaTime &delta, double volume) { if (!isValid(volume)) { return 0.0; } double flow; Date_Time dt(*when); if (offset != 0) { dt += DeltaTime(offset * delta.time(), delta.units()); } seconds_t secs = dt.getStepSeconds(delta,0); unitMgr->convertType(volume, 1.0, VOLUME, flow, 1.0, FLOW, secs, dt); return flow; } bool Exchange::getBorrowValue(double &value, const Date_Time *when, int offset) const { bool status(false); // return status // get the borrow supply. Supply* supply = (_borrowSupply ? _borrowSupply : _inputBorrow); if (supply != NULL) { // get the borrow supply slot. SeriesSlot (*slot)(supply->getSupplySlot()); // get the slot value at the specified timestep and offset. if (slot->getValue(when, offset, value) == false) { # if 0 // return an invalid value as zero. if (!isValid(value)) { value = 0.0; } # else if (slot->getUnitType() == FLOW) { value = flowToVolume(when, offset, slot->getStep(), value); } else if (!isValid(value)) { value = 0.0; } # endif status = true; } } return status; } // // set the destination supply by name (when loading a model file) // // private void Exchange::setDestSupply (const RWCString &name) throw (cwException) { Supply (*supply) (NULL); // destination supply if (!name.isNull()) { if ((supply = (Supply*) AccountMgrObj->getSupply(name)) == NULL) { IFDEBUG(dbs) { dbs.os() << "Exchange::setDestSupply " << name << endl; } throw cwInvalidArgument(SUPPLY_NAME_NOEXIST, name.data()); } } setDestSupply (supply); } // // set and return the destination supply // void Exchange::setDestSupply(Supply *supply, bool origDeleted) throw (cwException) { // if this is an edit copy, simply set the destination supply and // return. if (isEditCopy()) { _destSupply = supply; return; //------>> } // check for no change. if (supply == _destSupply) { return; //------>> } // check for a supply which is already part of an exchange. if (supply != NULL && supply->getRole() != Supply::Standalone) { throw cwInvalidArgument(EXCHANGE_SUPPLY_ROLE, supply->getName().data()); } deleteDestSupplyCallback(); // remove the original destination supply. if (_destSupply != NULL) { // if the original destination supply is being deleted, don't // set its role. if (!origDeleted) { _destSupply->setStandaloneRole(); } } // set the destination supply. _destSupply = supply; if (_destSupply != NULL) { addDestSupplyCallback(); _destSupply->setExchangeDestRole (this); } // todo: resolve the entire time series. exchange_changed_callback(dest_supply_changed, _destSupply); } // // return the destination value at the specified timestep and // offset // bool Exchange::getDestValue(double &value, const Date_Time *when, int offset) const { bool status(false); // return status if (_destSupply != NULL) { // get the destination supply slot. SeriesSlot (*slot)(_destSupply->getSupplySlot()); // get the slot value at the specified timestep and offset. if (slot->getValue(when, offset, value) == false) { # if 0 // return an invalid value as zero. if (!isValid(value)) { value = 0.0; } # else value = flowToVolume(when, offset, slot->getStep(), value); # endif status = true; } } return status; } // // add a payback by payback supply name (when loading a model // file) Payback *Exchange::addPayback(const RWCString &name) throw (cwException) { if (name.isNull()) { // todo: finish this. throw cwInvalidArgument("The Payback name cannot be empty."); } // create the payback. Payback *payback = new Payback(this); try { payback->setPaybackSupply( (Supply *) AccountMgrObj->getSupply(name)); _paybacks.addObj(payback); } catch (const cwException &) { delete payback; throw; } exchange_changed_callback(payback_list_changed, NULL); return payback; } // // return the payback list // int Exchange::getPaybacks(PaybackList &paybacks) const { rwAssert(isOriginal()); return _paybacks.getObjList(paybacks); } // // edit the payback list // PaybackEditList *Exchange::getPaybackEditList() throw (cwException) { rwAssert(isEditCopy()); return _editList; } // // return the payback with the specified supply // const Payback *Exchange::getPayback(const Supply *supply) const { const Payback* payback(0); foreach_EditObjList (Payback, _paybacks, payback) { if (payback->getPaybackSupply() == supply) { break; } } return payback; } // // sum the payback values at the specified timestep and offset // (with or without the losses applied) // bool Exchange::sumPaybackValues(double &sum, const Date_Time *when, int offset) const { bool status(false); // return status double value; // payback value sum = 0.0; // initialize the sum const Payback* payback; foreach_EditObjList (Payback, _paybacks, payback) { if (payback->getPaybackValue(value, when, offset)) { sum += value; status = true; } } return status; } bool Exchange::sumPaybackMinusLossValues(double &sum, const Date_Time *when, int offset) const { bool status(false); // return status double value; // payback-minus-loss value sum = 0.0; // initialize the sum const Payback* payback; foreach_EditObjList (Payback, _paybacks, payback) { if (payback->getPaybackMinusLossValue(value, when, offset)) { sum += value; status = true; } } return status; } void Exchange::retrieveAllSupplies (cwSlist& retList, bool enforceUnique /*=false*/) const { // ************************************* // *** Borrow Supply or Input Supply *** // ************************************* if (_borrowSupply != NULL) { if (!enforceUnique || !retList.contains (_borrowSupply)) { retList.append (_borrowSupply); } } else { if (!enforceUnique || !retList.contains (_inputBorrow)) { if (_inputBorrow) retList.append (_inputBorrow); } } // ************************** // *** Destination Supply *** // ************************** if (_destSupply) { if (!enforceUnique || !retList.contains (_destSupply)) { retList.append (_destSupply); } } // ************************ // *** Payback Supplies *** // ************************ // for each payback ... const Payback (*payback) (NULL); foreach_EditObjList (Payback, _paybacks, payback) { Supply (*pbSply) (payback->getPaybackSupply()); if (pbSply != NULL) { if (!enforceUnique || !retList.contains (pbSply)) { retList.append (pbSply); } } } } // // expand slots to encompass the slot // // todo: fix this. static void getMinMaxDates(Date_Time &startDate, Date_Time &endDate, SeriesSlot *slot) { Date_Time start(*slot->getStartDate()); Date_Time end(*slot->getEndDate()); if (start < startDate) startDate = start; if (end > endDate) endDate = end; } // private bool Exchange::updateSlotDates() { bool status(true); // return status // todo: fix this. const Payback *payback; Date_Time startDate(*AccountMgrObj->getBeginAccountingDate()); Date_Time endDate(*AccountMgrObj->getEndAccountingDate()); // input borrow should span accounting period. if (_inputBorrow != NULL) { _inputBorrow->getSupplySlot()->spanDates(&startDate, &endDate); } // get earliest start date and latest end date of accounting // period and borrow, destination, and payback supplies. if (_borrowSupply != NULL) { getMinMaxDates(startDate, endDate, _borrowSupply->getSupplySlot()); } if (_destSupply != NULL) { getMinMaxDates(startDate, endDate, _destSupply->getSupplySlot()); } foreach_EditObjList (Payback, _paybacks, payback) { Supply (*supply)(payback->getPaybackSupply()); if (supply != NULL) { getMinMaxDates(startDate, endDate, supply->getSupplySlot()); } } // apply earliest start date and latest end date to source and // destination balances and payback debts. _sourceBalance.spanDates(&startDate, &endDate); _destBalance.spanDates(&startDate, &endDate); foreach_EditObjList (Payback, _paybacks, payback) { const_cast(payback)->getDebtSlot()->spanDates(&startDate, &endDate); } return status; } // // apply the slot configuration // // private bool Exchange::applySlotConfig(const SeriesSlot *slot) { // if the slot configuration is different... if (!slot->hasSameConfig(&_sourceBalance)) { // apply the slot configuration to the source and destination // balance slots. slot->copyConfigTo (&_sourceBalance); slot->copyConfigTo (&_destBalance); } // apply the configuration to the payback debt slots. const Payback* payback; foreach_EditObjList (Payback, _paybacks, payback) { slot->copyConfigTo(const_cast(payback)->getDebtSlot()); } return true; } // // return the unit type and standard scale and units // unit_type Exchange::getUnitType() const { // todo: fix this. return rwAssert(true/*isEditCopy()*/) ? _sourceBalance.getUnitType() : NOUNITS; } double Exchange::getStdScale() const { // todo: fix this. return rwAssert(true/*isEditCopy()*/) ? 1.0 : 0.0; } const RWCString Exchange::getStdUnits() const { // todo: fix this. return rwAssert(true/*isEditCopy()*/) ? _sourceBalance.getStdUnits() : RWCString(""); } // // set and return the user scale and units // bool Exchange::setUsrScale(double scale) { // note: this method is only called on the edit copy. if (rwAssert(isEditCopy())) { _sourceBalance.setUsrScale(scale); } return true; } bool Exchange::setUsrUnits(const RWCString &units) { // note: this method is only called on the edit copy. if (rwAssert(isEditCopy())) { _sourceBalance.setUsrUnits(units); } return true; } double Exchange::getUsrScale() const { // todo: fix this. return rwAssert(true/*isEditCopy()*/) ? _sourceBalance.getUsrScale() : 0.0; } const RWCString Exchange::getUsrUnits() const { // todo: fix this. return rwAssert(true/*isEditCopy()*/) ? _sourceBalance.getUsrUnits() : RWCString(""); } // // set and return the format and precision // bool Exchange::setUsrFormat(const RWCString &format) { // note: this method is only called on the edit copy. if (rwAssert(isEditCopy())) { _sourceBalance.setUsrFormat(format); } return true; } bool Exchange::setUsrPrecision(int precision) { // note: this method is only called on the edit copy. if (rwAssert(isEditCopy())) { _sourceBalance.setUsrPrecision(precision); } return true; } const RWCString Exchange::getUsrFormat() const { // todo: fix this. return rwAssert(true/*isEditCopy()*/) ? _sourceBalance.getUsrFormat() : RWCString(""); } int Exchange::getUsrPrecision() const { // todo: fix this. return rwAssert(true/*isEditCopy()*/) ? _sourceBalance.getUsrPrecision() : 0; } // // return the slot // Slot *Exchange::getSlot(const RWCString &name) { Slot (*slot)(NULL); // returned slot if (name == SourceBalance) { slot = &_sourceBalance; } else if (name == DestBalance) { slot = &_destBalance; } else if (name == Borrow) { if (_borrowSupply != NULL) { slot = _borrowSupply->getSupplySlot(); } else if (_inputBorrow != NULL) { slot = _inputBorrow->getSupplySlot(); } } return slot; } // Find Payback Slot given the Payback Slot name without the initial // Exchange part. That is, the input format is . Slot* Exchange::getPaybackSlot (const RWCString& name) { static const char (*mname) ("Exchange::getPaybackSlot"); // std::cout << mname << " (" << name << ")" << std::endl; // There should be a part and a part, // delimited with a period character. size_t dotPos (name.first('.')); if (dotPos == RW_NPOS) return (NULL); if (dotPos == 0) return (NULL); if (dotPos == name.length() - 1) return (NULL); //------------------------------------------->> const RWCString supplyName (name (0, dotPos)); const Supply (*refSply) (AccountMgr::instance()->getSupply(supplyName)); ObjList exchPaybacks; getPaybacks (exchPaybacks); const Payback* pback (NULL); Payback* matchPback (NULL); foreach_ObjList (Payback, exchPaybacks, pback) { if (pback->getPaybackSupply() == refSply) { matchPback = const_cast(pback); break; } } if (matchPback == NULL) return (NULL); //---------------------------------->> const RWCString slotName (name (dotPos+1, name.length() - dotPos - 1)); Slot (*pbackSlot) (matchPback->getSlot (slotName)); return (pbackSlot); } SeriesSlot* Exchange::getSourceBalanceSlot() const { return (const_cast (&_sourceBalance)); } SeriesSlot* Exchange::getDestBalanceSlot() const { return (const_cast (&_destBalance)); } SeriesSlot* Exchange::getBorrowSupplySlot() const { if (_borrowSupply != NULL) { return (_borrowSupply->getSupplySlot()); } return (NULL); } SeriesSlot* Exchange::getInputBorrowSlot() const { return (_inputBorrow ? _inputBorrow->getSupplySlot() : NULL); } SeriesSlot* Exchange::getBorrowSlot() const { if (_borrowSupply != NULL) { return (_borrowSupply->getSupplySlot()); } else if (_inputBorrow) { return (_inputBorrow->getSupplySlot()); } return (NULL); } // // return various names // const RWCString Exchange::getRiverWareName() const { return _name; } const RWCString Exchange::getCompleteName() const { return _name; } // // set the accounting period // void Exchange::setAccountingPeriod(const Date_Time *beginDate, const Date_Time *endDate, const DeltaTime *timestep) throw (cwException) { // set the begin and end dates (and perhaps the timestep) on the // source and destination balance slots. if (timestep == NULL) { if (_sourceBalance.setDates(beginDate, endDate) == true) { throw cwRuntimeError(SUPPLY_ACCT_PERIOD, _sourceBalance.getCompleteName().data(), "Invalid dates"); } if (_destBalance.setDates(beginDate, endDate) == true) { throw cwRuntimeError(SUPPLY_ACCT_PERIOD, _destBalance.getCompleteName().data(), "Invalid dates"); } } else { errstat setErr (false); setErr = _sourceBalance.setDatesAndStep(beginDate, endDate, timestep); if (setErr) { throw cwRuntimeError(SUPPLY_ACCT_PERIOD, _sourceBalance.getCompleteName().data(), setErr.msg().data()); } setErr = _destBalance.setDatesAndStep(beginDate, endDate, timestep); if (setErr) { throw cwRuntimeError(SUPPLY_ACCT_PERIOD, _destBalance.getCompleteName().data(), setErr.msg().data()); } } // set the accounting period on the input borrow supply. if (_inputBorrow != NULL) { _inputBorrow->setAccountingPeriod(beginDate, endDate, timestep); } // set the accounting period on each payback. const Payback* payback; foreach_EditObjList (Payback, _paybacks, payback) { const_cast(payback)->setAccountingPeriod(beginDate, endDate, timestep); } } // // slot changed notification // bool Exchange::slotChanged(const Date_Time *when, const Slot *slot, CallbackType callback) { #ifdef DEBUG_EXCHANGE /* * DEBUGGING: *if slot name is "Destination Balance" and *object name is RGOtowiBernalilloHeronEX * stop here in debugger */ if(slot->getName() == "Destination Balance") { Exchange *e = slot->getExchange(); if(e && e->getName() == "RGOtowiBernalilloHeronEX") { cerr << "RGOtowiBernalilloHeronEX.Destination Balance" <hasAttribute(Slot::ExchangeSlotBit)) { if(callback == SERIESSLOT_SERIES_VALUE_CHANGED_NOWONLY || callback == SUBSLOT_SERIES_VALUE_CHANGED_NOWONLY) { // don't solve ahead; return true; } bool status; Date_Time timestepContext = *applContext.getTimestepContext(); if (slot == &_sourceBalance) { status = solve_next_source_balance(when, slot); } else if (slot == &_destBalance) { status = solve_next_dest_balance(when, slot); } APPL_TIMESTEP_CONTEXT(×tepContext); return status; } else if (slot->hasAttribute(Slot::PaybackSlotBit)) { return true; } else if (slot->hasAttribute(Slot::SupplySlotBit)) { // get the supply. Supply* supply = slot->getSupply(); if ((_borrowSupply && (supply == _borrowSupply)) || (supply == _inputBorrow)) { return solve_source_balance(when, slot) && solve_dest_balance(when, slot); } else if (getPayback(supply) != NULL) { return solve_source_balance(when, slot); } else if (supply == _destSupply) { return solve_dest_balance(when, slot); } } rwAssert(false); return false; } // // controller methods // void Exchange::clearState() { // If we are in a resumed-run, then only clear the state for the part // of the run that has not yet been simulated. TimeInterval interval = AccountMgr::instance()->getAccountingPeriod(); if (rwWorkspace->getRunInfo()->isResumedRun()) { Date_Time startDate(*rwWorkspace->getRunInfo()->getResumedStartDate()); interval.start(startDate); } clearOutputs(interval); clearOutputFlags(interval); } void Exchange::clearOutputs(const class TimeInterval &interval) { // if necessary, clear the input borrow supply outputs. if ((_borrowSupply == NULL) && rwAssert (_inputBorrow != NULL)) { _inputBorrow->getSupplySlot()->clearOutputs(interval); } // clear the source and destination balance outputs. _sourceBalance.clearOutputs(interval); _destBalance.clearOutputs(interval); // clear the payback outputs. const Payback* payback; foreach_EditObjList (Payback, _paybacks, payback) { const_cast(payback)->clearOutputs(interval); } } void Exchange::clearOutputFlags(const class TimeInterval &interval) { // if necessary, clear the input borrow supply output flags. if ((_borrowSupply == NULL) && rwAssert (_inputBorrow != NULL)) { _inputBorrow->getSupplySlot()->clearOutputFlags(interval); } // clear the source and destination balance output flags. _sourceBalance.clearOutputFlags(interval); _destBalance.clearOutputFlags(interval); // clear the payback output flags. const Payback* payback; foreach_EditObjList (Payback, _paybacks, payback) { const_cast(payback)->clearOutputFlags(interval); } } void Exchange::clearIterations() { // if necessary, clear the input borrow supply iteration count. if ((_borrowSupply == NULL) && rwAssert (_inputBorrow != NULL)) { _inputBorrow->getSupplySlot()->clearIterations(); } // clear the source and destination balance iteration counts. _sourceBalance.clearIterations(); _destBalance.clearIterations(); // clear the payback iteration counts. const Payback* payback; foreach_EditObjList (Payback, _paybacks, payback) { const_cast(payback)->clearIterations(); } } void Exchange::setUserInput(RunInfo *ri) { // if necessary, set the input borrow supply user input. if ((_borrowSupply == NULL) && rwAssert (_inputBorrow != NULL)) { _inputBorrow->getSupplySlot()->setUserInput(ri); } // set the payback user input. const Payback* payback; foreach_EditObjList (Payback, _paybacks, payback) { const_cast(payback)->setUserInput(ri); } } // // given borrow or payback at T, solve for source balance at T // // private bool Exchange::solve_source_balance(const Date_Time *when, const Slot * /*slot*/) { bool status(true); // return status double prevBalance; // previous source balance double borrow; // borrow value double payback; // sum of payback-minus-loss values // get the previous source balance, the borrow value, and the sum // of the payback-minus-loss values. if (_sourceBalance.getValue(when, -1, prevBalance) == false && getBorrowValue(borrow, when) && sumPaybackMinusLossValues(payback, when)) { if (!isValid(prevBalance)) { prevBalance = 0.0; } // calculate the source balance. double balance(prevBalance + borrow - payback); // set the source balance. SetStatus setStatus( _sourceBalance.setStdValue(balance, when)); if (setStatus == SET_OK || setStatus == SET_NOCHANGE) { // solve the paybacks. solve_paybacks(when); } else { status = false; } } return status; } // // given source balance at T, solve for source balance at T+1 // // private bool Exchange::solve_next_source_balance(const Date_Time *when, const Slot * /*slot*/) { bool status(true); // return status double balance; // source balance double nextBorrow; // next borrow value double nextPayback; // sum of next payback-minus-loss values // get the source balance, the next borrow value, and the sum of // the next payback-minus-loss values. if (_sourceBalance.getValue(when, 0, balance) == false && getBorrowValue(nextBorrow, when, 1) && sumPaybackMinusLossValues(nextPayback, when, 1)) { if (!isValid(balance)) { balance = 0.0; } // calculate the next balance. double nextBalance(balance + nextBorrow - nextPayback); // set the next source balance. Date_Time nextDt(*when); nextDt += _sourceBalance.getStep(); SetStatus setStatus( _sourceBalance.setStdValue(nextBalance, &nextDt)); if (setStatus == SET_OK || setStatus == SET_NOCHANGE) { // solve the paybacks. solve_paybacks(&nextDt); } else { status = false; } } return status; } // // given borrow or destination at T, solve for destination balance // at T // // private bool Exchange::solve_dest_balance(const Date_Time *when, const Slot * /*slot*/) { bool status(true); // return status double prevBalance; // previous destination balance double borrow; // borrow value double dest; // destination value // get the previous destination balance, the borrow value, and the // destination value. if (_destBalance.getValue(when, -1, prevBalance) == false && getBorrowValue(borrow, when) && getDestValue(dest, when)) { if (!isValid(prevBalance)) { prevBalance = 0.0; } // calculate the destination balance. double balance(prevBalance + borrow - dest); // set the destination balance. SetStatus setStatus( _destBalance.setStdValue(balance, when)); if (setStatus != SET_OK && setStatus != SET_NOCHANGE) { status = false; } } return status; } // // given destination balance at T, solve for destination balance // at T+1 // // private bool Exchange::solve_next_dest_balance(const Date_Time *when, const Slot * /*slot*/) { bool status(true); // return status double balance; // destination balance double nextBorrow; // next borrow value double nextDest; // next destination value // get the destination balance, the next borrow, and the next // destination value. if (_destBalance.getValue(when, 0, balance) == false && getBorrowValue(nextBorrow, when, 1) && getDestValue(nextDest, when, 1)) { if (!isValid(balance)) { balance = 0.0; } // calculate the next balance. double nextBalance(balance + nextBorrow - nextDest); // set the next destination balance. Date_Time nextDt(*when); nextDt += _destBalance.getStep(); SetStatus setStatus( _destBalance.setStdValue(nextBalance, &nextDt)); if (setStatus != SET_OK && setStatus != SET_NOCHANGE) { status = false; } } return status; } // // solve paybacks at T // // private bool Exchange::solve_paybacks(const Date_Time *when) const { bool status(false); // return status double balance; // source balance // get the source balance. if (((SeriesSlot *) &_sourceBalance)->getValue(when, 0, balance) == false) { // for each payback... const Payback* payback; foreach_EditObjList (Payback, _paybacks, payback) { // set its debt value. status = const_cast(payback)->setDebtValue(balance, when); if (status == false) { break; } } } return status; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // // comparison functions for sorting exchanges // int compareExchangesByName(const Exchange * const &exchange1, const Exchange * const &exchange2) { return exchange1->getName().compareTo(exchange2->getName()); } int compareExchangesByName(Exchange * const &exchange1, Exchange * const &exchange2) { return exchange1->getName().compareTo(exchange2->getName()); } int compareExchangesByBorrowSupply(const Exchange * const &exchange1, const Exchange * const &exchange2) { Supply (*supply1)(NULL); // exchange 1 borrow supply Supply (*supply2)(NULL); // exchange 2 borrow supply // get the borrow supplies. if (exchange1 != NULL) { supply1 = exchange1->hasBorrowSupply() ? exchange1->getBorrowSupply() : exchange1->getInputBorrow(); } if (exchange2 != NULL) { supply2 = exchange2->hasBorrowSupply() ? exchange2->getBorrowSupply() : exchange2->getInputBorrow(); } // compare the borrow supply names. if (supply1 == NULL) { return supply2 == NULL ? 0 : -1; } else { return supply2 == NULL ? 1 : supply1->getName().compareTo(supply2->getName()); } // NOTREACHED } int compareExchangesByBorrowSupply(Exchange * const &exchange1, Exchange * const &exchange2) { return compareExchangesByBorrowSupply((const Exchange *) exchange1, (const Exchange *) exchange2); }