Skip to content
Snippets Groups Projects
HTNBehaviors.py 11.83 KiB
import mtry.cxxi.model.HierarchicalTaskNetwork.HTNUtilities as HTNUtilities
from java.util import ArrayList
import cxxi.model.objects.holders.CMHolder as CMHolder
import cxxi.model.behavior.OrderUtilities as OrderUtilities

import mtry.cxxi.model.HierarchicalTaskNetwork.GoalContainer as GoalContainer
import cxxi.model.behavior.PythonUtilities as PythonUtilities

import simkit.Schedule as Schedule

#from UtilityFuncsExp import HTNBorg as borg

__HTN_PATH__ = "HTN/Trees/"
__BASIC_MOVE_PATH__ = "HTN/Trees/BasicMove.xml"
__NETWORK_MOVE_PATH__ = "HTN/Trees/NetworkMove.xml"

borg = None

# creates a way point at the specified entities location (entity is a cxxi entity)
def CreateWPAtCurrentLoc(entity):
    wp = HTNUtilities._py_addControlMeasure(entity.getAssignedName(), entity.getPhysicalState().getGroundTruthLocation())
    return wp

# used to calculate if an entity is talking on its radios at whatever time this
# method is called. Not used currently
def IsTransmitting(unitName, isCommander):
    _InitBorgInternal()

    # they can't transmit if they aren't doing anything yet
    if not unitName in borg.redUnitActivity:
        return False

    # is transmitting
    activityName = borg.redUnitActivity[unitName]
    activityTime = int(borg.redUnitActivityTime[unitName])
    elapsedTime = int(Schedule.getSimTime()) - activityTime
    
    if activityName == "MOVE_TO_DEPLOYMENT_SITE" and not isCommander and (elapsedTime >= 300 and elapsedTime <= 310):
        return True
    elif activityName == "OPERATE_AT_SITE" and isCommander and (elapsedTime >= 3600 and elapsedTime <= 3610):
        return True
    else:
        return False

# sets a red units activity. This is the activity reported in the loggers
def SetRedUnitActivity(unitName, activityName):
    _InitBorgInternal()

    borg.redUnitActivity[unitName]=activityName
    borg.redUnitActivityTime[unitName]=Schedule.getSimTime()

# sets a specific entities activity. This is reported in loggers
# Note this is used to create activities for specific entities in a unit
# for example a unit that has only one entity with radar would:
# 1) set the units activity to 'deployed' and the one entity with radar
# could override the 'deployed' status to 'using radar'
def SetRedEntityActivityOverride(entityName, activityName):
    _InitBorgInternal()

    if activityName != None:
        borg.redEntityActivityOverride[entityName]=activityName
        borg.redEntityActivityOverride[entityName]=Schedule.getSimTime()
    else:
        del borg.redEntityActivityOverride[entityName]

# Reads from a list of allWPs
def GetRandomPoint():
    _InitBorgInternal()

    int_num = PythonUtilities._py_getRandomNumber("UNIFORM", [0.0, 1.0])
    int_num *= borg.allWPs.size()
    int_num = int(round(int_num))

    destName = borg.allWPs.get(int_num).getName()

    return destName

# returns a true or false depending on whether the drawn number is
# less than the probability
def GetRandomChance(probability):
    int_num = PythonUtilities._py_getRandomNumber("UNIFORM", [0.0, 1.0])
    return int_num < probability

# used to initialize the borg in this script. any function that calls
# the borg should call this first to make sure the borg is available
# this is used to overcome some jython/python/cxxi shortcomings
# you don't normally need to do this in a python script
def _InitBorgInternal():
    global borg

    if borg == None:
        borgName = GoalContainer.getBorgName()
        impStr = "from "+borgName+" import HTNBorg"
        exec(impStr)
        borg = HTNBorg()
        print "Borg created"

# test the borg is available
def TestBorg():
    _InitBorgInternal()
    print "Test borg=",borg.allWPs.size()

