// // $Id: Payback.cpp,v 1.48 2008/12/23 19:54:31 philw Exp $ // #ifndef PaybackINCLUDED # include "Payback.hpp" #endif #ifndef AccountMgrINCLUDED # include "AccountMgr.hpp" #endif #ifndef ExchangeINCLUDED # include "Exchange.hpp" #endif #ifndef SupplyINCLUDED # include "Supply.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 SeriesINCLUDED # include "Series.hpp" #endif #ifndef TimeIntervalINCLUDED # include "TimeInterval.hpp" #endif #ifndef rwErrorINCLUDED # include "rwError.hpp" #endif #ifndef cwExceptINCLUDED # include "cwExcept.hpp" #endif #include "cwiostream.hpp" #ifndef AccountingCBDataINCLUDED # include "AccountingCBData.hpp" #endif #ifndef CallbackReceiverMgrINCLUDED #include "CallbackReceiverMgr.hpp" #endif // // constructors and destructor // Payback::Payback(Exchange *exchange) : // base classes Root(), EditObj(), // members _exchange(exchange), _paybackSupply(NULL), _lossCoeff(LossCoeff, FRACTION, Slot::PaybackSlotBit), _debt(Debt, VOLUME, NOUNITS, "", Slot::PaybackSlotBit), _slotList() { rwAssert(_exchange != NULL); _lossCoeff.setManagedByPayback(1); _lossCoeff.setPayback(this); _debt.setManagedByPayback(1); _debt.setPayback(this); _slotList.append (&_lossCoeff); _slotList.append (&_debt); if (_exchange != NULL) { // Copy the Exchange's Source Balance slot config to the Debt slot. const SeriesSlot* exchSourceBal (_exchange->getSourceBalanceSlot()); if (exchSourceBal != NULL) { exchSourceBal->copyConfigTo (&_debt); } } } Payback::~Payback() { { // 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. PaybackCBData data(this, PAYBACK_DELETED, AccountingCBData::no_subtype, NULL); callbackReceiverMgr.invoke(this, PAYBACK_DELETED, (CallbackData *)(&data)); } // clear the payback supply and callback. setPaybackSupply (NULL); #ifdef CWDEBUG // We should never be able to link slots on paybacks // except as supplies rwAssert(_lossCoeff.numOutLinks() == 0); rwAssert(_debt.numOutLinks() == 0); #endif _slotList.clear(); } // // EditObj: init, apply, cancel, and revert the edit // // private, virtual Payback *Payback::init_edit() throw (cwException) { Payback (*copy)(NULL); // edit copy copy = new Payback(_exchange); // initialize the payback supply copy->_paybackSupply = _paybackSupply; // initialize the loss coefficient and debt configurations. if (_lossCoeff.copyConfigTo (©->_lossCoeff) || _debt.copyConfigTo (©->_debt)) { // todo: finish this. throw cwRuntimeError(); } // initialize the loss coefficient value. copy->_lossCoeff.setStdValue(_lossCoeff.getStdValue()); return copy; } // private, virtual void Payback::apply_edit(Payback *copy) throw (cwException) { // apply the payback supply. setPaybackSupply (copy->_paybackSupply); // apply the loss coefficient and debt configurations. if (copy->_lossCoeff.copyConfigTo (&_lossCoeff) || copy->_debt.copyConfigTo (&_debt)) { // todo: finish this. throw cwRuntimeError(); } // apply the loss coefficient value. double copyCoeff(copy->_lossCoeff.getStdValue()); if (!isEqual(_lossCoeff.getStdValue(), copyCoeff)) { _lossCoeff.setStdValue(copyCoeff); } return; } // private, virtual void Payback::revert_edit(Payback * /*orig*/) throw (cwException) { throw cwLogicError("Payback::revert_edit() is not implemented."); } // // EditObj: is-modified // // private, virtual bool Payback::is_modified(const Payback *orig) const { bool modified(false); // modified? // compare the payback supply, the loss coefficient and debt // configurations, and the loss coefficient value. if (_paybackSupply != orig->_paybackSupply || !_lossCoeff.hasSameConfig(&orig->_lossCoeff) || !_debt.hasSameConfig(&orig->_debt) || !isEqual(_lossCoeff.getStdValue(), orig->_lossCoeff.getStdValue())) { modified = true; } return modified; } // // save and load // bool Payback::save(cwostream &os) const { // set o "$mgr.payback name" // set the variable for all subsequent payback commands. os << "set o \"$mgr." << _exchange->getName() << "." << _paybackSupply->getName() << "\"" << endl; // for each slot... // // 1. "$o" {slot type} {slot name} // the payback 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\" {" << _lossCoeff.getType() << "} {" << _lossCoeff.getName() << "}" << endl; ((ScalarSlot *) &_lossCoeff)->dumpEnt(os); os << "\"$o\" {" << _debt.getType() << "} {" << _debt.getName() << "}" << endl; ((SeriesSlot *) &_debt)->dumpEnt(os); return true; } int paybackCmd(ClientData data, Tcl_Interp *interp, int argc, char **argv) { Payback *payback; // payback pointer char first; // first command character // the client data is the payback pointer. payback = (Payback *) data; // // valid commands: // // {slot type} {slot name} // first = argv[1][0]; if (rwWorkspace->isValidSlotType(argv[1]) == true) { // {slot type} {slot name} return payback->slot_cmd(interp, argc, argv); } else { // invalid command TclSetResult(PAYBACK_CMD_INVALID, argv[0], argv[1], "{slot type}"); return TCL_ERROR; } // NOTREACHED } // // load commands // // private int Payback::slot_cmd(Tcl_Interp *interp, int argc, char **argv) { Slot *slot; // slot pointer if (argc != 3) { TclSetResult(PAYBACK_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 payback'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 Payback::createTclCmd(Tcl_Interp *interp, const RWCString &cmd) const { // todo: check the return value. Tcl_CreateCommand(interp, (char *) (cmd + "." + _paybackSupply->getName()).data(), paybackCmd, (ClientData) this, NULL); return true; } // // set the exchange // // private void Payback::setExchange(Exchange *exchange) { _exchange = exchange; if (_exchange != NULL) { // Copy the Exchange's Source Balance slot config to the Debt slot. const SeriesSlot* exchSourceBal (_exchange->getSourceBalanceSlot()); if (exchSourceBal != NULL) { exchSourceBal->copyConfigTo (&_debt); } } return; } // // set and return the payback supply // void Payback::setPaybackSupply (Supply *supply, bool origDeleted) throw (cwException) { // if this is an edit copy, simply set the payback supply and // return. if (isEditCopy()) { _paybackSupply = supply; return; } // check for no change. if (supply == _paybackSupply) { return; } // check for a supply which is already part of an exchange. if (supply != NULL && supply->getRole() != Supply::Standalone) { static QString errMsg; QString splyExchName ("Unknown"); // tentative if (supply->getParentExchange()) { splyExchName = supply->getParentExchange()->getName().data(); } errMsg = QString ( "The selected Supply cannot be used as a new Payback:\n" " \"%1\" \n\n" "It is already part of an Exchange:\n" " \"%2\" ") .arg (supply->getName().data()) .arg (splyExchName); // todo: finish this. throw cwInvalidArgument (errMsg.ascii()); } // remove the original payback supply. if (_paybackSupply != NULL) { rwAssert (_paybackCbId != NULL && _paybackSupply->deleteCallback(_paybackCbId) == false); _paybackCbId = NULL; // if the original payback supply is being deleted, don't set // its role. if (!origDeleted) { _paybackSupply->setStandaloneRole(); } } // set the payback supply. _paybackSupply = supply; if (_paybackSupply != NULL) { _paybackCbId = new MethodCb( _exchange, &Exchange::supplyCbHandler, SUPPLY_DELETED); _paybackSupply->addCCallback (SUPPLY_DELETED, _paybackCbId); _paybackSupply->setExchangePaybackRole (this); // give the debt slot the same timeseries as the payback // supply slot. SeriesSlot (*supplySlot)(_paybackSupply->getSupplySlot()); DeltaTime step = supplySlot->getStep(); _debt.initializeSeries( *supplySlot->getStartDate(), *supplySlot->getEndDate(), step); } // todo: notify the exchange to resolve the entire time series and // issue "payback changed" callback. return; } // // return the payback value at the specified timestep and offset // (with or without the loss applied) // bool Payback::getPaybackValue(double &value, const Date_Time *when, int offset) const { bool status(false); // return status if (_paybackSupply != NULL) { // get the payback supply slot. SeriesSlot (*slot)(_paybackSupply->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 extern double flowToVolume(const Date_Time *, int, const DeltaTime &, double); value = flowToVolume(when, offset, slot->getStep(), value); # endif status = true; } } return status; } bool Payback::getPaybackMinusLossValue(double &value, const Date_Time *when, int offset) const { bool status(false); // return status // get the payback value at the specified timestep and offset. if (getPaybackValue(value, when, offset)) { double coeff; // loss coefficient // get the loss coefficient and apply it to the payback value. if (isValid(coeff = _lossCoeff.getStdValue())) { value *= (1.0 - coeff); } status = true; } return status; } // // set and return the debt value at the specified timestep and // offset // bool Payback::setDebtValue(double balance, const Date_Time *when, int offset) { double debt(balance); // debt value double coeff; // loss coefficient Date_Time dt(*when); // date + offset // get the loss coefficient and apply it to the debt value. if (isValid(coeff = _lossCoeff.getStdValue())) { if (coeff == 1.0) { rwError(ACCTMGMTID, PAYBACK_LOSS_COEFF, _exchange->getName().data(), _paybackSupply->getName().data()); debt = INVALIDVALUE; } else { debt /= (1.0 - coeff); } } // if necessary, add the offset. if (offset != 0) { dt += DeltaTime(offset, _debt.getStep()); } // set the debt value. _debt.setStdValue(debt, &dt); return true; } bool Payback::getDebtValue(double &value, const Date_Time *when, int offset) const { bool status(false); // return status // get the debt slot value at the specified timestep and offset. if (((SeriesSlot *) &_debt)->getValue(when, offset, value) == false) { // return an invalid value as zero. if (!isValid(value)) { value = 0.0; } status = true; } return status; } // // return the slot // Slot *Payback::getSlot(const RWCString &name) { Slot (*slot)(NULL); // returned slot if (name == LossCoeff) { slot = &_lossCoeff; } else if (name == Debt) { slot = &_debt; } return slot; } // // return various names // const RWCString Payback::getName() const { if (_paybackSupply != NULL) { const RWCString ret1 (_exchange->getCompleteName() + "." + _paybackSupply->getName()); return (ret1); //-------------------------------------->> } const RWCString ret2 (_exchange->getCompleteName() + ".NoSupply"); return ret2; } const RWCString Payback::getRiverWareName() const { if (_paybackSupply != NULL) { const RWCString ret1 (_exchange->getRiverWareName() + "." + _paybackSupply->getName()); return (ret1); //-------------------------------------->> } const RWCString ret2 (_exchange->getRiverWareName() + ".NoSupply"); return (ret2); } const RWCString Payback::getCompleteName() const { if (_paybackSupply != NULL) { const RWCString ret1 (_exchange->getCompleteName() + "." + _paybackSupply->getName()); return (ret1); //-------------------------------------->> } const RWCString ret2 (_exchange->getCompleteName() + ".NoSupply"); return ret2; } // static okstat Payback::splitName (const RWCString& completeName, RWCString& exchangeNameReturn, RWCString& supplyNameReturn) { exchangeNameReturn = ""; supplyNameReturn = ""; const int delimIndex (completeName.first ('.')); if ((delimIndex < 0) || (delimIndex == RW_NPOS)) { exchangeNameReturn = completeName; return okstat ("Period delimeter not found between Exchange" " and Supply names within Payback name."); //---------------->> } const int completeLen (completeName.length()); const int exchangeLen (delimIndex); const int supplyLen (completeLen - delimIndex - 1); if (exchangeLen > 0) exchangeNameReturn = completeName (0, exchangeLen); if (supplyLen > 0) supplyNameReturn = completeName (delimIndex+1, supplyLen); if (exchangeLen <= 0) return okstat ("Missing Exchange name within Payback name."); if (supplyLen <= 0) return okstat ("Missing Supply name within Payback name."); return ((okstat) true); } // // set the accounting period // void Payback::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 // debt slot. if (timestep == NULL) { if (_debt.setDates(beginDate, endDate) == true) { throw cwRuntimeError(PAYBACK_ACCT_PERIOD, _debt.getCompleteName().data(), "Invalid dates"); } } else { errstat setErr = _debt.setDatesAndStep (beginDate, endDate, timestep); if (setErr) { throw cwRuntimeError(PAYBACK_ACCT_PERIOD, _debt.getCompleteName().data(), setErr.msg().data()); } } return; } // // slot changed notification // bool Payback::slotChanged(const Date_Time *when, const Slot *slot, CallbackType callback) { _exchange->slotChanged(when, slot, callback); return true; } // // controller methods // void Payback::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); return; } void Payback::clearOutputs(const class TimeInterval &interval) { // clear the debt outputs. _debt.clearOutputs(interval); return; } void Payback::clearOutputFlags(const class TimeInterval &interval) { // clear the debt output flags. _debt.clearOutputFlags(interval); return; } void Payback::clearIterations() { // clear the debt iteration count. _debt.clearIterations(); return; } void Payback::setUserInput(RunInfo * /*ri*/) { return; } //--- (end Payback.cpp) ---