import java.util.Iterator; import java.util.Vector; import java.util.Collection; /** *

* Implementation of "Move in global frame" formation morphing algorithm * rewritten (nearly) from scratch to represent state of the algorithm circa * February 2007. *

* @author Michael Schuresko * @version %I%, %G% * @since 1.1 */ public class MorphAgentGlbl implements IAgent { IMorphSpec m_shapeDesc; double m_lfVel; double m_lfTCom; double m_lfD1, m_lfD2; boolean m_bMotionParamsSet; boolean m_bVerbose; public MorphAgentGlbl() { m_bVerbose = false; m_lfVel = 1.0; m_bMotionParamsSet = false; } public MorphAgentGlbl(double lfRobotVelocity) { m_bVerbose = false; m_lfVel = lfRobotVelocity; m_shapeDesc = new MorphSpecGlblRect(); m_bMotionParamsSet = false; } public MorphAgentGlbl(MorphAgentGlbl src) { m_bVerbose = false; m_lfVel = src.m_lfVel; m_shapeDesc = src.m_shapeDesc; //hopefully these things // will not be dynamic! m_bMotionParamsSet = src.m_bMotionParamsSet; } /** * Override the default shape description */ public void setShapeDesc(IMorphSpec shapeDesc) { m_shapeDesc = shapeDesc; } public void setMotionParams(double lfVel, double lfTCom, double lfRad) { m_bMotionParamsSet = true; m_lfVel = lfVel; m_lfTCom = lfTCom; m_lfD2 = lfRad; m_lfD1 = m_lfD2 - 4.0*m_lfTCom*m_lfVel; } /** * checks that a particular discrete state is valid for this agent */ public boolean checkStateValidity(StateBundle state) { try { TreeAgentState treeState = (TreeAgentState)state.getVars(); if(treeState != null) { return true; } else { return false; } } catch(java.lang.ClassCastException e) { return false; } } // Discrete state components /** *

Given the state of an agent, and the channels * it can send messages on, push the next set of messages * onto the appropriate channels. * Channels are assumed to be modified by this operation. * One weakness of this signature is that it requires * programmer discipline not to look at the continuous state * of agents other then your neighbors.

* @param discreteState discrete component of agent state * @param arrLfStateCont global vector of continuous state * @param nIdxStateOffset offset into global state vector * corresonding to this agent. * @param channelsToSendOn Iterator containing objects of type * CommLink corresponding to channels to send messages * on (neighbor indices can be ascertained from channels). * @see CommLink for type handled by iterator */ public void getMsgs(ILogicVarBundle discreteState, IDiscreteDynamicsCallback dynCallback, double arrLfStateCont[], double lfCurrTime, int nIdxStateOffset, Iterator channelsToSendOn) { try { dynCallback.doCallback(discreteState, arrLfStateCont); IMsg msg = (IMsg)(new TreeAgentState((TreeAgentState)discreteState)); while(channelsToSendOn.hasNext()) { CommLink linkSendOn = (CommLink)channelsToSendOn.next(); if(linkSendOn != null && linkSendOn.queue() != null) { linkSendOn.queue().sendMsg(msg); } } } catch(java.lang.ClassCastException e) { // send nothing } } /** *

Given the previous state of an agent, and the channels * to recieve messages from, recieve messages (destructively) * and return new state (non-destructively).

* @param statePrev previous state (do not modify) * @param arrLfStateCont vector of all continuous states (do not modify) * @param nIdxStateOffset offset into arrLfStateCont corresponding to * start of this agents continuous variables * @param channelsRecieveFrom iterator storing CommLink * classes corresponding incoming channels * @see CommLink for type handled by iterator * @return new state bundle containing discrete state of agent * and new control function */ public StateBundle updateState(ILogicVarBundle statePrev, double arrLfStateCont[], double lfCurrTime, int nIdxStateOffset, IEnvironment env, Iterator channelsReceiveFrom) { if(!m_bMotionParamsSet) { System.out.println("************************************"); System.out.println("** Error! Motion params not set **"); } ILogicVarBundle stateNext = getNextState(statePrev, channelsReceiveFrom, nIdxStateOffset/2); IControlFunc controlFuncNext = getNextControlFunc((TreeAgentState)stateNext, arrLfStateCont, nIdxStateOffset); return new StateBundle(stateNext, controlFuncNext); } class SelectMsgMatchingId implements IFuncObj { int m_nIdToMatch; public SelectMsgMatchingId(int nId) { m_nIdToMatch = nId; } public Boolean doFunc(IMsg msg) { try { ILogicVarBundle vars = (ILogicVarBundle)msg; return new Boolean(vars.getId() == m_nIdToMatch); } catch(java.lang.ClassCastException e) { return new Boolean(false); } } } class SelectMsgMatchingDepth implements IFuncObj { int m_nDepToMatch; public SelectMsgMatchingDepth(int nDep) { m_nDepToMatch = nDep; } public Boolean doFunc(IMsg msg) { try { TreeAgentState vars = (TreeAgentState)msg; return new Boolean(vars.getDepthEst() == m_nDepToMatch); } catch(java.lang.ClassCastException e) { return new Boolean(false); } } } class SelectMsgWithParent implements IFuncObj { int m_nIdToMatch; public SelectMsgWithParent(int nId) { m_nIdToMatch = nId; } public Boolean doFunc(IMsg msg) { try { TreeAgentState vars = (TreeAgentState)msg; return new Boolean(vars.getParentId() == m_nIdToMatch); } catch(java.lang.ClassCastException e) { return new Boolean(false); } } } class SelectMsgWithPropParent implements IFuncObj { int m_nIdToMatch; public SelectMsgWithPropParent(int nId) { m_nIdToMatch = nId; } public Boolean doFunc(IMsg msg) { try { TreeAgentState vars = (TreeAgentState)msg; return new Boolean(vars.getParentProp() == m_nIdToMatch); } catch(java.lang.ClassCastException e) { return new Boolean(false); } } } class SelectMsgWithTrueVar implements IFuncObj { Integer m_nObjVarIdx; public SelectMsgWithTrueVar(int nVarIdx) { m_nObjVarIdx = new Integer(nVarIdx); } public Boolean doFunc(IMsg msg) { try { TreeAgentState vars = (TreeAgentState)msg; Boolean bObjResult = vars.getBoolVar(m_nObjVarIdx); if(bObjResult == null) { return new Boolean(false); } return bObjResult; } catch(java.lang.ClassCastException e) { return new Boolean(false); } } } ILogicVarBundle getNextState(ILogicVarBundle statePrev, Iterator channelsReceiveFrom, int nAgentId) { TreeAgentState treeStateNext = null; try { TreeAgentState treeStatePrev = (TreeAgentState)statePrev; treeStateNext = new TreeAgentState(treeStatePrev); } catch(java.lang.ClassCastException e) { System.out.println("Error in class cast in MorphAgentGlbl"); return null; } treeStateNext.setTargetConfig(m_shapeDesc.getTopoDepth(nAgentId), m_shapeDesc.getTopoFirstVisit(nAgentId), m_shapeDesc.getTopoLastVisit(nAgentId), m_shapeDesc.getNumChildren(nAgentId)); if(m_bVerbose) { if(treeStateNext.getId() == 0) { System.out.println("I am root!"); } else { System.out.println("I am "+treeStateNext.getId()); } System.out.println("Anc == (" + treeStateNext.getFirstVisit() + "," + treeStateNext.getTargDepth() + "," + treeStateNext.getLastVisit() + ")"); } if(! treeStateNext.getDfsFinished()) { treeStateNext = doDfs(treeStateNext, channelsReceiveFrom, nAgentId); treeStateNext.incrRound(); return treeStateNext; } IFuncObj funcSibl = new SelectMsgMatchingDepth(treeStateNext.getDepthEst()); IFuncObj funcChildren = new SelectMsgWithParent(treeStateNext.getId()); IFuncObj funcParent = new SelectMsgMatchingId(treeStateNext.getParentId()); IFuncObj funcPropParent = new SelectMsgMatchingId(treeStateNext.getParentProp()); IFuncObj funcVar2True = new SelectMsgWithTrueVar(2); IFuncObj funcCntFinalChilds = new BoolFuncAggregators.BoolFuncAnd(funcChildren,funcVar2True); IFuncObj funcOther = new BoolFuncTrue(); // slight innefficiency introduced here through // looking through copies of the same list multiple times // However -- ease of coding should be greatly improved Vector msgVec = new Vector(); Collection msgCollection; msgCollection = AgentMsgHelpers.accumMsgs(channelsReceiveFrom, msgVec); AgentMsgHelpers helperInstance = new AgentMsgHelpers(); Iterator iParent = helperInstance.getFilteredMsgs(funcParent, msgCollection); TreeAgentState stateParent = null; if(treeStateNext.getId() == 0) { treeStateNext.insertBoolVar(new Integer(0), true); } if(iParent.hasNext()) { IMsg msgParent = iParent.next(); stateParent = (TreeAgentState)msgParent; if(stateParent.isAncest(treeStateNext)) { treeStateNext.insertBoolVar(new Integer(0), true); } } else if(treeStateNext.getId() != 0) { System.out.println("Agent "+treeStateNext.getId()+ " has lost touch with parent ("+ treeStateNext.getParentId()+")"); treeStateNext.incrRound(); return treeStateNext; // treeStateNext.insertBoolVar(new Integer(0), true); // var 0 is always true for root. } Iterator iChildren = helperInstance.getFilteredMsgs(funcChildren, msgCollection); //Boolean bObjFinalMove = // treeStateNext.getBoolVar(new Integer(2)); Boolean bObjAttachedToAnc = treeStateNext.getBoolVar(new Integer(0)); Boolean bObjExpand = treeStateNext.getBoolVar(new Integer(1)); //boolean bFinalMove = (bObjFinalMove != null && // bObjFinalMove.booleanValue()); boolean bAttachedToAnc = (bObjAttachedToAnc != null && bObjAttachedToAnc.booleanValue()); boolean bExpand = (bObjExpand != null && bObjExpand.booleanValue()); if(bExpand && treeStateNext.getId() != 0) { if(treeStateNext.getTargDepth() == stateParent.getTargDepth()+1) { int nCntChilds = helperInstance.getCountMsg(funcCntFinalChilds, msgCollection); int nTargChilds = treeStateNext.getTargNChildren(); if(nCntChilds == nTargChilds) { treeStateNext.insertBoolVar(new Integer(2), true); } else { treeStateNext.insertBoolVar(new Integer(2), false); } } else { treeStateNext.insertBoolVar(new Integer(2), false); } } if(m_bVerbose && !bAttachedToAnc && treeStateNext.getParentId() == 0) { System.out.println("Agent "+treeStateNext.getId()+ " is attached to root with bad anc info"); System.out.println("Anc == (" + treeStateNext.getFirstVisit() + "," + treeStateNext.getTargDepth() + "," + treeStateNext.getLastVisit() + ")"); } // check whether we're in "expand" state yet if(bAttachedToAnc) { // && !bExpand) { bExpand = true; while(bExpand && iChildren.hasNext()) { IMsg childMsg = iChildren.next(); TreeAgentState treeChildMsg = (TreeAgentState)childMsg; Boolean bChildExpand = treeChildMsg.getBoolVar(new Integer(1)); bExpand = bExpand && bChildExpand != null && bChildExpand.booleanValue() == true; } if(bExpand) { treeStateNext.insertBoolVar(new Integer(1), true); } } if(treeStateNext.getCurrRound() == TreeAgentState.ROUND_UPDATE_DEPTH) { // do depth update // if(stateParent != null) // program SHOULD cause null pointer dereference if we cannot find parent if(treeStateNext.getId() != 0 && treeStateNext.getParentId() >= 0) { treeStateNext.updateDepthEst(stateParent); } } if(treeStateNext.getCurrRound() == TreeAgentState.ROUND_ATTACH) { // do check and attach if(treeStateNext.getParentProp() != treeStateNext.getParentId()) { Iterator iPropParent = helperInstance.getFilteredMsgs(funcPropParent, msgCollection); if(iPropParent.hasNext()) { TreeAgentState statePropParent = (TreeAgentState)iPropParent.next(); if(statePropParent.getDepthEst() < treeStateNext.getDepthEst() || statePropParent.getId() > treeStateNext.getId()) { treeStateNext.setParentId(treeStateNext.getParentProp()); } else { IFuncObj funcPropChildren = new SelectMsgWithPropParent(treeStateNext.getId()); Iterator iPropChildren = helperInstance.getFilteredMsgs(funcPropChildren, msgCollection); boolean bFoundSmallerPropChild = false; while(!bFoundSmallerPropChild && iPropChildren.hasNext()) { TreeAgentState statePropChild = (TreeAgentState)iPropChildren.next(); bFoundSmallerPropChild = (statePropChild.getId() < treeStateNext.getId()); } if(!bFoundSmallerPropChild) { treeStateNext.setParentId(treeStateNext.getParentProp()); } } } } } if(treeStateNext.getCurrRound() == TreeAgentState.ROUND_SET_PARLESS) { // do set parent-less indicator if(treeStateNext.getId() == 0) { treeStateNext.setParDepLess(true); } else if(treeStateNext.getParentId() >= 0) { treeStateNext.setParDepLess(stateParent); // again, SHOULD cause error if stateParent is null } } // done checking for expand state if(treeStateNext.getCurrRound() == TreeAgentState.ROUND_PROPOSE_NEW && treeStateNext.getId() != 0) { // do topology re-arrangements int nMaxFinalDepthAttach = -1; int nIdAttach = -1; IMsg msgConsider; TreeAgentState treeStateConsider; if(true) { Iterator iSiblings = helperInstance.getFilteredMsgs(funcSibl, msgCollection); while(iSiblings.hasNext()) { msgConsider = iSiblings.next(); treeStateConsider = (TreeAgentState)msgConsider; if(treeStateConsider.isAncest(treeStateNext) && treeStateConsider.getParDepLess() == true) { if(treeStateConsider.getTargDepth() > nMaxFinalDepthAttach && treeStateConsider.getParDepLess()) { nMaxFinalDepthAttach = treeStateConsider.getTargDepth(); nIdAttach = treeStateConsider.getId(); } } } // end while(iSiblings.hasNext()) } // end if(bExpand) Iterator iOther = helperInstance.getFilteredMsgs(funcOther, msgCollection); while(iOther.hasNext()) { msgConsider = iOther.next(); treeStateConsider = (TreeAgentState)msgConsider; if(treeStateConsider.isAncest(treeStateNext)) { Boolean bObjAncExpanding = treeStateConsider.getBoolVar(new Integer(1)); boolean bAncExpanding = bObjAncExpanding != null && bObjAncExpanding.booleanValue() == true; if(treeStateConsider.getTargDepth() > nMaxFinalDepthAttach && (bAncExpanding == bExpand || treeStateConsider.getId() == 0 )) { if(treeStateConsider.getDepthEst() < treeStateNext.getDepthEst() || treeStateConsider.getId() == stateParent.getId()) { nMaxFinalDepthAttach = treeStateConsider.getTargDepth(); nIdAttach = treeStateConsider.getId(); } } } } // while iOther.hasNext if(nMaxFinalDepthAttach < 0 && stateParent != null) { // now find the parents parent, if its here IFuncObj funcParentsPar = new SelectMsgMatchingId(stateParent.getParentId()); Iterator iParPar = helperInstance.getFilteredMsgs(funcParentsPar, msgCollection); if(iParPar.hasNext()) { msgConsider = iParPar.next(); treeStateConsider = (TreeAgentState)msgConsider; nIdAttach = treeStateConsider.getId(); } } if(nIdAttach >= 0) { treeStateNext.setParentProp(nIdAttach); } } treeStateNext.incrRound(); return treeStateNext; } IControlFunc getNextControlFunc(TreeAgentState stateNext, double arrLfStateCont[], int nIdxStateOffset) { if(!stateNext.getDfsFinished()) { if(m_bVerbose) { System.out.println("Agent "+stateNext.getId()+ " is doing nothing"); } return new ControlFuncDoNothing(2); } double [] arrLfMotionPoint = null; int nParentId = stateNext.getParentId(); double lfDistToParent = 0.0; lfDistToParent = arrLfStateCont[nParentId*2]- arrLfStateCont[nIdxStateOffset]; lfDistToParent *= lfDistToParent; double lfTmp = arrLfStateCont[nParentId*2+1]- arrLfStateCont[nIdxStateOffset+1]; lfDistToParent += lfTmp*lfTmp; if(lfDistToParent > m_lfD1*m_lfD1) { arrLfMotionPoint = new double[2]; arrLfMotionPoint[0] = arrLfStateCont[2*nParentId]; arrLfMotionPoint[1] = arrLfStateCont[2*nParentId+1]; if(m_bVerbose) { System.out.println("Moving towards parent!"); System.out.println("My offset is "+nIdxStateOffset+ " moving towards agent "+nParentId); System.out.println("From point ("+ arrLfStateCont[nIdxStateOffset] +","+arrLfStateCont[nIdxStateOffset+1]+ ") to point ("+ arrLfMotionPoint[0]+","+ arrLfMotionPoint[1]+")"); System.out.println("Agent "+stateNext.getId()+ " is moving towards parent with velocity " +m_lfVel); } return new ControlFuncMoveTowards(arrLfMotionPoint, m_lfVel, nParentId); } if(m_bVerbose) { System.out.println("I (agent " + nIdxStateOffset/2+ ") am at (" + arrLfStateCont[nIdxStateOffset]+","+ arrLfStateCont[nIdxStateOffset+1]+")"); } Boolean bFinalMove = stateNext.getBoolVar(new Integer(2)); if(bFinalMove != null && bFinalMove.booleanValue() == true) { // move to final position arrLfMotionPoint = m_shapeDesc.getFinalUv(nIdxStateOffset/2); if(m_bVerbose) { System.out.println("Agent "+stateNext.getId()+ " is converging on final position"); } return new ControlFuncMoveTowards(arrLfMotionPoint, m_lfVel); // m_shapeDesc } arrLfMotionPoint = m_shapeDesc.getFinalUv(0); return new ControlFuncMoveTowards(arrLfMotionPoint, m_lfVel); // return new ControlFuncDoNothing(2); } TreeAgentState doDfs(TreeAgentState stateToUpdate, Iterator channelsReceiveFrom, int nAgentId) { if(m_bVerbose) { System.out.println("Doing Dfs"); } if(stateToUpdate.getId() == 0) { stateToUpdate.setParentId(0); stateToUpdate.setDepthEst(0); } // boolean bFoundParent = false; if(stateToUpdate.getParentId() < 0) { // look for a valid parent if(m_bVerbose) { System.out.println("Looking for a parent"); } int nParentId = -1; int nDep = -2; if(m_bVerbose) { if(!channelsReceiveFrom.hasNext()) { System.out.println("Agent "+stateToUpdate.getId()+ " has no input channels"); } } while(channelsReceiveFrom.hasNext()) { CommLink currLink = channelsReceiveFrom.next(); IMsgChannel msgChannel = currLink.queue(); IMsg currMsg = msgChannel.readMsg(); if(currMsg != null) { TreeAgentState currTreeMsg = (TreeAgentState)currMsg; if(m_bVerbose) { System.out.println("Agent "+stateToUpdate.getId()+ " is looking at agent "+ currTreeMsg.getId()); } if(currTreeMsg.getParentId() >= 0) { if(m_bVerbose) { System.out.println("Found a prospective parent"); } if(nParentId < 0 || currTreeMsg.getDepthEst() < nDep && (!currTreeMsg.getDfsFinished() || currTreeMsg.getId() == 0)) { nParentId = currLink.from(); nDep = currTreeMsg.getDepthEst(); } } } } if(m_bVerbose) { System.out.println("Setting parent id to "+nParentId); } stateToUpdate.setParentId(nParentId); stateToUpdate.setDepthEst(nDep+1); } else { // wait for all neighbors to have parents // and all children to be done boolean bNeighbors = true; boolean bChildrenDone = true; while(channelsReceiveFrom.hasNext()) { CommLink currLink = channelsReceiveFrom.next(); IMsgChannel msgChannel = currLink.queue(); int nMyId = currLink.to(); IMsg currMsg = msgChannel.readMsg(); if(currMsg == null) { bNeighbors = false; } else { TreeAgentState currTreeMsg = (TreeAgentState)currMsg; if(currTreeMsg.getParentId() == nMyId && !currTreeMsg.getDfsFinished() ) { bChildrenDone = false; } if(currTreeMsg.getParentId() < 0) { bNeighbors = false; } if(currTreeMsg.getId() == stateToUpdate.getParentId()) { // bFoundParent = true; if(currTreeMsg.getDfsFinished()) { } } } } stateToUpdate.setDfsFinished(bNeighbors && bChildrenDone); } return stateToUpdate; } /** * in case the agent has some internal state, this * allows syncrhonized wrapper calls to cache a snapshot of the * agent at some rational point in time. * If the agent has no internal state, this function can just return * "this". */ public IAgent makeCopy() { return this; } }