Skip to content

Instantly share code, notes, and snippets.

@LaxLacks LaxLacks/mytasks.iss
Last active Mar 11, 2019

Embed
What would you like to do?
objectdef mytasks_Controller
{
variable taskmanager TaskManager=${LMAC.NewTaskManager["mytaskstest"]}
method Initialize()
{
This:AddTaskType["{\"name\":\"my first task type\",\"object\":\"MyTasks\",\"method\":\"Task_Template\"}"]
}
method Shutdown()
{
LMAC.TaskManager["mytaskstest"]:Destroy
}
method AddTaskType(string jsonData)
{
variable int64 id
id:Set[${LMAC.NewTaskType["${jsonData.Escape}"].ID}]
if ${id}
{
echo "LMAC: Task Type ${id} added: ${LMAC.TaskType[${id}].Name.Escape}"
}
}
method Task_Template()
{
; Task_Retarget_Wrapper checks for a supplied "target" or "targets" property. If it exists, it wraps the Task in a unicast or multicast to send to the given target(s)
; These lines can be removed if this Task Type should not be retargetable
if ${This.Task_Retarget_Wrapper[Context]}
return
; Task Types will generally do different things depending whether the TaskState is Start, Continue, or Stop.
; For example, Start may initialize data, Continue may update data, and Stop may clean up data
switch ${Context.TaskState}
{
case Start
echo ${Context(type)} ${Context.Timestamp} ${Context.ElapsedMS} ${Context.Task} ${Context.TaskState} instant=${Context.Task.IsInstant} ${Context.Task.Args}
; Peform initialization here ...
break
case Continue
; echo ${Context(type)} ${Context.Timestamp} ${Context.ElapsedMS} ${Context.Task} ${Context.TaskState} instant=${Context.Task.IsInstant} ${Context.Task.Args}
; Perform repeating behaviors/checks here
break
case Stop
echo ${Context(type)} ${Context.Timestamp} ${Context.ElapsedMS} ${Context.Task} ${Context.TaskState} instant=${Context.Task.IsInstant} ${Context.Task.Args}
; Perform shutdown/cleanup here ...
break
}
}
; Retargets a Task, given a taskpulseargs (which comes from Context during Task execution)
member:uint RetargetTask(persistentref TaskPulseArgs)
{
variable uint subTaskID
variable jsonvalue castTask={}
; Generate a copy of the Task args, without target/targets properties, to send to the target
variable jsonvalue innerTask
innerTask:SetValue["${TaskPulseArgs.Task.Args.AsJSON.Escape[0]}"]
innerTask:Erase["target"]:Erase["targets"]
; Set the "task" property to be the targetless copy
castTask:Set["task","${innerTask.AsJSON.Escape[0]}"]
; Check if we're doing unicast (single target) or multicast (any number of targets)
if ${TaskPulseArgs.Task.Args.Get[target](exists)}
{
castTask:Set["type","\"unicast\""]
castTask:Set["target","\"${TaskPulseArgs.Task.Args.Get[target].Escape}\""]
}
elseif ${TaskPulseArgs.Task.Args.Get[targets](exists)}
{
castTask:Set["type","\"multicast\""]
castTask:Set["target","\"${TaskPulseArgs.Task.Args.Get[targets].Escape}\""]
}
else
{
; uhh.. no.
return 0
}
; Now we have a unicast or multicast task, with the specified target(s), and the inner Task. Go!
return ${TaskPulseArgs.Task.TaskManager.BeginTask["${castTask.AsJSON.Escape[0]}"].ID}
}
; returns TRUE if the task should be retargeted, FALSE if it should not.
member:bool Task_Retarget_Wrapper(persistentref TaskPulseArgs)
{
; even if there is an error, this is the only condition where we return FALSE
if !${TaskPulseArgs.Task.Args.Get[target](exists)} && !${TaskPulseArgs.Task.Args.Get[targets](exists)}
{
return FALSE
}
; subTaskID will be populated with the unicast/multicast Task ID
variable uint subTaskID
switch ${TaskPulseArgs.TaskState}
{
case Start
echo [Retarget] ${TaskPulseArgs(type)} ${TaskPulseArgs.Timestamp} ${TaskPulseArgs.ElapsedMS} ${TaskPulseArgs.Task} ${TaskPulseArgs.TaskState} instant=${TaskPulseArgs.Task.IsInstant} ${TaskPulseArgs.Task.Args}
; Here's our initialization stage
subTaskID:Set[${This.RetargetTask[TaskPulseArgs]}]
if !${subTaskID}
{
; Well, it didn't work.
TaskPulseArgs:SetError["Failed to retarget Task"]
TaskPulseArgs.Task:Stop
; it SHOULD be retargeted, but it is not. we still return TRUE.
return TRUE
}
; task is running. update the Args with the subTaskID so we can access it in Continue and Stop states later on
TaskPulseArgs.Task.Args:Set["subTaskID","${subTaskID}"]
break
case Continue
; echo [Retarget] ${TaskPulseArgs(type)} ${TaskPulseArgs.Timestamp} ${TaskPulseArgs.ElapsedMS} ${TaskPulseArgs.Task} ${TaskPulseArgs.TaskState} instant=${TaskPulseArgs.Task.IsInstant} ${TaskPulseArgs.Task.Args}
; Since we added subTaskID to the Args in Start, we can retrieve it here
subTaskID:Set[${TaskPulseArgs.Task.Args.Get["subTaskID"]}]
if !${subTaskID}
{
; sub-task is missing, this should have been handled by Start...?
TaskPulseArgs.Task:Stop
return
}
; we Continue until our subtask is no longer running
if !${LMAC.Task[${subTaskID}].IsRunning}
{
; It's not running, so we can Stop now
TaskPulseArgs.Task:Stop
}
; continue waiting
break
case Stop
echo [Retarget] ${TaskPulseArgs(type)} ${TaskPulseArgs.Timestamp} ${TaskPulseArgs.ElapsedMS} ${TaskPulseArgs.Task} ${TaskPulseArgs.TaskState} instant=${TaskPulseArgs.Task.IsInstant} ${TaskPulseArgs.Task.Args}
; To reach this point, the subtask may already be stopped on its own, or a condition that ends this Task early (e.g. duration) is occurring and will abort the subtask.
; Since we added subTaskID to the Args in Start, we can retrieve it here
subTaskID:Set[${LMAC.Task[${taskID}].Args.Get["subTaskID"]}]
if ${subTaskID}
{
; We can issue a Stop to the sub-task even if it is already stopped. If it's already stopped, this just does nothing, so it's okay.
LMAC.Task[${taskID}]:Stop
; Now that we're done with our sub-task, we can remove it from the Args so we know we don't need to touch it anymore.
TaskPulseArgs.Task.Args:Erase["subTaskID"]
}
break
}
return TRUE
}
; begin a test!
method Start()
{
TaskManager:BeginTask["{\"type\":\"my first task type\",\"duration\":1.0,\"my setting\":\"my setting value\"}"]
}
method TestRetarget(string target, bool multicast=TRUE)
{
if ${multicast}
{
TaskManager:BeginTask["{\"type\":\"my first task type\",\"targets\":\"${target.Escape}\",\"duration\":1.0,\"from\":\"${Session.Escape}\"}"]
}
else
{
TaskManager:BeginTask["{\"type\":\"my first task type\",\"target\":\"${target.Escape}\",\"duration\":1.0,\"from\":\"${Session.Escape}\"}"]
}
}
}
variable(global) mytasks_Controller MyTasks
function main()
{
MyTasks:Start
while 1
{
waitframe
}
}
atom atexit()
{
}
@LaxLacks

