Hi all,
I'm developing a web client to control some robots, the non-web version
works fine.
I use JSON-RPC to communicate with python service (running on Apache with
mod_python - built for windows & python 2.7, if any one needs it, email me).
My solution is to render each frame to a JPEG file and save it to a folder
on server, return the file path to client, and then the client loads it.
DOM seems to not reload an image from the same URL, so for each frame I
choose a filename from a set of names.
It works, but after several frames, the client displays a random old image
which doesn't exist on server anymore - I'm sure of it, when I stop the
robot, every image on server is the same, whereas the client stutters with
an old image once every several frames. I also tried using loadImages, no
changes
I attach the source code of the client. If you need the server side
components, I can upload them too.
Any helps will be deeply appreciated.
TIA,
hxp
import pyjd # dummy in pyjs
from pyjamas import DOM
from pyjamas.ui.RootPanel import RootPanel
from pyjamas.ui.Label import Label
from pyjamas.ui.VerticalPanel import VerticalPanel
from pyjamas.ui.Label import Label
from pyjamas.ui.FocusPanel import FocusPanel
from pyjamas.ui.SimplePanel import SimplePanel
from pyjamas.ui import Event
from pyjamas.ui import KeyboardListener
from pyjamas.ui import MouseListener
from pyjamas.ui.KeyboardListener import KeyboardHandler
from pyjamas.ui.MouseListener import MouseHandler
from pyjamas.ui.Image import Image
from pyjamas.Timer import Timer
from pyjamas import Window
from pyjamas.Canvas.GWTCanvas import GWTCanvas
from pyjamas.Canvas.ImageLoader import loadImages
from pyjamas.Canvas import Color
from pyjamas.JSONService import JSONProxy
from pyjamas import log
import pygwt
# this class handles RPC communication
class RoverService(JSONProxy):
def __init__(self):
JSONProxy.__init__(self, "services/RoverService.py", ["getgeneralinfo", "getframe", "getimagemain", "getimagethumb", "sendaction", "sendcommand"])
# client canvas, do the drawings
class GameCanvas(GWTCanvas, MouseHandler):
def __init__(self, width, height, game):
GWTCanvas.__init__(self, width, height)
MouseHandler.__init__(self)
self.old_w = 0
self.old_h = 0
self.width = width
self.height = height
self.game = game
self.addMouseListener(self)
self.frame = Image()
self.frame.setUrl("rover_big.jpg")
self.draw()
def set_size(self, w, h):
self.width = w
self.height = h
def load_frame(self, url):
if self.frame.getUrl() == url or url == "FAIL":
return
self.frame.setUrl(url)
self.draw()
def draw(self):
if self.old_h != self.height or self.old_w != self.width:
self.resize(self.width, self.height)
self.old_h = self.height
self.old_w = self.width
self.drawImage(self.frame, 0, 0)
def onMouseDown(self, sender, x, y):
for rover in self.game.rovers:
if rover["x1"] <= x <= rover["x2"] and rover["y1"] <= (y-480) <= rover["y2"]:
# hit
self.game.select(rover["index"])
break
# client window layout
class GameLayout(VerticalPanel, KeyboardHandler):
def __init__(self, game):
VerticalPanel.__init__(self)
KeyboardHandler.__init__(self)
self.game = game
self.addKeyboardListener(self)
def onKeyPress(self, sender, keyCode, modifiers = None):
return
def onKeyDown(self, sender, keyCode, modifiers = None):
self.game.keypress(keyCode, True)
def onKeyUp(self, sender, keyCode, modifiers = None):
self.game.keypress(keyCode, False)
# logic
FPS = 25
class Game:
def __init__(self):
self.rovers = []
self.main = -1
self.remote_service = RoverService()
self.run = False
self.main = -1
self.online = 0
self.all = False
self.infra = False
self.direction = 0
self.old_n = 0
# key flags
self.key_up = False
self.key_down = False
self.key_left = False
self.key_right = False
self.key_a = False
self.key_space = False
self.key_i = False
# status
self.status = False
def load(self):
self.game_panel = GameLayout(self)
self.label = Label("Status: Calling servers ...")
self.label2 = Label("Select All: No")
self.game_canvas = GameCanvas(640, 600, self)
self.game_panel.add(self.label)
self.game_panel.add(self.label2)
focus_panel = FocusPanel(Widget=self.game_canvas)
self.game_panel.add(focus_panel)
RootPanel().add(self.game_panel)
focus_panel.setFocus(True)
self.get_info("GENERAL")
# service call handlers
def onRemoteResponse(self, response, request_info):
self.remote_response(request_info.method, response)
def onRemoteError(self, code, errobj, request_info):
message = errobj['message']
if code != 0:
text = "HTTP error %d: %s from %s" % (code, message, request_info.method)
else:
code = errobj['code']
text = "JSONRPC Error %s: %s from %s" % (code, message, request_info.method)
self.remote_error(request_info.method, text)
# start
def start(self):
self.run = True
self.onTimer()
# calling services
def get_info(self, msg):
self.remote_service.getgeneralinfo(msg, self)
def get_image(self, msg):
if msg == "MAIN":
self.remote_service.getimagemain(msg, self)
elif msg == "THUMB":
self.remote_service.getimagethumb(msg, self)
elif msg == "FRAME":
self.remote_service.getframe(msg, self)
def send_action(self, msg):
self.remote_service.sendaction(msg, self)
def select(self, index):
if not self.run:
return
if index == self.main:
return
self.main = index
if self.rovers[index]["stat"] != "online":
return
msg = "SET_MAIN"
l = 15 - len(msg)
for i in range(l):
msg = msg + " "
msg = msg + str(index)
l = 50 - len(msg)
for i in range(l):
msg = msg + " "
self.remote_service.sendcommand(msg, self)
# handle responses from server
def remote_response(self, method_name, response):
if method_name == "getgeneralinfo":
self.process_info_general(response)
elif method_name == "getframe":
self.process_frame(response)
elif method_name == "sendaction":
pass
elif method_name == "sendcommand":
pass
def remote_error(self, method_name, message):
if not self.run:
Window.alert("Couldn't call services. Client will not start!!!")
else:
if method_name == "sendaction":
pass
elif method_name == "sendcommand":
pass
else:
Window.alert("Services call failed. Client stopped!!!")
self.run = False
# process the returned data
def update_info(self, data):
chunks = data.split(" ")
count = chunks[0].split("=")
n = int(count[1])
if n <= 0:
return False
tmp = chunks[1].split("=")
main_ip = tmp[1]
self.online = 0
self.rovers = []
for i in range(n):
tmp = chunks[i+2].split("=")
rover = {}
rover["index"] = i
rover["ip"] = tmp[1]
rover["stat"] = tmp[0]
if tmp[0] == "online":
self.online += 1
if tmp[1] == main_ip:
self.main = i
# set coordinates
rows = int(i / 4)
cols = i % 4
x1 = cols * 160
x2 = x1 + 159
y1 = rows * 120
y2 = y1 + 119
rover["x1"] = x1
rover["x2"] = x2
rover["y1"] = y1
rover["y2"] = y2
self.rovers.append(rover)
if self.online == 0:
return False
else:
text = "[Status]: [Online: " + str(self.online) + "] ; Main Rover [" + str(self.main)+ "] IP: " + self.rovers[self.main]["ip"]
self.label.setText(text)
if self.old_n != n:
cols = n % 4
if cols == 0:
rows = int(n / 4)
else:
rows = int(n / 4) + 1
self.game_canvas.set_size(640, rows * 120 + 480)
self.old_n = n
return True
def process_info_general(self, msg):
if not self.run:
# game not started. do smth here
if msg == "FAIL":
Window.alert("Couldn't retrieve rovers' information. Client will not start!!!")
else:
# update information
if self.update_info(msg):
self.start()
self.get_image("FRAME")
else:
Window.alert("Error. Client will not start.")
else:
# update rovers information
if msg != "FAIL":
self.update_info(msg)
self.get_image("FRAME")
def process_frame(self, msg):
if msg != "FAIL":
self.game_canvas.load_frame(msg)
self.status = False
# timer, to update screen & info
def onTimer(self, t=None):
Timer(int(1000/FPS), self)
if not self.run:
return
self.event_loop()
if not self.status:
self.update_display()
def switch_rover(self):
index = self.main + 1
while index != self.main:
if index >= len(self.rovers):
index = 0
if index == self.main:
break
elif self.rovers[index]["stat"] == "online":
break
else:
index += 1
return index
def event_loop(self):
# all or not
if self.key_a:
self.all = not self.all
if self.all:
self.label2.setText("[Select All] : Yes")
header = "ALL"
else:
self.label2.setText("[Select All] : No")
s = str(self.main)
if len(s) == 1:
header = "00" + s
elif len(s) == 2:
header = "0" + s
else:
header = s
# infra red
if self.key_i:
self.infra = not self.infra
if self.infra:
msg = header + "INFRA_ON"
else:
msg = header + "INFRA_OFF"
self.send_action(msg)
# switch rover
if self.key_space:
index = self.switch_rover()
if index != self.main:
self.select(index)
self.main = index
# movements
if self.key_up or self.key_down:
if self.key_up:
if self.direction >= 0:
if self.key_left:
msg = header + "FWD_LEFT"
elif self.key_right:
msg = header + "FWD_RIGHT"
else:
msg = header + "FORWARD"
self.direction = 1
else:
msg = header + "STOP_BACK"
self.direction = 0
if self.key_down:
if self.direction <= 0:
if self.key_left:
msg = header + "BWD_LEFT"
elif self.key_right:
msg = header + "BWD_RIGHT"
else:
msg = header + "BACKWARD"
self.direction = -1
else:
msg = header + "STOP_FRONT"
self.direction = 0
self.send_action(msg)
def update_display(self):
self.status = True
self.get_info("GENERAL")
def keypress(self, keycode, status):
if keycode == KeyboardListener.KEY_UP:
self.key_up = status
elif keycode == KeyboardListener.KEY_DOWN:
self.key_down = status
elif keycode == KeyboardListener.KEY_LEFT:
self.key_left = status
elif keycode == KeyboardListener.KEY_RIGHT:
self.key_right = status
elif keycode == 65: # A
self.key_a = status
elif keycode == 73: # I
self.key_i = status
elif keycode == 32: # SPACE
self.key_space = status
if __name__ == '__main__':
pyjd.setup("public/RoverWebClient.html")
game = Game()
game.load()
pyjd.run()