#include "Aria.h" #include "ArExport.h" #include "ArServerMode.h" #include "ArServerModeIdle.h" #include AREXPORT bool ArServerMode::ourActiveModeLocked = false; AREXPORT bool ArServerMode::ourActiveModeWillUnlockIfRequested = false; AREXPORT ArServerMode *ArServerMode::ourActiveMode = NULL; AREXPORT ArServerMode *ArServerMode::ourNextActiveMode = NULL; AREXPORT ArServerMode *ArServerMode::ourLastActiveMode = NULL; AREXPORT std::list ArServerMode::ourDefaultModes; AREXPORT std::list ArServerMode::ourRequestedActivateModes; AREXPORT std::list ArServerMode::ourModes; AREXPORT bool ArServerMode::ourUserTaskAdded = false; AREXPORT ArGlobalFunctor ArServerMode::ourUserTaskCB( ArServerMode::modeUserTask); AREXPORT std::multimap ArServerMode::ourModeDataMap; AREXPORT ArMutex ArServerMode::ourModeDataMapMutex; AREXPORT ArGlobalFunctor2 ArServerMode::ourGetModeDataListCB( ArServerMode::getModeDataList); AREXPORT ArGlobalFunctor2 ArServerMode::ourGetModeInfoCB( ArServerMode::getModeInfo); AREXPORT ArGlobalFunctor2 ArServerMode::ourGetModeBusyCB(ArServerMode::getModeBusy); AREXPORT bool ArServerMode::ourBroadcastActiveModeLocked = false; AREXPORT ArServerMode *ArServerMode::ourBroadcastActiveMode = NULL; AREXPORT bool ArServerMode::ourBroadcastActiveModeWillUnlockIfRequested = false; AREXPORT bool ArServerMode::ourIsBusy = false; AREXPORT ArServerBase *ArServerMode::ourServerBase = NULL; AREXPORT ArServerModeIdle *ArServerMode::ourIdleMode = NULL; AREXPORT ArMutex ArServerMode::ourIdleModeMutex; AREXPORT bool ArServerMode::ourIdleModeCreated = false; AREXPORT bool ArServerMode::ourActiveModeSetActivityThisCycle = false; AREXPORT void ArServerMode::modeUserTask(void) { if (ourActiveMode != NULL) { ourActiveMode->mySetActivityThisCycle = false; ourActiveMode->userTask(); if (ourActiveMode != NULL) ourActiveModeSetActivityThisCycle = ourActiveMode->mySetActivityThisCycle; else ourActiveModeSetActivityThisCycle = true; } else { std::list::iterator it; for (it = ourDefaultModes.begin(); it != ourDefaultModes.end() && ourActiveMode == NULL; it++) { ArLog::log(ArLog::Terse, "Checking default on %s mode", (*it)->getName()); (*it)->checkDefault(); if (ourActiveMode != NULL) { ArLog::log(ArLog::Normal, "Activated %s mode as default", (*it)->getName()); break; } } // end for each default mode } // end else no active mode bool wasBusy = ourIsBusy; // It seems to me that we wouldn't want to call the robot busy if there is no // active mode. (Perhaps getActiveModeActivityTimeSecSince() should return // something besides zero in this case.) ourIsBusy = ((ourActiveMode != NULL) && (ArServerMode::getActiveModeActivityTimeSecSince() == 0)); if (wasBusy != ourIsBusy) { if (ourServerBase != NULL) { //ArLog::log(ArLog::Normal, "ArServerMode busy changed: %i", ourIsBusy); ArNetPacket packet; packet.byteToBuf(ourIsBusy); ourServerBase->broadcastPacketTcp(&packet, "modeBusyChanged"); } } // end if busy changed } // end method modeUserTask AREXPORT ArServerMode::ArServerMode(ArRobot *robot, ArServerBase *server, const char *name) { ourModeDataMapMutex.setLogName("ArServerMode::ourModeDataMapMutex"); myName = name; std::replace(myName.begin(), myName.end(), ' ', '_'); std::string activityMutexName; activityMutexName = "ArServerMode::"; activityMutexName += myName; activityMutexName += "::myActivityTimeMutex"; myActivityTimeMutex.setLogName(activityMutexName.c_str()); myVerboseLogLevel = ArLog::Verbose; myRobot = robot; myServer = server; myIsActive = false; myStatusSetThisCycle = false; ourModes.push_front(this); myHasSetActivityTime = false; mySetActivityThisCycle = false; std::string cbListName; cbListName = "ArServerMode::"; cbListName += myName; cbListName += "::myActivateCallbacks"; myActivateCallbacks.setName(cbListName.c_str()); cbListName = "ArServerMode::"; cbListName += myName; cbListName += "::myDeactivateCallbacks"; myDeactivateCallbacks.setName(cbListName.c_str()); cbListName = "ArServerMode::"; cbListName += myName; cbListName += "::mySingleShotDeactivateCallbacks"; mySingleShotDeactivateCallbacks.setName(cbListName.c_str()); mySingleShotDeactivateCallbacks.setSingleShot(true); if (!ourUserTaskAdded) { ourUserTaskAdded = true; myRobot->addUserTask("server mode", 50, &ourUserTaskCB); myServer->addData("getModeDataList", "Gets the list of commands associated with modes.", &ourGetModeDataListCB, "none", "ubyte4: numData; repeating numData times, string: mode; string: data", "RobotInfo", "RETURN_SINGLE"); myServer->addData("getModeInfo", "Gets the current mode active and if its locked and will or won't unlock (for lists of which commands are with which modes use getModeDataList) commands associated with modes.", &ourGetModeInfoCB, "none", "string: mode; uByte: locked (1 == locked, 0 == unlocked); uByte: willUnlockIfRequested (1 == will, 0 == won't)", "RobotInfo", "RETURN_SINGLE"); myServer->addData("modeBusyChanged", "Broadcast when the busy state (non-zero idle time) of the active mode changes", &ourGetModeBusyCB, "None", "byte: 1 if busy, 0 if idle", "RobotInfo", "RETURN_SINGLE"); } // end if static tasks and handlers not yet added // Storing the first occurrence of the server base for use in the static // modeUserTask method. In general, there seems to be a single server base which // isn't deleted during the program execution. However, if this should change, // then will need to add a way of resetting the static ourServerBase. if (ourServerBase == NULL) { ourServerBase = server; } ourIdleModeMutex.lock(); if (ourIdleMode == NULL && !ourIdleModeCreated) { ourIdleModeCreated = true; ourIdleModeMutex.unlock(); ourIdleMode = new ArServerModeIdle(server, robot); } else { ourIdleModeMutex.unlock(); } } // end ctor AREXPORT ArServerMode::~ArServerMode() { } AREXPORT void ArServerMode::lockMode(bool willUnlockIfRequested) { if (!myIsActive || ourActiveMode != this) { ArLog::log(myVerboseLogLevel, "ArServerMode::lockMode: mode %s could not lock because it is not active", getName()); return; } ourActiveModeLocked = true; ourActiveModeWillUnlockIfRequested = willUnlockIfRequested; ArLog::log(myVerboseLogLevel, "Locked into %s mode, will unlock %s", getName(), ArUtil::convertBool(willUnlockIfRequested)); checkBroadcastModeInfoPacket(); } AREXPORT void ArServerMode::unlockMode(void) { if (!myIsActive || ourActiveMode != this) { ArLog::log(myVerboseLogLevel, "ArServerMode::unlockMode: mode %s could not unlock because it is not locked", getName()); return; } ourActiveModeLocked = false; ourActiveModeWillUnlockIfRequested = false; ArLog::log(myVerboseLogLevel, "Unlocked from %s mode", getName()); checkBroadcastModeInfoPacket(); } /** This should only be called from places where a forced unlock _HAS_ to happen, ie on a robot joystick, but modes should do whatever they have to in this to turn things off or what not. **/ AREXPORT void ArServerMode::forceUnlock(void) { ArLog::log(ArLog::Terse, "Mode %s being forcibly unlocked", getName()); unlockMode(); } /// Gets whether we'll unlock if requested or not AREXPORT bool ArServerMode::willUnlockIfRequested(void) { if (ourActiveMode == NULL || !ourActiveModeLocked || (ourActiveModeLocked && ourActiveModeWillUnlockIfRequested)) return true; else return false; } /// Gets if the active mode is locked or not AREXPORT bool ArServerMode::isLocked(void) { if (ourActiveMode == NULL || !ourActiveModeLocked) return false; else return true; } AREXPORT bool ArServerMode::isAutoResumeAfterInterrupt() { return false; } /** Makes this mode active if it can... If this returns false then the mode's activate() method should just return... Note that before calling this a mode should have already made sure it can activate... This also calls the activate callbacks (only if the mode will be allowed to activate of course). IMPORTANT: This method must only be called within the context of the activate() method. **/ AREXPORT bool ArServerMode::baseActivate(void) { // if we're locked then return false so nothing else activates if (ourActiveMode != NULL && ourActiveMode != this && ourActiveModeLocked) { ArLog::log(myVerboseLogLevel, "Could not switch to %s mode because of lock.", getName()); // request our active mode to unlock // // KMC: Moved the following line so that it is called after this mode is pushed // onto the ourRequestedActivatedModes queue. Under certain conditions, // the requestUnlock() call results in a mode being deactivated and the next // queued mode being activated. This mode will essentially be pushed onto the // queue too late -- and will either be ignored or activated at an unexpected // time. (An example scenario occurs when the robot is going to dock, and the // go-to-goal mode attempts to activate.) // ourActiveMode->requestUnlock(); ArLog::log(myVerboseLogLevel, "Removing this (%s) from requested activate modes", getName()); ourRequestedActivateModes.remove(this); ArLog::log(myVerboseLogLevel, "Mode %s wants to be activated, adding to requested activate modes.", getName()); ourRequestedActivateModes.push_front(this); ourActiveMode->requestUnlock(); return false; } if (this != ourIdleMode && myServer->idleProcessingPending()) { if (ourActiveMode == ourIdleMode) { ArLog::log(myVerboseLogLevel, "Since idle already active didn't set nextActiveMode when mode %s tried to activate, just returning", this->getName()); return false; } ourNextActiveMode = ourIdleMode; if (ourActiveMode != NULL) ArLog::log(myVerboseLogLevel, "Setting nextActiveMode explicitly to idle mode from mode %s trying to activate while %s is active", this->getName(), ourActiveMode->getName()); else ArLog::log(myVerboseLogLevel, "Setting nextActiveMode explicitly to idle mode from mode %s trying to activate while no mode is active", this->getName()); if (ourActiveMode != NULL && ourActiveMode != ourIdleMode) { ourIdleMode->setModeInterrupted(ourActiveMode); ourActiveMode->deactivate(); } ArLog::log(myVerboseLogLevel, "Removing this (%s) from requested activate modes", getName()); ourRequestedActivateModes.remove(this); ArLog::log(myVerboseLogLevel, "Mode %s wants to be activated, denying for now because of idle, but adding to requested activate modes", getName()); ourRequestedActivateModes.push_front(this); ourIdleMode->activate(); return false; } ourNextActiveMode = this; if (ourActiveMode != NULL) ArLog::log(myVerboseLogLevel, "Setting nextActiveMode to mode %s (ourActiveMode %s)", ourNextActiveMode->getName(), ourActiveMode->getName()); else ArLog::log(myVerboseLogLevel, "Setting nextActiveMode to mode %s (ourActiveMode NULL)", ourNextActiveMode->getName()); if (ourActiveMode != NULL) ourActiveMode->deactivate(); myIsActive = true; myActivityTimeMutex.lock(); myActivityTime.setToNow(); myActivityTimeMutex.unlock(); /* if (myRobot != NULL) { myRobot->addUserTask(myName.c_str(), 50, &myUserTaskCB); } */ ourLastActiveMode = ourActiveMode; ourActiveMode = this; ourNextActiveMode = NULL; ArLog::log(myVerboseLogLevel, "Setting nextActiveMode to NULL"); if (myRobot != NULL) { myRobot->stop(); myRobot->clearDirectMotion(); } /// Call our activate callbacks myActivateCallbacks.invoke(); /// Set the activity time to now, but do NOT set the flag, since that flag is used to determine if a particular mode has used it or not ArLog::log(myVerboseLogLevel, "Activated %s mode", getName()); checkBroadcastModeInfoPacket(); return true; } /** Whenever a mode uses this it should already have done all of its own deactivations (this also calls the deactive callbacks) **/ AREXPORT void ArServerMode::baseDeactivate(void) { std::list::iterator it; /* for (it = ourDefaultModes.begin(); it != ourDefaultModes.end(); it++) { ArLog::log(myVerboseLogLevel, "defaults are %s mode", (*it)->getName()); } */ // if we're the active mode, we're deactivating, and we're locked, then unlock if (ourActiveMode != NULL && ourActiveMode == this && ourActiveModeLocked) unlockMode(); /* if (myRobot != NULL) myRobot->remUserTask(&myUserTaskCB); */ myIsActive = false; if (ourActiveMode == this) { ourLastActiveMode = ourActiveMode; ourActiveMode = NULL; } myDeactivateCallbacks.invoke(); mySingleShotDeactivateCallbacks.invoke(); ArLog::log(myVerboseLogLevel, "Deactivated %s mode", getName()); if (ourNextActiveMode == NULL) { ArLog::log(myVerboseLogLevel, "No next active mode %s"); // walk through until one of these is ready while ((it = ourRequestedActivateModes.begin()) != ourRequestedActivateModes.end()) { std::string name; name = (*it)->getName(); ArLog::log(myVerboseLogLevel,"Trying to activate %s", name.c_str()); (*it)->activate(); if (ourActiveMode == ourIdleMode) { ArLog::log(myVerboseLogLevel, "Idle mode activated instead, so leaving the requestedActivateModes alone"); return; } ArLog::log(myVerboseLogLevel, "Popping front of requested activate modes (%s)"); ourRequestedActivateModes.pop_front(); if (ourActiveMode != NULL) { ArLog::log(myVerboseLogLevel, "and did, clearing requested activate modes (size = %i)", ourRequestedActivateModes.size()); // now clear out the old modes so that we don't wind up // stacking too much. First, notify them that they will not be activated. for (std::list::iterator dIter = ourRequestedActivateModes.begin(); dIter != ourRequestedActivateModes.end(); dIter++) { ArServerMode *deniedMode = *dIter; if (deniedMode != NULL) { deniedMode->activationDenied(); } } ourRequestedActivateModes.clear(); ArLog::log(myVerboseLogLevel, "Deactivate %s mode returns (1)", getName()); return; } } // end while modes to activate ArLog::log(myVerboseLogLevel, "Deactivate did not activate any modes, clearing requested activate modes (size = %i)", ourRequestedActivateModes.size()); // now clear out the old modes so that we don't wind up stacking // too much (should be empty anyways here, just make sure. (Same logic // as above about notifying the mode that it won't be activated.) for (std::list::iterator dIter = ourRequestedActivateModes.begin(); dIter != ourRequestedActivateModes.end(); dIter++) { ArServerMode *deniedMode = *dIter; if (deniedMode != NULL) { deniedMode->activationDenied(); } } ourRequestedActivateModes.clear(); for (it = ourDefaultModes.begin(); it != ourDefaultModes.end() && ourActiveMode == NULL; it++) { ArLog::log(ArLog::Normal, "Checking default on %s mode", (*it)->getName()); (*it)->checkDefault(); if (ourActiveMode != NULL) { //printf("and did\n"); ArLog::log(myVerboseLogLevel, "Deactivate %s mode returns (2)", getName()); return; } } } else { ArLog::log(myVerboseLogLevel, "Our next active mode %s, removing it from requested activate modes", ourNextActiveMode->getName()); ourRequestedActivateModes.remove(ourNextActiveMode); } ArLog::log(myVerboseLogLevel, "Deactivate %s mode returns (3)", getName()); } /** The default mode is activated whenever a mode is deactivated, even if a mode is deactivated only for another mode to be activated **/ AREXPORT void ArServerMode::addAsDefaultMode(ArListPos::Pos pos) { ArLog::log(ArLog::Normal, "Mode %s added as default mode", getName()); if (pos == ArListPos::LAST) ourDefaultModes.push_back(this); else if (pos == ArListPos::FIRST) ourDefaultModes.push_front(this); else { ArLog::log(ArLog::Terse, "ArServerMode::addAsDefaultMode: bad list position."); ourDefaultModes.push_front(this); } } AREXPORT ArTime ArServerMode::getActivityTime(void) { ArTime ret; myActivityTimeMutex.lock(); ret = myActivityTime; myActivityTimeMutex.unlock(); return ret; } AREXPORT void ArServerMode::setActivityTimeToNow(void) { myActivityTimeMutex.lock(); myHasSetActivityTime = true; mySetActivityThisCycle = true; myActivityTime.setToNow(); myActivityTimeMutex.unlock(); } AREXPORT int ArServerMode::getActiveModeActivityTimeSecSince(void) { // chop this to an int so its easier to use, if you care about it // use the getActivityTime on the ourActiveMode if (ourActiveMode != NULL) return (int)ourActiveMode->getActivityTime().secSince(); else return -1; } AREXPORT bool ArServerMode::getActiveModeSetActivityThisCycle(void) { return ourActiveModeSetActivityThisCycle; } AREXPORT const char *ArServerMode::getActiveModeModeString(void) { if (ourActiveMode != NULL) return ourActiveMode->getMode(); else return NULL; } AREXPORT const char *ArServerMode::getActiveModeStatusString(void) { if (ourActiveMode != NULL) return ourActiveMode->getStatus(); else return NULL; } AREXPORT const char *ArServerMode::getActiveModeExtendedStatusString(void) { if (ourActiveMode != NULL) return ourActiveMode->getExtendedStatus(); else return NULL; } /** This basically just notes the commands associated with a mode so that clients can know what can and can't happen based on what mode is active/locked/etc. You should call it only if the addData on the ArServerBase returns true (since otherwise it means that command wasn't added). **/ AREXPORT bool ArServerMode::addModeData( const char *name, const char *description, ArFunctor2 *functor, const char *argumentDescription, const char *returnDescription, const char *commandGroup, const char *dataFlags) { if (myServer->addData(name, description, functor, argumentDescription, returnDescription, commandGroup, dataFlags)) { ourModeDataMapMutex.lock(); ourModeDataMap.insert(std::pair(myName, name)); ourModeDataMapMutex.unlock(); return true; } else { ArLog::log(ArLog::Normal, "ArServerMode %s: Could not add mode data %s", myName.c_str(), name); return false; } } /** This returns the list of which data is in which mode **/ AREXPORT void ArServerMode::getModeDataList(ArServerClient *client, ArNetPacket *packet) { ourModeDataMapMutex.lock(); ArNetPacket sending; sending.uByte4ToBuf(ourModeDataMap.size()); std::multimap::iterator it; for (it = ourModeDataMap.begin(); it != ourModeDataMap.end(); it++) { sending.strToBuf((*it).first.c_str()); sending.strToBuf((*it).second.c_str()); } ourModeDataMapMutex.unlock(); client->sendPacketTcp(&sending); } /** This will get the info about the current active mode **/ AREXPORT void ArServerMode::getModeInfo(ArServerClient *client, ArNetPacket *packet) { ArNetPacket sending; buildModeInfoPacket(&sending); client->sendPacketTcp(&sending); } AREXPORT void ArServerMode::buildModeInfoPacket(ArNetPacket *sending) { if (ourActiveMode != NULL) { sending->strToBuf(ourActiveMode->getName()); if (ourActiveModeLocked) sending->uByteToBuf(1); else sending->uByteToBuf(0); if (ourActiveModeWillUnlockIfRequested) sending->uByteToBuf(1); else sending->uByteToBuf(0); } else { sending->strToBuf(""); sending->uByteToBuf(0); sending->uByteToBuf(0); } return; } AREXPORT void ArServerMode::checkBroadcastModeInfoPacket(void) { if (ourActiveMode != ourBroadcastActiveMode || ourActiveModeLocked != ourBroadcastActiveModeLocked || ourActiveModeWillUnlockIfRequested != ourBroadcastActiveModeWillUnlockIfRequested) { ArNetPacket sending; buildModeInfoPacket(&sending); myServer->broadcastPacketTcp(&sending, "getModeInfo"); } ourBroadcastActiveMode = ourActiveMode; ourBroadcastActiveModeLocked = ourActiveModeLocked; ourBroadcastActiveModeWillUnlockIfRequested = ourActiveModeWillUnlockIfRequested; } AREXPORT ArServerMode* ArServerMode::getActiveMode(void) { return ourActiveMode; } AREXPORT ArServerMode* ArServerMode::getLastActiveMode() { return ourLastActiveMode; } AREXPORT ArServerModeIdle* ArServerMode::getIdleMode(void) { return ourIdleMode; } AREXPORT std::list *ArServerMode::getRequestedActivateModes(void) { return &ourRequestedActivateModes; } AREXPORT void ArServerMode::getModeBusy(ArServerClient *client, ArNetPacket *packet) { ArNetPacket sending; sending.byteToBuf(ourIsBusy); client->sendPacketTcp(&sending); }