# gets a random whole number between minNum and maxNum (inclusive)
# the value is rounded before returning to make sure that
# small and large random vars return min and max (without
# that those values become extremely rare as only a random
# value of 0 or 1 would generate them
# also note this uses the cxxi random stream so its safe to call inside
# behaviors
def GetRandomInt(minNum, maxNum):
    int_num = PythonUtilities._py_getRandomNumber("UNIFORM", [0.0, 1.0])
    len = maxNum - minNum
    size = round(int_num * len) + minNum
    return size

# convenience method to convert a python list into a randomized java list
# safe to call in behaviors
def ConvertToJavaListRandom(l, minNum, maxNum):
    int_num = PythonUtilities._py_getRandomNumber("UNIFORM", [0.0, 1.0])
    len = maxNum - minNum
    size = round(int_num * len) + minNum
    al = ArrayList()
    i = 0
    for o in l:
        if i<=size:
            al.add(o)
        i+=1

    return al

# convenience method to conver a python list to a java list
def ConvertToJavaList(l):
    al = ArrayList()
    for o in l:
        al.add(o)

    return al

# convenience method to convert a java list to a python list
def ConvertJavaToList(l):
    al = []
    for i in range(l.size()):
        al.append(l.get(i))

    return al

# sends the behavior message to the specified entity
def SendUnitEventDelay(entityName, msg, data, delay):
	params = ArrayList()
	for d in data:
		params.add(d)
	HTNUtilities._py_scheduleEventForUnit(entityName, msg, delay, params)

# create a basic move tree and assign it
def CreateBasicMove(locList, speed, formation, useOr, orient, entityToMove, toNotify, msgTo, goalStack, delay):
    al = ArrayList()

    for loc in locList:
        al.add(loc)

    if formation == None:
        formation = "NO_FORMATION"

    goalStackName = goalStack
    if goalStack == None:
        goalStackName = "default"

    addSpecificGoal(
        entityToMove,
        delay,
        __BASIC_MOVE_PATH__,
        goalStackName,
        [al, speed, toNotify, msgTo, formation, orient, useOr],
        None)

# create a network move tree and assign it
def CreateNetworkMove(currentLoc, wpNames, speed, formation, entityToMove, toNotify, msgTo, goalStack, delay):
    al = ArrayList()
    if currentLoc != None:
        al.add(currentLoc)

    for wpName in wpNames:
        wp = CMHolder.retrieveControlMeasureByName(wpName)
        al.add(wp.getLocation())

    if formation == None:
        formation = "NO_FORMATION"

    goalStackName = goalStack
    if goalStack == None:
        goalStackName = "default"

    addSpecificGoal(
        entityToMove,
        delay,
        __NETWORK_MOVE_PATH__,
        goalStackName,
        [al, speed, toNotify, msgTo, formation],
        None)

# sends the behavior message to the specified entity
def SendEntityEventDelay(entityName, msg, data, delay):
	params = ArrayList()
	for d in data:
		params.add(d)
	HTNUtilities._py_scheduleEvent(entityName, msg, delay, params)

# sends the behavior message to the specified entity
def SendEntityEvent(entityName, msg, data):
	params = ArrayList()
	for d in data:
		params.add(d)
	HTNUtilities._py_scheduleEvent(entityName, msg, 5.0, params)

# sends the behavior message to the specified entity
def SendBehaviorMsg(entityName, msg, data):
	params = ArrayList()
	for d in data:
		params.add(d)
	HTNUtilities._py_scheduleEvent(entityName, "GoalTracker_HTNBehaviorMsg", 5.0, params)

# creates a 2d array of specified dimensions
# width = columns, height = rows
def CreateArray(width, height):
	gridMap=[]
	for i in range(width):
		gridMap.append([])
		for j in range(height):
			gridMap[i].append(i+j)
	return gridMap

# gets the neighbors of an element in a 2d array
# assumes neighbors are in cardinal directions
def GetArrayNeighbors(net, x, y, xlim, ylim):
	neighbors = []