This comment has been minimized.

Copy link
Owner Author

commented Mar 10, 2019

Sample Output from running the script:

LMAC: Task Type 161 added: my first task type
taskpulseargs 271000984 0 164 Start instant=FALSE {"duration":1.000000,"my setting":"my setting value","type":"my first task type"}
taskpulseargs 271001984 1000 164 Stop instant=FALSE {"duration":1.000000,"my setting":"my setting value","type":"my first task type"}

@LaxLacks

This comment has been minimized.

Copy link
Owner Author

commented Mar 11, 2019

Updated with comments.

Also added Task_Retarget_Wrapper which can be used from a Task method to automatically wrap a "target" or "targets" property into a unicast or multicast task to perform remotely. If "target" or "targets" is not provided, the original, local behavior is used.

Task_Template is updated to include this functionality.

To test the retarget wrapper code (which requires Inner Space build 6419 or later) run mytasks.iss in multiple Inner Space sessions. Then issue a command like MyTasks:TestRetarget[is1] to send to is1, or MyTasks:TestRetarget[is2] to send to is2, etc.

Example output from the sender:
[Retarget] taskpulseargs 353503687 0 161 Start instant=FALSE {"duration":1.000000,"from":"is2","targets":"is1","type":"my first task type"}
[Retarget] taskpulseargs 353504687 1000 161 Stop instant=FALSE {"duration":1.000000,"from":"is2","subTaskID":164,"targets":"is1","type":"my
first task type"}

Example output from the remote session:
taskpulseargs 353503750 0 116 Start instant=FALSE {"duration":1.000000,"from":"is2","type":"my first task type"}
taskpulseargs 353504765 1015 116 Stop instant=FALSE {"duration":1.000000,"from":"is2","type":"my first task type"}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.