Packages

An Erlang/LFE gnuplot process server

Current section

Files

Jump to
plottah src plottah-svr.lfe
Raw

src/plottah-svr.lfe

(defmodule plottah-svr
(behaviour gen_server)
;; gen_server implementation
(export
(start_link 0)
(stop 0))
;; callback implementation
(export
(init 1)
(handle_call 3)
(handle_cast 2)
(handle_info 2)
(terminate 2)
(code_change 3)))
(include-lib "logjam/include/logjam.hrl")
;;; ----------------
;;; config functions
;;; ----------------
(defun SERVER () (MODULE))
(defun initial-state () #m())
(defun genserver-opts () '())
;;; -------------------------
;;; gen_server implementation
;;; -------------------------
(defun start_link ()
(gen_server:start_link `#(local ,(SERVER))
(MODULE)
(initial-state)
(genserver-opts)))
(defun stop ()
(gen_server:call (SERVER) 'stop))
;;; -----------------------
;;; callback implementation
;;; -----------------------
(defun init (state)
(log-info "Initialising gnuplot server ...")
(erlang:process_flag 'trap_exit 'true)
(let ((`#(ok ,pid ,os-pid) (exec:run_link "gnuplot" (erlexec-opts (self)))))
`#(ok ,(maps:merge state `#m(pid ,pid os-pid ,os-pid)))))
(defun handle_cast
((msg state)
(unknown-command msg)
`#(noreply ,state)))
(defun handle_call
(('stop _from state)
`#(stop shutdown ok ,state))
((`#(cmd echo ,msg) _from state)
`#(reply ,msg ,state))
((`#(cmd ping) _from state)
`#(reply pong ,state))
((`#(cmd state) _from state)
`#(reply ,state ,state))
((`#(cmd gplot ,cmd) from state)
(handle_call `#(cmd gplot ,cmd delay 0) from state))
((`#(cmd gplot ,cmd delay ,ms-delay) _from (= `#m(os-pid ,os-pid) state))
(log-debug "Sending command: ~s" (list cmd))
(let ((output (exec:send os-pid (list_to_binary (++ cmd "\n")))))
;; (log-debug (string:substr output 0 1000))
;; for some operations, erlexec needs to give gnuplot a little time to catch up:
(timer:sleep ms-delay))
`#(reply ok ,state))
((msg _from state)
`#(reply ,(unknown-command msg) ,state)))
(defun handle_info
;; Output from gnuplot
((`#(stderr ,_pid ,msg) state)
(io:format "~s~n" (list (string:substr (binary_to_list msg) 1 1000)))
`#(noreply ,state))
((`#(EXIT ,_from normal) state)
`#(noreply ,state))
((`#(EXIT ,pid ,reason) state)
(log-warn "Process ~p exited! (Reason: ~p)~n" `(,pid ,reason))
`#(noreply ,state))
((msg state)
(unhandled-info msg)
`#(noreply ,state)))
(defun terminate (_reason _state)
'ok)
(defun code_change (_old-version state _extra)
`#(ok ,state))
;;; -----------------------
;;; private functions
;;; -----------------------
(defun erlexec-opts (mgr-pid)
`(stdin
pty
#(stdout ,mgr-pid)
#(stderr ,mgr-pid)
monitor))
(defun unknown-command (data)
(let ((msg (io_lib:format "Unknown command: ~p" `(,data))))
(log-error msg)
#(error mag)))
(defun unhandled-info (data)
(let ((msg (io_lib:format "Unhandled info: ~p" `(,data))))
(log-warn msg)
#(error mag)))