-
Notifications
You must be signed in to change notification settings - Fork 15
/
delayed.cljc
124 lines (108 loc) · 3.71 KB
/
delayed.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
(ns statecharts.delayed
(:require [clojure.walk :refer [postwalk]]
[statecharts.utils :as u]))
(defprotocol IScheduler
(schedule [this fsm state event delay])
(unschedule [this fsm state event]))
(defn scheduler? [x]
(satisfies? IScheduler x))
(def path-placeholder [:<path>])
(defn delay-fn-id [d]
(if (int? d)
d
#?(:cljs (aget d "name")
:clj (str (type d)))))
(defn generate-delayed-events [delay txs]
(let [event [:fsm/delay
path-placeholder
;; When the delay is a context function, after each
;; reload its value of change, causing the delayed
;; event can't find a match in :on keys. To cope with
;; this we extract the function name as the event
;; element instead.
(delay-fn-id delay)]]
;; (def vd1 delay)
{:entry {:action :fsm/schedule-event
:event-delay delay
:event event}
:exit {:action :fsm/unschedule-event
:event event}
:on [event (mapv #(dissoc % :delay) txs)]}))
#_(generate-delayed-events 1000 [{:delay 1000 :target :s1 :guard :g1}
{:delay 1000 :target :s2}])
#_(group-by odd? [1 2 3])
;; statecharts.impl/T_DelayedTransition
;; =>
#_[:map
[:entry]
[:exit]
[:on]]
(defn derived-delay-info [delayed-transitions]
(doseq [dt delayed-transitions]
(assert (contains? dt :delay)
(str "no :delay key found in" dt)))
(->> delayed-transitions
(group-by :delay)
;; TODO: the transition's entry/exit shall be grouped by delay,
;; otherwise a delay with multiple targets (e.g. with guards)
;; would result in multiple entry/exit events.
(map (fn [[delay txs]]
(generate-delayed-events delay txs)))
(reduce (fn [accu curr]
(merge-with conj accu curr))
{:entry [] :exit [] :on []})))
#_(derived-delay-info [:s1] [{:delay 1000 :target :s1 :guard :g1}
{:delay 2000 :target :s2}])
(defn insert-delayed-transitions
"Translate delayed transitions into internal entry/exit actions and
transitions."
[node]
;; node
(let [after (:after node)]
(if-not after
node
(let [{:keys [entry exit on]} (derived-delay-info after)
on (into {} on)
vconcat (fn [xs ys]
(-> (concat xs ys) vec))]
(-> node
(update :entry vconcat entry)
(update :exit vconcat exit)
(update :on merge on))))))
(defn replace-path [path form]
(if (nil? form)
form
(postwalk (fn [x]
x
(if (= x path-placeholder)
path
x))
form)))
(defn replace-delayed-place-holder
([fsm]
(replace-delayed-place-holder fsm []))
([node path]
(let [replace-path (partial replace-path path)]
(cond-> node
(:on node)
(update :on replace-path)
(:entry node)
(update :entry replace-path)
(:exit node)
(update :exit replace-path)
(:states node)
(update :states
(fn [states]
(u/map-kv (fn [id node]
[id
(replace-delayed-place-holder node (conj path id))])
states)))))))
#_(replace-delayed-place-holder
{:on {[:fsm/delay [:<path>] 1000] :s2}
:states {:s3 {:on {[:fsm/delay [:<path>] 1000] :s2}
:entry [{:fsm/type :schedule-event
:fsm/delay 1000
:fsm/event [:fsm/delay [:<path>] 1000]}]}}
:entry [{:fsm/type :schedule-event
:fsm/delay 1000
:fsm/event [:fsm/delay [:<path>] 1000]}]} [:root])