From 05c7bb329182a638608ca6e9c36273cc7e71c71c Mon Sep 17 00:00:00 2001 From: luxick Date: Thu, 4 Jun 2020 21:13:21 +0200 Subject: [PATCH] Add JS client --- randopix.nimble | 16 +++++-- src/common.nim | 6 +-- src/pixctrl.nim | 101 ++++++++++++++++++++++++--------------- src/resources/index.html | 17 +++++++ src/resources/script.js | 29 +++++++++++ src/resources/site.css | 56 +++++++++++++++++++++- src/server.nim | 26 +++++++--- 7 files changed, 198 insertions(+), 53 deletions(-) diff --git a/randopix.nimble b/randopix.nimble index 4c68054..f186620 100644 --- a/randopix.nimble +++ b/randopix.nimble @@ -10,13 +10,21 @@ binDir = "bin" bin = @["randopix", "pixctrl"] # Dependencies -requires "nim >= 1.0.0", "gintro", "argparse", "jester" +requires "nim >= 1.0.0", "gintro", "argparse", "jester", "ajax" + +proc genJS = + echo "Generating JS Client" + exec("nim js -o:src/resources/pixctrl.js src/pixctrl.nim") task genJS, "Generate the Javascript client": - exec "nim js -o:src/resources/script.js src/pixscript.nim" + genJS() + +task buildAll, "Generate JS and run build": + genJS() + exec "nimble build" task debug, "Compile debug version": exec "nim c -d:debug --debugger:native -o:bin/randopix src/randopix.nim" -task release, "Compile release version": - exec fmt"nim c -d:release -o:bin/randopix-{version} src/randopix.nim" +before install: + genJS() \ No newline at end of file diff --git a/src/common.nim b/src/common.nim index 93f1c52..031ca06 100644 --- a/src/common.nim +++ b/src/common.nim @@ -21,6 +21,9 @@ type command*: Command ## Command that the application should execute parameter*: string ## Optional parameter for the command +proc `$`(cMsg: CommandMessage): string = + $(%cMsg) + proc newOpResult*(): OpResult = OpResult(success: true) @@ -30,9 +33,6 @@ proc newOpResult*(msg: string): OpResult = proc newCommandMessage*(c: Command, p: string = ""): CommandMessage = CommandMessage(command: c, parameter: p) -proc wrap*(msg: CommandMessage): string = - $(%msg) & "\r\L" - proc enumToStrings*(en: typedesc): seq[string] = for x in en: result.add $x \ No newline at end of file diff --git a/src/pixctrl.nim b/src/pixctrl.nim index ce65321..35d51f8 100644 --- a/src/pixctrl.nim +++ b/src/pixctrl.nim @@ -1,17 +1,32 @@ -import strutils, httpClient -import argparse +import strutils import common -const modeHelp = "Change the display mode. Possible values: [$1]" % Mode.enumToStrings().join(", ") - -var - randopixServer*: string ## URL for the randopix server - client = newHttpClient() +when defined(js): + import ajax, jsconsole, dom, json +else: + import httpClient + import argparse + var randopixServer* {.exportc.}: string ## URL for the randopix server proc sendCommand(msg: CommandMessage) = - let resp = client.post(randopixServer, msg.wrap) - if not resp.status.contains("200"): - echo "Error while sending command: ", resp.status + when defined(js): + console.log("Sending:", $msg, "to URL:", document.URL) + var req = newXMLHttpRequest() + + proc processSend(e:Event) = + if req.readyState == rsDONE: + if req.status != 200: + console.log("There was a problem with the request.") + console.log($req.status, req.statusText) + + req.onreadystatechange = processSend + req.open("POST", document.URL) + req.send(cstring($(%*msg))) + else: + let client = newHttpClient() + let resp = client.post(randopixServer, $msg) + if not resp.status.contains("200"): + echo "Error while sending command: ", resp.status proc sendCommand(cmd: Command) = sendCommand(newCommandMessage(cmd)) @@ -26,7 +41,7 @@ proc switchMode*(mode: string) = return sendCommand(newCommandMessage(cMode, mode)) -proc refresh*() = +proc refresh*() {.exportc.} = ## Force refresh of the current image sendCommand(cRefresh) @@ -34,33 +49,43 @@ proc setTimeout*(seconds: string) = ## Set the image timeout to this value sendCommand(newCommandMessage(cTimeout, seconds)) -when isMainModule: - var p = newParser("pixctrl"): - help("Control utilitiy for randopix") - option("-s", "--server", help="Host running the randopix server", default="http://localhost:8080/") - run: - randopixServer = opts.server - - command($cRefresh): - ## Force refresh command - help("Force image refresh now") +when defined(js): + proc getModes(): seq[cstring] {.exportc.} = + for mode in enumToStrings(Mode): + result.add cstring(mode) + proc switchMode*(mode: cstring) {.exportc.} = + switchMode($mode) + proc setTimeout*(seconds: cstring) {.exportc.} = + setTimeout($seconds) +else: + when isMainModule: + const modeHelp = "Change the display mode. Possible values: [$1]" % Mode.enumToStrings().join(", ") + var p = newParser("pixctrl"): + help("Control utilitiy for randopix") + option("-s", "--server", help="Host running the randopix server", default="http://localhost:8080/") run: - refresh() + randopixServer = opts.server - command($cTimeout): - ## Timeout Command - help("Set timeout in seconds before a new image is displayed") - arg("seconds", default = "300") - run: - setTimeout(opts.seconds) + command($cRefresh): + ## Force refresh command + help("Force image refresh now") + run: + refresh() - command($cMode): - ## Mode switch command - help(modeHelp) - arg("mode") - run: - switchMode(opts.mode) - try: - p.run(commandLineParams()) - except: - echo getCurrentExceptionMsg() \ No newline at end of file + command($cTimeout): + ## Timeout Command + help("Set timeout in seconds before a new image is displayed") + arg("seconds", default = "300") + run: + setTimeout(opts.seconds) + + command($cMode): + ## Mode switch command + help(modeHelp) + arg("mode") + run: + switchMode(opts.mode) + try: + p.run(commandLineParams()) + except: + echo getCurrentExceptionMsg() \ No newline at end of file diff --git a/src/resources/index.html b/src/resources/index.html index 6e70aff..0b690e2 100644 --- a/src/resources/index.html +++ b/src/resources/index.html @@ -9,10 +9,27 @@ +

