-
Notifications
You must be signed in to change notification settings - Fork 0
Continuous Simulation Single Server System Example
Colin Kahn edited this page Sep 19, 2018
·
2 revisions

A simple machine simulation where jobs arrive, are loaded by an idle machine, processed, and unloaded. This example uses continuous simulation using clojure.core.async.
{:deps
{org.clojure/clojurescript {:mvn/version "1.10.238"}
org.clojars.protocol55/step {:mvn/version "0.2.0"}
org.clojure/core.async {:mvn/version "0.4.474"}
fsmviz {:mvn/version "0.1.3"}}}(ns simulation.core
(:require [clojure.spec.alpha :as s]
[clojure.core.async :as a]
[fsmviz.core :as fsmviz]
[protocol55.step.alpha :refer [stepdef]]))
(s/def ::initialize #{:initialize})
(s/def ::arrive #{:arrive})
(s/def ::load #{:load})
(s/def ::unload #{:unload})
(s/def ::m #{0 1})
(s/def ::q (s/int-in 0 1e9))
(s/def ::state (s/keys :req-un [::m ::q]))
(s/def :buffer-empty/q #{0})
(s/def :some-buffer/q (s/int-in 1 1e9))
(s/def :idle/m #{1})
(s/def :busy/m #{0})
(s/def :empty/state (s/and map? empty?))
(s/def :idle/state (s/keys :req-un [:idle/m :some-buffer/q]))
(s/def :idle.buffer-empty/state (s/keys :req-un [:idle/m :buffer-empty/q]))
(s/def :busy/state (s/keys :req-un [:busy/m :some-buffer/q]))
(s/def :busy.buffer-empty/state (s/keys :req-un [:busy/m :buffer-empty/q]))
(stepdef ::step
{:data-def step-data}
(:empty/state
::initialize (:idle/state))
(:idle/state
::arrive (:idle/state)
::load (:busy/state :busy.buffer-empty/state))
(:idle.buffer-empty/state
::arrive (:idle/state))
(:busy/state
::arrive (:busy/state)
::unload (:busy/state :idle/state :idle.buffer-empty/state))
(:busy.buffer-empty/state
::arrive (:busy/state)
::unload (:idle.buffer-empty/state)))
(def ta 500)
(def ts 200)
(defn idle? [{:keys [m]}]
(> m 0))
(defn buffer-empty? [{:keys [q]}]
(zero? q))
(def buffer-not-empty? (complement buffer-empty?))
(defn dispatch*
[state action]
(case action
:initialize
{:m 1 :q 3}
:arrive
(update state :q inc)
:load
(-> state
(update :m dec)
(update :q dec))
:unload
(update state :m inc)))
(defn dispatch
[state action]
(let [state' (dispatch* state action)
step [state action state']]
(assert (s/valid? ::step step)
(str (s/explain-str ::step step) " " (pr-str step)))
(println
(let [[state [action [state']]] (s/conform ::step step)]
[state action state']))
state'))
(defn work [state]
(a/go-loop []
(if (buffer-not-empty? @state)
(do
(swap! state dispatch :load)
(a/<! (a/timeout ts))
(swap! state dispatch :unload)
(recur)))))
(defn run []
(let [state (atom {})]
(swap! state dispatch :initialize)
(a/go-loop []
(swap! state dispatch :arrive)
(when (idle? @state)
(work state))
(a/<! (a/timeout ta))
(recur))))
(defn graphify-state [k]
(->> (clojure.string/split (namespace k) #"[\.-]")
(map clojure.string/capitalize)
(clojure.string/join)))
(defn generate-image! []
(fsmviz/generate-image
(mapcat
(fn [[state m]]
(mapcat
(fn [[action states']]
(map #(mapv keyword %&)
(repeat (graphify-state state))
(repeat (name action))
(map graphify-state states')))
m))
step-data)
"simulation-graph"))
(defn -main [& _]
(println "Generating graph image...")
(generate-image!)
(println "Running simulation...")
(run)
(while true nil))We can run our simulation using the clj cli like so:
clj -m simulation.core
Doing so will give us the following output:
Generating graph image...
Running simulation...
[:empty :initialize :idle]
[:idle :arrive :idle]
[:idle :load :busy]
[:busy :unload :idle]
[:idle :load :busy]
[:busy :unload :idle]
[:idle :load :busy]
[:busy :arrive :busy]
[:busy :unload :idle]
[:idle :load :busy]
[:busy :unload :idle]
[:idle :load :busy.buffer-empty]
[:busy.buffer-empty :arrive :busy]
[:busy :unload :idle]
[:idle :load :busy.buffer-empty]
[:busy.buffer-empty :unload :idle.buffer-empty]
[:idle.buffer-empty :arrive :idle]
[:idle :load :busy.buffer-empty]
[:busy.buffer-empty :unload :idle.buffer-empty]
[:idle.buffer-empty :arrive :idle]
[:idle :load :busy.buffer-empty]
[:busy.buffer-empty :unload :idle.buffer-empty]
[:idle.buffer-empty :arrive :idle]
[:idle :load :busy.buffer-empty]
[:busy.buffer-empty :unload :idle.buffer-empty]
[:idle.buffer-empty :arrive :idle]
[:idle :load :busy.buffer-empty]
[:busy.buffer-empty :unload :idle.buffer-empty]
[:idle.buffer-empty :arrive :idle]
[:idle :load :busy.buffer-empty]
[:busy.buffer-empty :unload :idle.buffer-empty]
...