Matias Guijarro
Web browsers as clients
Raise of webapps since 2005
Remote Access-enabled by design
No graphical application to install
Enforced separation between front-end and back-end code
Multi-platform at no cost
Long term support
web server + application framework
Python WSGI: Web Server Gateway Interface
Dozens of application framework exist
bottle micro-framework
1 from bottle import route, run, template
2
3 @route('/hello/:name')
4 def index(name='World'):
5 return template('<b>Hello {{name}}</b>!', name=name)
6
7 run(host='localhost', port=8080)
Framework 2 Hardware Repository is a library
1 from HardwareRepository import HardwareRepository
2
3 hwr = HardwareRepository.HardwareRepository('localhost:hwr')
4 hwr.connect()
5
6 minidiff = hwr.getHardwareObject("/minidiff")
7
8 print minidiff.phiMotor.getPosition()
Example: setting minidiff front light level
1 @bottle.get("/set_light_level")
2 def set_light_level():
3 light_level = float(bottle.request.GET["light_level"])
4
5 light_level_changed_event = gevent.event.Event()
6 def set_level_changed(*args):
7 light_level_changed_event.set()
8
9 minidiff.lightMotor.connect("moveDone", set_level_changed)
10 minidiff.lightMotor.move(light_level)
11 light_level_changed_event.wait()
12 minidiff.lightMotor.disconnect("moveDone", set_level_changed)
13
14 return minidiff.lightMotor.getPosition()
In the case of mxCuBE, the sample video display is the more demanding; let's use a websocket to convey frames to the client
1 new_frame = gevent.event.AsyncResult()
2
3 def new_frame_received(img, width, height, *args):
4 raw_data = img.bits().asstring(img.numBytes())
5 data = numpy.asarray(Image.fromstring("RGBX", (width, height), raw_data).convert("RGB"))
6 image = Image.fromarray(numpy.roll(data, -1, axis=-1)) #roll converts BGR to RGB
7 jpeg_buffer = cStringIO.StringIO()
8 image.save(jpeg_buffer, "JPEG", quality=95)
9 new_frame.set(base64.b64encode(jpeg_buffer.getvalue()))
10
11 minidiff.camera.connect("imageReceived", new_frame_received)
12
13 @bottle.get('/sample_video_stream', apply=[websocket])
14 def stream_video(ws):
15 while True:
16 if ws.receive():
17 jpeg_data = new_frame.get()
18 #print "sending",len(jpeg_data), "bytes"
19 ws.send(jpeg_data)
20 else:
21 break
Nice Model-View-Controller pattern
AJAX requests
1 function set_light_level() {
2 $.ajax({
3 error: function(XMLHttpRequest, textStatus, errorThrown) { alert(textStatus) },
4 url: 'set_light_level',
5 type: 'GET',
6 data: { "light_level": $("#light").val() },
7 success: function(res) {
8 update_gui(res);
9 },
10 dataType: "json" });
11 }
Websocket
1 function get_video_frame() {
2 if (video_socket==null) {
3 var ws_address;
4 if (window.location.port != "") {
5 ws_address = "ws://"+window.location.hostname+":"+window.location.port+"/sample_video_stream";
6 } else {
7 ws_address = "ws://"+window.location.hostname+"/sample_video_stream";
8 }
9 try {
10 video_socket = new WebSocket(ws_address);
11 } catch(e) {
12 video_socket = new MozWebSocket(ws_address);
13 }
14 video_socket.onopen = function() {
15 video_socket.send('!');
16 };
17 video_socket.onmessage = function(packed_msg) {
18 jpeg_frame.src = "data:image/jpeg;base64,"+packed_msg.data;
19 setTimeout(get_video_frame, 35);
20 };
21 } else {
22 video_socket.send('!');
23 }
24 }
Websockets + Reverse Proxy issue
Use a dedicated software for Reverse Proxy
Or use another web server
Hardware Objects can be 100% reused
To be continued ?
Table of Contents | t |
---|---|
Exposé | ESC |
Full screen slides | e |
Presenter View | p |
Source Files | s |
Slide Numbers | n |
Toggle screen blanking | b |
Show/hide slide context | c |
Notes | 2 |
Help | h |