randopix web control

+ +

+ +

+ +
Switch the image mode
+

+ + +

+ +
Adjust timespan between images
+

+ + +

\ No newline at end of file diff --git a/src/resources/script.js b/src/resources/script.js index e69de29..60d1657 100644 --- a/src/resources/script.js +++ b/src/resources/script.js @@ -0,0 +1,29 @@ +function capitalize(string) { + return string.charAt(0).toUpperCase() + string.slice(1); + } + +window.onload = () => { + const modes = getModes(); + const modeselect = document.getElementById("modeselect"); + modes.forEach(mode => { + let opt = document.createElement('option'); + opt.value = mode; + opt.innerHTML = capitalize(mode) + modeselect.appendChild(opt); + }); +} + +function js_setTimeout() { + const elem = document.getElementById("timeout"); + const timeout = parseInt(elem.value); + if (timeout < 1) { + console.error("timeout must be positive"); + return; + } + setTimeout(timeout.toString()); +} + +function js_setMode() { + const modeselect = document.getElementById("modeselect"); + switchMode(modeselect.value) +} \ No newline at end of file diff --git a/src/resources/site.css b/src/resources/site.css index aabdee4..fdf46cb 100644 --- a/src/resources/site.css +++ b/src/resources/site.css @@ -1,3 +1,57 @@ body { background-color: blue; -} \ No newline at end of file + color: yellow; +} +/* Chrome, Safari, Opera */ +@-webkit-keyframes rainbow { + 0%{color: orange;} + 10%{color: purple;} + 20%{color: red;} + 30%{color: CadetBlue;} + 40%{color: yellow;} + 50%{color: coral;} + 60%{color: green;} + 70%{color: cyan;} + 80%{color: DeepPink;} + 90%{color: DodgerBlue;} + 100%{color: orange;} +} + +/* Internet Explorer */ +@-ms-keyframes rainbow { + 0%{color: orange;} + 10%{color: purple;} + 20%{color: red;} + 30%{color: CadetBlue;} + 40%{color: yellow;} + 50%{color: coral;} + 60%{color: green;} + 70%{color: cyan;} + 80%{color: DeepPink;} + 90%{color: DodgerBlue;} + 100%{color: orange;} +} + +/* Standard Syntax */ +@keyframes rainbow { + 0%{color: orange;} + 10%{color: purple;} + 20%{color: red;} + 30%{color: CadetBlue;} + 40%{color: yellow;} + 50%{color: coral;} + 60%{color: green;} + 70%{color: cyan;} + 80%{color: DeepPink;} + 90%{color: DodgerBlue;} + 100%{color: orange;} +} + +h1 { + /* Chrome, Safari, Opera */ + -webkit-animation: rainbow 5s infinite; + /* Internet Explorer */ + -ms-animation: rainbow 5s infinite; + /* Standar Syntax */ + animation: rainbow 5s infinite; +} diff --git a/src/server.nim b/src/server.nim index dbecbaa..7e24b0d 100644 --- a/src/server.nim +++ b/src/server.nim @@ -1,10 +1,11 @@ -import asyncdispatch, strutils, json +import asyncdispatch, strutils, json, logging import jester import common const index = slurp("resources/index.html") style = slurp("resources/site.css") + pixctrlJs = slurp("resources/pixctrl.js") script = slurp("resources/script.js") type @@ -22,24 +23,35 @@ proc log(things: varargs[string, `$`]) = router randopixRouter: get "/": - log "Access from ", request.ip resp index get "/style": resp(style, contentType="text/css") + get "/pixctrl": + resp(pixctrlJs, contentType="text/javascript") + get "/script": resp(script, contentType="text/javascript") post "/": - let json = request.body.parseJson - let msg = json.to(CommandMessage) - # Pass command from client to main applicaiton - chan.send(msg) - resp Http200 + try: + log "Command from ", request.ip + let json = request.body.parseJson + let msg = json.to(CommandMessage) + log "Got message: ", $msg + # Pass command from client to main applicaiton + chan.send(msg) + resp Http200 + except: + log "Error: ", getCurrentExceptionMsg() proc runServer*[ServerArgs](arg: ServerArgs) {.thread, nimcall.} = verbose = arg.verbose + if verbose: + logging.setLogFilter(lvlInfo) + else: + logging.setLogFilter(lvlNotice) let port = Port(arg.port) let settings = newSettings(port=port) var server = initJester(randopixRouter, settings=settings)