Add JS client

This commit is contained in:
2020-06-04 21:13:21 +02:00
parent 13cf05bf99
commit 05c7bb3291
7 changed files with 198 additions and 53 deletions

View File

@@ -10,13 +10,21 @@ binDir = "bin"
bin = @["randopix", "pixctrl"] bin = @["randopix", "pixctrl"]
# Dependencies # 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": 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": task debug, "Compile debug version":
exec "nim c -d:debug --debugger:native -o:bin/randopix src/randopix.nim" exec "nim c -d:debug --debugger:native -o:bin/randopix src/randopix.nim"
task release, "Compile release version": before install:
exec fmt"nim c -d:release -o:bin/randopix-{version} src/randopix.nim" genJS()

View File

@@ -21,6 +21,9 @@ type
command*: Command ## Command that the application should execute command*: Command ## Command that the application should execute
parameter*: string ## Optional parameter for the command parameter*: string ## Optional parameter for the command
proc `$`(cMsg: CommandMessage): string =
$(%cMsg)
proc newOpResult*(): OpResult = proc newOpResult*(): OpResult =
OpResult(success: true) OpResult(success: true)
@@ -30,9 +33,6 @@ proc newOpResult*(msg: string): OpResult =
proc newCommandMessage*(c: Command, p: string = ""): CommandMessage = proc newCommandMessage*(c: Command, p: string = ""): CommandMessage =
CommandMessage(command: c, parameter: p) CommandMessage(command: c, parameter: p)
proc wrap*(msg: CommandMessage): string =
$(%msg) & "\r\L"
proc enumToStrings*(en: typedesc): seq[string] = proc enumToStrings*(en: typedesc): seq[string] =
for x in en: for x in en:
result.add $x result.add $x

View File

@@ -1,17 +1,32 @@
import strutils, httpClient import strutils
import argparse
import common import common
const modeHelp = "Change the display mode. Possible values: [$1]" % Mode.enumToStrings().join(", ") when defined(js):
import ajax, jsconsole, dom, json
var else:
randopixServer*: string ## URL for the randopix server import httpClient
client = newHttpClient() import argparse
var randopixServer* {.exportc.}: string ## URL for the randopix server
proc sendCommand(msg: CommandMessage) = proc sendCommand(msg: CommandMessage) =
let resp = client.post(randopixServer, msg.wrap) when defined(js):
if not resp.status.contains("200"): console.log("Sending:", $msg, "to URL:", document.URL)
echo "Error while sending command: ", resp.status 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) = proc sendCommand(cmd: Command) =
sendCommand(newCommandMessage(cmd)) sendCommand(newCommandMessage(cmd))
@@ -26,7 +41,7 @@ proc switchMode*(mode: string) =
return return
sendCommand(newCommandMessage(cMode, mode)) sendCommand(newCommandMessage(cMode, mode))
proc refresh*() = proc refresh*() {.exportc.} =
## Force refresh of the current image ## Force refresh of the current image
sendCommand(cRefresh) sendCommand(cRefresh)
@@ -34,33 +49,43 @@ proc setTimeout*(seconds: string) =
## Set the image timeout to this value ## Set the image timeout to this value
sendCommand(newCommandMessage(cTimeout, seconds)) sendCommand(newCommandMessage(cTimeout, seconds))
when isMainModule: when defined(js):
var p = newParser("pixctrl"): proc getModes(): seq[cstring] {.exportc.} =
help("Control utilitiy for randopix") for mode in enumToStrings(Mode):
option("-s", "--server", help="Host running the randopix server", default="http://localhost:8080/") result.add cstring(mode)
run: proc switchMode*(mode: cstring) {.exportc.} =
randopixServer = opts.server switchMode($mode)
proc setTimeout*(seconds: cstring) {.exportc.} =
command($cRefresh): setTimeout($seconds)
## Force refresh command else:
help("Force image refresh now") 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: run:
refresh() randopixServer = opts.server
command($cTimeout): command($cRefresh):
## Timeout Command ## Force refresh command
help("Set timeout in seconds before a new image is displayed") help("Force image refresh now")
arg("seconds", default = "300") run:
run: refresh()
setTimeout(opts.seconds)
command($cMode): command($cTimeout):
## Mode switch command ## Timeout Command
help(modeHelp) help("Set timeout in seconds before a new image is displayed")
arg("mode") arg("seconds", default = "300")
run: run:
switchMode(opts.mode) setTimeout(opts.seconds)
try:
p.run(commandLineParams()) command($cMode):
except: ## Mode switch command
echo getCurrentExceptionMsg() help(modeHelp)
arg("mode")
run:
switchMode(opts.mode)
try:
p.run(commandLineParams())
except:
echo getCurrentExceptionMsg()

View File

@@ -9,10 +9,27 @@
<link rel="stylesheet" type="text/css" href="/style"> <link rel="stylesheet" type="text/css" href="/style">
<script src="/pixctrl" type="text/javascript"></script>
<script src="/script" type="text/javascript"></script> <script src="/script" type="text/javascript"></script>
</head> </head>
<body> <body>
<h1>randopix web control</h1> <h1>randopix web control</h1>
<p>
<button type="button" class="btn" onclick="refresh()">Refresh Image Now!</button>
</p>
<div>Switch the image mode</div>
<p>
<select id="modeselect"></select>
<button type="button" class="btn" onclick="js_setMode()">Set Mode</button>
</p>
<div>Adjust timespan between images</div>
<p>
<input id="timeout" type="number">
<button type="button" class="btn" onclick="js_setTimeout()">Set Timeout</button>
</p>
</body> </body>
</html> </html>

View File

@@ -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)
}

View File

@@ -1,3 +1,57 @@
body { body {
background-color: blue; background-color: blue;
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;
} }

View File

@@ -1,10 +1,11 @@
import asyncdispatch, strutils, json import asyncdispatch, strutils, json, logging
import jester import jester
import common import common
const const
index = slurp("resources/index.html") index = slurp("resources/index.html")
style = slurp("resources/site.css") style = slurp("resources/site.css")
pixctrlJs = slurp("resources/pixctrl.js")
script = slurp("resources/script.js") script = slurp("resources/script.js")
type type
@@ -22,24 +23,35 @@ proc log(things: varargs[string, `$`]) =
router randopixRouter: router randopixRouter:
get "/": get "/":
log "Access from ", request.ip
resp index resp index
get "/style": get "/style":
resp(style, contentType="text/css") resp(style, contentType="text/css")
get "/pixctrl":
resp(pixctrlJs, contentType="text/javascript")
get "/script": get "/script":
resp(script, contentType="text/javascript") resp(script, contentType="text/javascript")
post "/": post "/":
let json = request.body.parseJson try:
let msg = json.to(CommandMessage) log "Command from ", request.ip
# Pass command from client to main applicaiton let json = request.body.parseJson
chan.send(msg) let msg = json.to(CommandMessage)
resp Http200 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.} = proc runServer*[ServerArgs](arg: ServerArgs) {.thread, nimcall.} =
verbose = arg.verbose verbose = arg.verbose
if verbose:
logging.setLogFilter(lvlInfo)
else:
logging.setLogFilter(lvlNotice)
let port = Port(arg.port) let port = Port(arg.port)
let settings = newSettings(port=port) let settings = newSettings(port=port)
var server = initJester(randopixRouter, settings=settings) var server = initJester(randopixRouter, settings=settings)