Skip to content

Commit

Permalink
Merge pull request #24 from JuliaRobotics/feat/3Q20/fix21/interval
Browse files Browse the repository at this point in the history
add interval animation
  • Loading branch information
dehann authored Aug 30, 2020
2 parents 9a19f3d + e7a9b19 commit e10a236
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 4 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ addons:
apt:
packages:
- hdf5-tools
- graphviz

arch:
- amd64
Expand All @@ -29,7 +30,7 @@ jobs:
fast_finish: true
allow_failures:
- julia: nightly
# - os: osx
- os: osx
# - arch: arm64

notifications:
Expand Down
6 changes: 4 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name = "FunctionalStateMachine"
uuid = "3e9e306e-7e3c-11e9-12d2-8f8f67a2f951"
keywords = ["state machine"]
desc = "Functional state machine with stepping and visualization tools."
version = "0.2.1"
version = "0.2.2"

[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Expand All @@ -17,7 +17,9 @@ Requires = "0.5, 0.6, 0.7, 0.8, 0.9, 0.10, 1"
julia = "0.7, 1"

[extras]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]
test = ["Test", "Graphs", "Dates"]
138 changes: 137 additions & 1 deletion src/StateMachineAnimation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ export
drawStateTransitionStep,
drawStateMachineHistory,
animateStateMachineHistoryByTime,
animateStateMachineHistoryByTimeCompound
animateStateMachineHistoryByTimeCompound,
animateStateMachineHistoryIntervalCompound


"""
Expand Down Expand Up @@ -287,3 +288,138 @@ function animateStateMachineHistoryByTimeCompound(hists::Dict{Symbol, Vector{Tup
end

end

# count the total number of transitions contained in hists
function getTotalNumberSteps( hists::Dict{Symbol, Vector{Tuple{DateTime, Int, <: Function, T}}} ) where T
totSteps = 0
for (whId, hist) in hists, hi in hist
totSteps += 1
end
return totSteps
end

# point to the start step among all history steps
function getFirstStepHist( hists::Dict{Symbol, Vector{Tuple{DateTime, Int, <: Function, T}}} ) where T
startTime = now()
maxTime = DateTime(0)
# NOTE, this whichId=:null is super important to ensure rendering loop can exit properly
whichId, whichStep = :null, 0
for (whId, hist) in hists, (st,hi) in enumerate(hist)
if hi[1] < startTime
# new starting point indicator
whichId = whId
whichStep = st
startTime = hi[1]
end
if maxTime < hi[1]
maxTime = hi[1]
end
end
return whichId, whichStep, startTime, maxTime
end

# give the next step, closest in time and that has not previously been added to `prevList`.
# Also update prevList
function getNextStepHist!(hists,
intuple::Tuple{Symbol, Int, DateTime},
maxTime::DateTime,
prevList::Dict{Symbol, Vector{Int}} )
#
oldId, oldStep, oldT = intuple

whichId, whichStep, newT = :null, 0, maxTime
for (whId, hist) in hists, (st,hi) in enumerate(hist)
# make sure all options are populated in previous list tracker
if !haskey(prevList, whId) prevList[whId] = Int[]; end
if oldT < hi[1] && Millisecond(0) <= (hi[1] - oldT) < (newT-oldT) &&
!(st in prevList[whId]) # must be a different step than before
# new closest next step
whichId = whId
whichStep = st
newT = hi[1]
end
end

# register this step has previously been taken
if !haskey(prevList, whichId)
prevList[whichId] = Int[]
end
push!(prevList[whichId], whichStep)

return whichId, whichStep, newT
end


# for slower movies, use a slower fps
# run(`ffmpeg -r 10 -i /tmp/caesar/csmCompound/csm_%d.png -c:v libtheora -vf fps=5 -pix_fmt yuv420p -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" -q 10 /tmp/caesar/csmCompound/out.ogv`)
# @async run(`totem /tmp/caesar/csmCompound/out.ogv`)
function animateStateMachineHistoryIntervalCompound(hists::Dict{Symbol, Vector{Tuple{DateTime, Int, <: Function, T}}};
interval::Int=2, # frames
# frames::Int=100,
folder="animatestate",
title::String="",
show::Bool=false,
clearstale::Bool=true,
rmfirst::Bool=true ) where T
#
# Dict{Symbol, Vector{Symbol}}
stateVisits = Dict{Symbol, Vector{Symbol}}()
allStates = Vector{Symbol}()
for (csym,hist) in hists
stateVisits, allStates = histStateMachineTransitions(hist,allStates=allStates, stateVisits=stateVisits )
end

#
vg, lookup = histGraphStateMachineTransitions(stateVisits, allStates)

# total draw time and step initialization
# totT = stopT - startT
# totT = Millisecond(round(Int, 1.05*totT.value))
# histsteps = ones(Int, length(hists))

# clear any stale state
clearstale ? clearVisGraphAttributes!(vg) : nothing

totSteps = getTotalNumberSteps(hists)
whId, fsmStep, aniT, maxTime = getFirstStepHist(hists)
prevList = Dict{Symbol, Vector{Int}}()
latestList = Dict{Symbol, Int}(whId => fsmStep)

frameCount = 0
# loop across time
@showprogress "exporting state machine images, $title " for stepCount in 1:totSteps
# which step among the hist fsms is next
if 1 < stepCount
# skip first would-be repeat
whId, fsmStep, aniT = getNextStepHist!(hists, (whId, fsmStep, aniT), maxTime, prevList)
latestList[whId] = fsmStep
end

# loop over all state "known" machines
for (csym, lstep) in latestList
# modify vg for each history
csym == :null ? break : nothing
lbl = getStateLabel(hists[csym][lstep][3])
vertid = lookup[lbl]
setVisGraphOnState!(vg, vertid, appendxlabel=string(csym)*",")
end

# and draw as many frames for that setup
for itr in 1:interval
# increment frame counter
frameCount += 1
# finally render one frame
renderStateMachineFrame(vg,
frameCount,
title=title,
show=false,
folder=folder,
timest=string(split(string(aniT),' ')[1]),
rmfirst=false )
#
end
# clear current frame in prep for the next interval
clearVisGraphAttributes!(vg)
end

end
13 changes: 13 additions & 0 deletions test/testStateMachine.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@

using FunctionalStateMachine
using Graphs
using Dates
using Test

## User state functions
Expand Down Expand Up @@ -50,4 +52,15 @@ while statemachine(nothing, verbose=true); end
end


@testset "test recording and rendering of an FSM run" begin

statemachine = StateMachine{Nothing}(next=foo!)
while statemachine(nothing, recordhistory=true); end

hists = Dict{Symbol,Vector{Tuple{DateTime,Int,Function,Nothing}}}(:first => statemachine.history)

animateStateMachineHistoryIntervalCompound(hists, interval=1)

end

#

0 comments on commit e10a236

Please sign in to comment.