diff --git a/rebar.config b/rebar.config index 1127c9d..6119b35 100644 --- a/rebar.config +++ b/rebar.config @@ -1,7 +1,8 @@ {erl_opts, [debug_info, {no_auto_import, [{get,2}]}]}. {deps, [ - {lfe, "2.1.2"} + {lfe, "2.1.3"}, + {logjam, "1.2.3"} ]}. diff --git a/src/equipoise.lfe b/src/equipoise.lfe index 12c44e8..0c5f6a6 100644 --- a/src/equipoise.lfe +++ b/src/equipoise.lfe @@ -4,15 +4,5 @@ (defmodule equipoise (export all)) -(defun generate-route - ((output-dir `(,path ,data)) - (let ((filename (filename:join output-dir path))) - (filelib:ensure_dir filename) - (case (file:write_file filename data) - ('ok (io:format "Created ~s.~n" `(,filename))) - (err err))))) - -(defun generate - ((routes `#m(output-dir ,o)) - (lists:map (lambda (x) (generate-route o x)) routes) - 'ok)) +(defun placeholder () + 'tbd) \ No newline at end of file diff --git a/src/poise-app.lfe b/src/poise-app.lfe new file mode 100644 index 0000000..ab3a273 --- /dev/null +++ b/src/poise-app.lfe @@ -0,0 +1,22 @@ +(defmodule poise-app + (behaviour application) + ;; app implementation + (export + (start 2) + (stop 1))) + +(include-lib "logjam/include/logjam.hrl") + +;;; -------------------------- +;;; application implementation +;;; -------------------------- + +(defun start (_type _args) + (logger:set_application_level 'poise 'all) + (logjam:set-dev-config) + (log-info "Starting poise application ...") + (plottah-sup:start_link)) + +(defun stop (_state) + (plottah-sup:stop) + 'ok) diff --git a/src/poise-sup.lfe b/src/poise-sup.lfe new file mode 100644 index 0000000..3ec8f03 --- /dev/null +++ b/src/poise-sup.lfe @@ -0,0 +1,52 @@ +(defmodule poise-sup + (behaviour supervisor) + ;; supervisor implementation + (export + (start_link 0) + (stop 0)) + ;; callback implementation + (export + (init 1))) + +;;; ---------------- +;;; config functions +;;; ---------------- + +(defun SERVER () (MODULE)) +(defun supervisor-opts () '()) +(defun child-opts () '()) +(defun sup-flags () + `#M(strategy one_for_one + intensity 3 + period 60)) + +;;; ------------------------- +;;; supervisor implementation +;;; ------------------------- + +(defun start_link () + (supervisor:start_link `#(local ,(SERVER)) + (MODULE) + (supervisor-opts))) + +(defun stop () + (gen_server:call (SERVER) 'stop)) + +;;; ----------------------- +;;; callback implementation +;;; ----------------------- + +(defun init (_args) + `#(ok #(,(sup-flags) (,(child 'poise-svr 'start_link (child-opts)))))) + +;;; ----------------- +;;; private functions +;;; ----------------- + +(defun child (mod fun args) + `#M(id ,mod + start #(,mod ,fun ,args) + restart permanent + shutdown 2000 + type worker + modules (,mod))) diff --git a/src/poise-svr.lfe b/src/poise-svr.lfe new file mode 100644 index 0000000..8f271b5 --- /dev/null +++ b/src/poise-svr.lfe @@ -0,0 +1,118 @@ +(defmodule poise-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 poise server ...") + (erlang:process_flag 'trap_exit 'true) + (let ((`#(ok ,pid ,os-pid) (exec:run_link "mdsploder" (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 mdsplode ,cmd) from state) + (handle_call `#(cmd gplot ,cmd delay 0) from state)) + ((`#(cmd mdsplode ,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 mdsploder + ((`#(stdout ,_pid ,msg) state) + (io:format "~s~n" (list (string:substr (binary_to_list msg) 1 1000))) + `#(noreply ,state)) + ;; Output from mdsploder + ((`#(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))) diff --git a/src/poise.app.src b/src/poise.app.src index db52a86..5805f74 100644 --- a/src/poise.app.src +++ b/src/poise.app.src @@ -5,9 +5,11 @@ [poise, equipoise]}, {registered, []}, - {applications,[kernel,stdlib]}, + {mod, {'poise-app', []}}, + {applications,[kernel,stdlib,erlexec]}, {included_applications, []}, {env, []}, + {modules, []}, %% Package metadata: {pkg_name, poise}, diff --git a/src/poise.lfe b/src/poise.lfe index 1273c10..6b59454 100644 --- a/src/poise.lfe +++ b/src/poise.lfe @@ -1,64 +1,51 @@ (defmodule poise - (export all)) + ;; Convenience wrappers + (export + (start 0) + (stop 0)) + ;; Plottah API + ;;(export + ;; ) + ;; Debug + (export + (echo 1) + (pid 0) + (ping 0) + (raw 1) + (state 0))) (include-lib "poise/include/poise.lfe") +(include-lib "logjam/include/logjam.hrl") -(defun site (route-list opts-map) - (make-site - routes (lists:map (match-lambda ((`(,p ,f)) - (make-route path p func f))) - route-list) - opts (make-options output-dir - (maps:get 'output-dir opts-map)))) +;;; Constants -(defun generate-route - ((output-dir (match-route path path func func)) - (let ((filename (filename:join output-dir path))) - (filelib:ensure_dir filename) - (case (file:write_file filename (list_to_binary (funcall func))) - ('ok (io:format "Created ~s.~n" `(,filename))))))) +(defun APP () 'plottah) +(defun SERVER () 'plottah-svr) +(defun default-ms-delay () 100) +(defun default-ms-long-delay () 1000) -(defun generate - (((= (match-site - routes routes - opts (= (match-options - output-dir output-dir) opts)) data)) - (lists:map (lambda (x) (generate-route output-dir x)) routes) - 'ok)) +;;; Convenience wrappers -;; XXX See if we can get this one working: -; (defmodule poise -; (export all)) +(defun start () (application:ensure_all_started (APP))) +(defun stop () (application:stop (APP))) -; (include-lib "poise/include/poise.lfe") +;;; Poise API -; (defun site (route-list opts-map) -; (make-site -; routes (lists:map (match-lambda -; ((`(,p ,f)) (when (is_function f)) -; (make-route path p func f)) -; ((`(,p ,d)) -; (make-route path p data d))) -; route-list) -; opts (make-options output-dir -; (maps:get 'output-dir opts-map)))) +;; TBD -; (defun generate-route -; ((output-dir (match-route path path func func)) (when (is_function func)) -; (generate-route -; output-dir -; (make-route path path data (funcall func)))) -; ((output-dir (match-route path path data data)) -; (let ((filename (filename:join output-dir path))) -; (filelib:ensure_dir filename) -; (case (file:write_file filename (list_to_binary data)) -; ('ok (io:format "Created ~s.~n" `(,filename))) -; (err err))))) +;;; Debug -; (defun generate -; (((= (match-site -; routes routes -; opts (= (match-options -; output-dir output-dir) opts)) data)) -; (lists:map (lambda (x) (generate-route output-dir x)) routes) -; 'ok)) +(defun echo (msg) + (gen_server:call (SERVER) `#(cmd echo ,msg))) + +(defun pid () + (erlang:whereis (SERVER))) + +(defun ping () + (gen_server:call (SERVER) `#(cmd ping))) + +(defun state () + (gen_server:call (SERVER) `#(cmd state))) + +(defun raw (raw-cmd) + (gen_server:call (SERVER) `#(cmd gplot ,raw-cmd)))