-
Joannis Koepsell authoredJoannis Koepsell authored
server.py 5.37 KiB
import argparse
import os, sys
import tornado.ioloop
import tornado.web
import tornado.httpserver
import tornado.websocket
from tornado.ioloop import PeriodicCallback
import json
from wlm import WavelengthMeter
# all connected browsers will be here
clients = []
def send_data():
"""Gets wavelengths from the wavemeter and sends it to the client"""
if len(clients)>0:
data = wlmeter.wavelengths
str = json.dumps(data)
for c in clients:
c.write_message(str)
class WsHandler(tornado.websocket.WebSocketHandler):
"""Websocket handler"""
def open(self):
"""Subscribes to the updates by adding itself to the clients list"""
clients.append(self)
def on_close(self):
"""Removes itself from clients list"""
clients.remove(self)
print('connection closed')
def check_origin(self, origin):
"""Allows cross origin connection if you want to embed wlm.js library in some page on another domain"""
return True
class ApiHandler(tornado.web.RequestHandler):
"""Creates simple HTTP API if you don't like websockets"""
def get(self, channel=None):
w = wlmeter.wavelengths
sw = wlmeter.switcher_mode
if channel is None:
self.write({ "wavelengths": w, "switcher_mode": sw })
else:
ch = int(channel)
if ch >=0 and ch<len(w):
self.write("%.8f" % w[ch])
else:
self.set_status(400)
self.write({"error":"Wrong channel"})
class IndexHandler(tornado.web.RequestHandler):
"""Renders index.html page"""
def get(self):
self.render("index.html",
wavelengths=wlmeter.wavelengths,
**get_config()
)
default_config_file = os.path.abspath(os.path.join(os.path.dirname(__file__), "config.json"))
def make_app(config):
"""All the routes are defined here"""
static_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "static"))
return tornado.web.Application([
(r"%s/" % config["root"], IndexHandler),
(r"%s/api/" % config["root"], ApiHandler),
(r"%s/api/(\d)/" % config["root"], ApiHandler),
(r"%s/ws/" % config["root"], WsHandler),
(r"%s/static/(.*)" % config["root"], tornado.web.StaticFileHandler, {'path': static_path}),
], debug=True)
class config_action(argparse.Action):
"""Parses config file argument"""
def __call__(self, parser, namespace, values, option_string=None):
config_file = values
if not os.path.isfile(config_file):
raise argparse.ArgumentTypeError("config:{0} is not a valid file".format(config_file))
if os.access(config_file, os.R_OK):
setattr(namespace, self.dest, config_file)
else:
raise argparse.ArgumentTypeError("config:{0} is not a readable file".format(config_file))
def get_config():
"""Building configuration dictionary"""
# command line arguments parsing
parser = argparse.ArgumentParser(description='Starts a webserver with wavemeter interface.')
parser.add_argument('--debug', dest='debug', action='store_const',
const=True,
help='runs the script in debug mode simulating wavelength values')
parser.add_argument('-c', '--config', action=config_action, default=default_config_file,
help='path to config json file, default: config.json in the script folder')
parser.add_argument('-r', '--root', default=None,
help='path where the interface will be, like localhost:8000/root/. Default is "/"')
parser.add_argument('port', type=int, nargs='?',
help='server port, default: 8000')
args = parser.parse_args()
# default configuration
config = {
"port": 8000, # port
"root": "/", # path
"precision": 5, # number of decimals in wavelength display
"update_rate": 0.1, # how often updates will be sent to the browsers
"debug": False, # do you want to work with real wavemeter or to test run it?
"channels": [{"i": i, "label": "Channel %d" % (i+1)} for i in range(8)] # channels to display
}
# configuration from the file
with open(args.config, "r") as f:
config.update(json.loads(f.read()))
# configuration from command line
config["port"] = (args.port or config["port"])
config["root"] = (args.root or config["root"])
config["debug"] = (args.debug or config["debug"])
# add leading slash
if len(config["root"]) > 0 and config["root"][0] != "/":
config["root"] = "/"+config["root"]
# remove trailing slash
if config["root"][-1] == "/":
config["root"] = config["root"][:-1]
return config
if __name__ == "__main__":
config = get_config()
wlmeter = WavelengthMeter(debug=config["debug"])
app = make_app(config)
if "ssl" in config:
# https and wss server
server = tornado.httpserver.HTTPServer(app, xheaders=True, ssl_options=config["ssl"])
else:
# http and ws server
server = tornado.httpserver.HTTPServer(app)
server.listen(config["port"])
print("Server started at http://localhost:%d%s/" % (config["port"], config["root"]))
# periodic callback takes update rate in ms
PeriodicCallback(send_data, config["update_rate"]*1000).start()
tornado.ioloop.IOLoop.instance().start()