#	directions = [-1, 0, 0, -1, 0, 1, 1, 0]
	directions = [-1, 0, 0, -1, 0, 1, 1, 0, -1, -1, -1, 1, 1, -1, 1, 1]
	for i in range(len(directions)/2):
		nx = x + directions[i*2]
		ny = y + directions[i*2+1]
		if nx >= 0 and nx < xlim and ny >= 0 and ny < ylim:
			neighbors.append(net[nx][ny])

	return neighbors

# convert a python list to a java list
def ConvertToJavaList(larr):
	l = ArrayList()
	for la in larr:
		l.add(la)
	return l

# convert python list of GraphPoint to name
def GraphPointDesc(l):
	desc=[]
	for ll in l:
		desc.append(ll.getWayPoint().getName())
	return desc

###################################################################################################
# generates the full argument list for addGoal methods without the side-effect of modifying the
# passed in args
#
def _buildGoalArgs(goalPath, goalStack, var, args):
    result = [goalPath, goalStack, var]

    for arg in args:
        result.append(arg)

    return result

###################################################################################################
# adds a goal to the specified entity. This is done by publishing the GoalTracker_AddGoal
# event so a behavior needs to be setup to handle this. (There's usually a default behavior setup to
# handle this)
#
# entityName: String, name of the entity to add to
# delay: double, time in seconds from now to send the event
# goalPath: String, path to the xml file that contains the HTN plan
# stackName: String, name of the stack to add the goal to
# args: List, list of arguments to send with the event
# var: HashMap<String, Object>, variable hash map to send with the event
def addSpecificGoal(entityName, delay, goalPath, stackName, args, var):
    arguments = _buildGoalArgs(goalPath, stackName, var, args)

    OrderUtilities.getEntityByName(entityName).getDM().waitDelay("GoalTracker_AddGoal", delay, arguments)

###################################################################################################
# adds a goal to the specified entity. This is done by publishing the GoalTracker_AddGoal
# event so a behavior needs to be setup to handle this. (There's usually a default behavior setup to
# handle this)
#
# entityName: String, name of the entity to add to
# delay: double, time in seconds from now to send the event
# goalPath: String, path to the xml file that contains the HTN plan
# args: List, list of arguments to send with the event
# var: HashMap<String, Object>, variable hash map to send with the event
def addGoal(entityName, delay, goalPath, args, var):
    arguments = _buildGoalArgs(goalPath, None, var, args)

    OrderUtilities.getEntityByName(entityName).getDM().waitDelay("GoalTracker_AddGoal", delay, arguments)

###################################################################################################
# adds a goal to everyone in the specified unit. This is done by publishing the GoalTracker_AddGoal
# event so a behavior needs to be setup to handle this. (There's usually a default behavior setup to
# handle this)
#
# unitName: String, name of the unit to add to
# delay: double, time in seconds from now to send the event
# goalPath: String, path to the xml file that contains the HTN plan
# stackName: String, name of the stack to add the goal to
# args: List, list of arguments to send with the event
# var: HashMap<String, Object>, variable hash map to send with the event
def addSpecificGoalToUnit(unitName, delay, goalPath, stackName, args, var):
    unit = UtilityFuncs.getUnitByName(unitName)
    arguments = _buildGoalArgs(goalPath, stackName, var, args)

    for member in unit.getMembers():
        member.getDM().waitDelay("GoalTracker_AddGoal", delay, arguments)

###################################################################################################
# adds a goal to everyone in the specified unit. This is done by publishing the GoalTracker_AddGoal
# event so a behavior needs to be setup to handle this. (There's usually a default behavior setup to
# handle this)
#
# unitName: String, name of the unit to add to
# delay: double, time in seconds from now to send the event
# goalPath: String, path to the xml file that contains the HTN plan
# args: List, list of arguments to send with the event
# var: HashMap<String, Object>, variable hash map to send with the event
def addGoalToUnit(unitName, delay, goalPath, args, var):
    unit = UtilityFuncs.getUnitByName(unitName)
    arguments = _buildGoalArgs(goalPath, None, var, args)

    for member in unit.getMembers():
        member.getDM().waitDelay("GoalTracker_AddGoal", delay, arguments)