diff --git a/examples/boolean_client.py b/examples/boolean_client.py index 499fcee..f1e8434 100644 --- a/examples/boolean_client.py +++ b/examples/boolean_client.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 import time import locale @@ -26,10 +26,10 @@ server = "sandbox.spacebrew.cc" for cur_ele in sys.argv: - if "name" in cur_ele: - name = cur_ele[5:] - if "server" in cur_ele: - server = cur_ele[7:] + if "name" in cur_ele: + name = cur_ele[5:] + if "server" in cur_ele: + server = cur_ele[7:] # configure the spacebrew client @@ -37,53 +37,57 @@ brew.addPublisher("local state", "boolean") brew.addSubscriber("remote state", "boolean") + def handleBoolean(value): - global code, stdscr - stdscr.addstr(pos_remote, pos_state, (str(value) + " ").encode(code)) - stdscr.refresh() + global code, stdscr + stdscr.addstr(pos_remote, pos_state, (str(value) + " ").encode(code)) + stdscr.refresh() + brew.subscribe("remote state", handleBoolean) try: - # start-up spacebrew - brew.start() - - # create and load info message at the top of the terminal window - info_msg = "This is the pySpacebrew library boolean example. It sends out a boolean message every time\n" - info_msg += "the enter or return key is pressed and displays the latest boolean value it has received.\n" - info_msg += "Connected to Spacebrew as: " + name + "\n" - info_msg += "IMPORTANT: don't shrink the Terminal window as it may cause app to crash (bug with curses lib)." - stdscr.addstr(0, 0, info_msg.encode(code)) - stdscr.refresh() - - # update the location for the remote and local dice state - pos_local = stdscr.getyx()[0] + 2 - pos_remote = pos_local + 2 - - # display the label for the remote and local boolean states - stdscr.addstr(pos_local, 0, "local state: ".encode(code), curses.A_BOLD) - stdscr.addstr(pos_remote, 0, "remote state: ".encode(code), curses.A_BOLD) - - # display the starting state for remote and local boolean states - stdscr.addstr(pos_local, pos_state, (str(local_state) + " ").encode(code)) - stdscr.addstr(pos_remote, pos_state, (str(remote_state) + " ").encode(code)) - stdscr.refresh() - - # listen for keypresses and handle input - while 1: - c = stdscr.getch() - - if (c == 10 or c == 13): - local_state = not local_state - brew.publish('local state', local_state) - stdscr.addstr(pos_local, pos_state, (str(local_state) + " ").encode(code)) - - stdscr.refresh() + # start-up spacebrew + brew.start() + + # create and load info message at the top of the terminal window + info_msg = "This is the pySpacebrew library boolean example. It sends out a boolean message every time\n" + info_msg += "the enter or return key is pressed and displays the latest boolean value it has received.\n" + info_msg += "Connected to Spacebrew as: " + name + "\n" + info_msg += "IMPORTANT: don't shrink the Terminal window as it may cause app to crash (bug with curses lib)." + stdscr.addstr(0, 0, info_msg.encode(code)) + stdscr.refresh() + + # update the location for the remote and local dice state + pos_local = stdscr.getyx()[0] + 2 + pos_remote = pos_local + 2 + + # display the label for the remote and local boolean states + stdscr.addstr(pos_local, 0, "local state: ".encode(code), curses.A_BOLD) + stdscr.addstr(pos_remote, 0, "remote state: ".encode(code), curses.A_BOLD) + + # display the starting state for remote and local boolean states + stdscr.addstr(pos_local, pos_state, (str(local_state) + " ").encode(code)) + stdscr.addstr(pos_remote, pos_state, + (str(remote_state) + " ").encode(code)) + stdscr.refresh() + + # listen for keypresses and handle input + while 1: + c = stdscr.getch() + + if (c == 10 or c == 13): + local_state = not local_state + brew.publish('local state', local_state) + stdscr.addstr(pos_local, pos_state, + (str(local_state) + " ").encode(code)) + + stdscr.refresh() # closing out the app and returning terminal to old settings finally: - brew.stop() - curses.nocbreak() - stdscr.keypad(0) - curses.echo() - curses.endwin() + brew.stop() + curses.nocbreak() + stdscr.keypad(0) + curses.echo() + curses.endwin() diff --git a/examples/custom_client.py b/examples/custom_client.py index 5acaca8..e67a8ff 100644 --- a/examples/custom_client.py +++ b/examples/custom_client.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 import time import locale @@ -27,10 +27,10 @@ server = "sandbox.spacebrew.cc" for cur_ele in sys.argv: - if "name" in cur_ele: - name = cur_ele[5:] - if "server" in cur_ele: - server = cur_ele[7:] + if "name" in cur_ele: + name = cur_ele[5:] + if "server" in cur_ele: + server = cur_ele[7:] # configure the spacebrew client brew = Spacebrew(name=name, server=server) @@ -38,59 +38,65 @@ brew.addSubscriber("what did you roll", "dice") # function that handles the incoming spacebrew dice messages + + def handleDice(value): - global code, stdscr - stdscr.addstr(pos_remote, pos_state, (" " * 30).encode(code)) - if value < 1 or value > 6: - stdscr.addstr(pos_remote, pos_state, ("you rolled a " + str(value) + "! What kind of dice is that? ").encode(code)) - else: - stdscr.addstr(pos_remote, pos_state, str(value).encode(code)) - stdscr.refresh() + global code, stdscr + stdscr.addstr(pos_remote, pos_state, (" " * 30).encode(code)) + if value < 1 or value > 6: + stdscr.addstr(pos_remote, pos_state, ("you rolled a " + + str(value) + "! What kind of dice is that? ").encode(code)) + else: + stdscr.addstr(pos_remote, pos_state, str(value).encode(code)) + stdscr.refresh() + # register handler function with appropriate subscription data feed brew.subscribe("what did you roll", handleDice) try: - # start-up spacebrew - brew.start() - - # create and load info message at the top of the terminal window - info_msg = "This is the pySpacebrew library custom data type example. It rolls the dice every time the enter or return\n" - info_msg += "key is pressed (value between 0 and 6), and displays the latest dice roll value it has received.\n" - info_msg += "Connected to Spacebrew as: " + name + "\n" - info_msg += "IMPORTANT: don't shrink the Terminal window as it may cause app to crash (bug with curses lib)." - stdscr.addstr(0, 0, info_msg.encode(code)) - stdscr.refresh() - - # update the location for the remote and local dice state - pos_local = stdscr.getyx()[0] + 2 - pos_remote = pos_local + 2 - - # display the label for the remote and local dice roll states - stdscr.addstr(pos_local, 0, "local dice roll: ".encode(code), curses.A_BOLD) - stdscr.addstr(pos_remote, 0, "remote dice roll: ".encode(code), curses.A_BOLD) - - # display the starting prompt next to local and remote dice state lines - stdscr.addstr(pos_local, pos_state, "waiting for first roll".encode(code)) - stdscr.addstr(pos_remote, pos_state, "waiting for first roll".encode(code)) - stdscr.refresh() - - # listen for keypresses and handle input - while 1: - c = stdscr.getch() - - if (c == 10 or c == 13): - local_state = random.randint(1,6) - brew.publish("roll of the dice", local_state) - stdscr.addstr(pos_local, pos_state, (" " * 30).encode(code)) - stdscr.addstr(pos_local, pos_state, str(local_state).encode(code)) - - stdscr.refresh() + # start-up spacebrew + brew.start() + + # create and load info message at the top of the terminal window + info_msg = "This is the pySpacebrew library custom data type example. It rolls the dice every time the enter or return\n" + info_msg += "key is pressed (value between 0 and 6), and displays the latest dice roll value it has received.\n" + info_msg += "Connected to Spacebrew as: " + name + "\n" + info_msg += "IMPORTANT: don't shrink the Terminal window as it may cause app to crash (bug with curses lib)." + stdscr.addstr(0, 0, info_msg.encode(code)) + stdscr.refresh() + + # update the location for the remote and local dice state + pos_local = stdscr.getyx()[0] + 2 + pos_remote = pos_local + 2 + + # display the label for the remote and local dice roll states + stdscr.addstr(pos_local, 0, "local dice roll: ".encode( + code), curses.A_BOLD) + stdscr.addstr(pos_remote, 0, "remote dice roll: ".encode( + code), curses.A_BOLD) + + # display the starting prompt next to local and remote dice state lines + stdscr.addstr(pos_local, pos_state, "waiting for first roll".encode(code)) + stdscr.addstr(pos_remote, pos_state, "waiting for first roll".encode(code)) + stdscr.refresh() + + # listen for keypresses and handle input + while 1: + c = stdscr.getch() + + if (c == 10 or c == 13): + local_state = random.randint(1, 6) + brew.publish("roll of the dice", local_state) + stdscr.addstr(pos_local, pos_state, (" " * 30).encode(code)) + stdscr.addstr(pos_local, pos_state, str(local_state).encode(code)) + + stdscr.refresh() # closing out the app and returning terminal to old settings finally: - brew.stop() - curses.nocbreak() - stdscr.keypad(0) - curses.echo() - curses.endwin() + brew.stop() + curses.nocbreak() + stdscr.keypad(0) + curses.echo() + curses.endwin() diff --git a/examples/pySpacebrew/spacebrew.py b/examples/pySpacebrew/spacebrew.py index a27f124..faa7b9f 100644 --- a/examples/pySpacebrew/spacebrew.py +++ b/examples/pySpacebrew/spacebrew.py @@ -4,134 +4,144 @@ import logging import time + class Spacebrew(object): - # Define any runtime errors we'll need - class ConfigurationError(Exception): - def __init__(self, brew, explanation): - self.brew = brew - self.explanation = explanation - def __str__(self): - return repr(self.explanation) - - class Slot(object): - def __init__(self, name, brewType, default = None): - self.name = name - self.type = brewType - self.value = None - self.default = default - def makeConfig(self): - d = { 'name':self.name, 'type':self.type, 'default':self.default } - return d - - class Publisher(Slot): - pass - - class Subscriber(Slot): - def __init__(self, name, brewType, default = None): - super(Spacebrew.Subscriber,self).__init__(name,brewType,default) - self.callbacks=[] - def subscribe(self, target): - self.callbacks.append(target) - def unsubscribe(self, target): - self.callbacks.remove(target) - def disseminate(self, value): - for target in self.callbacks: - target(value) - - def __init__(self, name, description="", server="sandbox.spacebrew.cc", port=9000): - self.server = server - self.port = port - self.name = name - self.description = description - self.connected = False - self.started = False - self.publishers = {} - self.subscribers = {} - self.ws = None - - def addPublisher(self, name, brewType="string", default=None): - if self.connected: - raise ConfigurationError(self,"Can not add a new publisher to a running Spacebrew instance (yet).") - else: - self.publishers[name]=self.Publisher(name, brewType, default) - - def addSubscriber(self, name, brewType="string", default=None): - if self.connected: - raise ConfigurationError(self,"Can not add a new subscriber to a running Spacebrew instance (yet).") - else: - self.subscribers[name]=self.Subscriber(name, brewType, default) - - def makeConfig(self): - subs = map(lambda x:x.makeConfig(),self.subscribers.values()) - pubs = map(lambda x:x.makeConfig(),self.publishers.values()) - d = {'config':{ - 'name':self.name, - 'description':self.description, - 'publish':{'messages':pubs}, - 'subscribe':{'messages':subs}, - }} - return d - - def on_open(self,ws): - logging.info("Opening brew.") - ws.send(json.dumps(self.makeConfig())) - self.connected = True - - def on_message(self,ws,message): - msg = json.loads(message)['message'] - sub=self.subscribers[msg['name']] - sub.disseminate(msg['value']) - - def on_error(self,ws,error): - logging.error("[on_error] ERROR: {0}".format(error)) - logging.error("[on_error] self started " + str(self.started)) - self.on_close(ws) - - def on_close(self, ws): - self.connected = False - while self.started and not self.connected: - time.sleep(5) - self.run() - - def publish(self,name,value): - publisher = self.publishers[name] - message = {'message': { - 'clientName':self.name, - 'name':publisher.name, - 'type':publisher.type, - 'value':value } } - self.ws.send(json.dumps(message)) - - def subscribe(self,name,target): - subscriber = self.subscribers[name] - subscriber.subscribe(target) - - def run(self): - self.ws = websocket.WebSocketApp( "ws://{0}:{1}".format(self.server,self.port), - on_message = lambda ws, msg: self.on_message(ws, msg), - on_error = lambda ws, err: self.on_error(ws,err), - on_close = lambda ws: self.on_close(ws) ) - self.ws.on_open = lambda ws: self.on_open(ws) - self.ws.run_forever() - self.ws = None - - def start(self): - def run(*args): - self.run() - self.started = True - self.thread = threading.Thread(target=run) - self.thread.start() - - def stop(self): - self.started = False - if self.ws is not None: - self.ws.close() - self.thread.join() + # Define any runtime errors we'll need + class ConfigurationError(Exception): + def __init__(self, brew, explanation): + self.brew = brew + self.explanation = explanation + + def __str__(self): + return repr(self.explanation) + + class Slot(object): + def __init__(self, name, brewType, default=None): + self.name = name + self.type = brewType + self.value = None + self.default = default + + def makeConfig(self): + d = {'name': self.name, 'type': self.type, 'default': self.default} + return d + + class Publisher(Slot): + pass + + class Subscriber(Slot): + def __init__(self, name, brewType, default=None): + super(Spacebrew.Subscriber, self).__init__(name, brewType, default) + self.callbacks = [] + + def subscribe(self, target): + self.callbacks.append(target) + + def unsubscribe(self, target): + self.callbacks.remove(target) + + def disseminate(self, value): + for target in self.callbacks: + target(value) + + def __init__(self, name, description="", server="sandbox.spacebrew.cc", port=9000): + self.server = server + self.port = port + self.name = name + self.description = description + self.connected = False + self.started = False + self.publishers = {} + self.subscribers = {} + self.ws = None + + def addPublisher(self, name, brewType="string", default=None): + if self.connected: + raise ConfigurationError( + self, "Can not add a new publisher to a running Spacebrew instance (yet).") + else: + self.publishers[name] = self.Publisher(name, brewType, default) + + def addSubscriber(self, name, brewType="string", default=None): + if self.connected: + raise ConfigurationError( + self, "Can not add a new subscriber to a running Spacebrew instance (yet).") + else: + self.subscribers[name] = self.Subscriber(name, brewType, default) + + def makeConfig(self): + subs = map(lambda x: x.makeConfig(), self.subscribers.values()) + pubs = map(lambda x: x.makeConfig(), self.publishers.values()) + d = {'config': { + 'name': self.name, + 'description': self.description, + 'publish': {'messages': list(pubs)}, + 'subscribe': {'messages': list(subs)}, + }} + return d + + def on_open(self, ws): + logging.info("Opening brew.") + ws.send(json.dumps(self.makeConfig())) + self.connected = True + + def on_message(self, ws, message): + msg = json.loads(message)['message'] + sub = self.subscribers[msg['name']] + sub.disseminate(msg['value']) + + def on_error(self, ws, error): + logging.error("[on_error] ERROR: {0}".format(error)) + logging.error("[on_error] self started " + str(self.started)) + self.on_close(ws) + + def on_close(self, ws): + self.connected = False + while self.started and not self.connected: + time.sleep(5) + self.run() + + def publish(self, name, value): + publisher = self.publishers[name] + message = {'message': { + 'clientName': self.name, + 'name': publisher.name, + 'type': publisher.type, + 'value': value}} + self.ws.send(json.dumps(message)) + + def subscribe(self, name, target): + subscriber = self.subscribers[name] + subscriber.subscribe(target) + + def run(self): + self.ws = websocket.WebSocketApp("ws://{0}:{1}".format(self.server, self.port), + on_message=lambda ws, msg: self.on_message( + ws, msg), + on_error=lambda ws, err: self.on_error( + ws, err), + on_close=lambda ws: self.on_close(ws)) + self.ws.on_open = lambda ws: self.on_open(ws) + self.ws.run_forever() + self.ws = None + + def start(self): + def run(*args): + self.run() + self.started = True + self.thread = threading.Thread(target=run) + self.thread.start() + + def stop(self): + self.started = False + if self.ws is not None: + self.ws.close() + self.thread.join() + if __name__ == "__main__": - print """ + print(""" This is the Spacebrew module. See spacebrew_ex.py for usage examples. -""" - +""") diff --git a/examples/range_client.py b/examples/range_client.py index ef0d761..7225dc8 100644 --- a/examples/range_client.py +++ b/examples/range_client.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 import time import locale @@ -21,10 +21,10 @@ server = "sandbox.spacebrew.cc" for cur_ele in sys.argv: - if "name" in cur_ele: - name = cur_ele[5:] - if "server" in cur_ele: - server = cur_ele[7:] + if "name" in cur_ele: + name = cur_ele[5:] + if "server" in cur_ele: + server = cur_ele[7:] # configure the spacebrew client brew = Spacebrew(name, server=server) @@ -42,74 +42,81 @@ remote_state = 0 # method that updates the range "bars" and value on the display + + def displayRange(value, source_line): - stdscr.addstr(source_line, pos_int, " ".encode(code)) - stdscr.addstr(source_line, pos_int, str(value).encode(code)) - stdscr.addstr(source_line, pos_state, "".encode(code)) - for i in range(100): - if (value / 10) > i: - stdscr.addstr(" ".encode(code), curses.A_STANDOUT) - else: - stdscr.addstr(" ".encode(code)) - stdscr.refresh() + stdscr.addstr(source_line, pos_int, " ".encode(code)) + stdscr.addstr(source_line, pos_int, str(value).encode(code)) + stdscr.addstr(source_line, pos_state, "".encode(code)) + for i in range(100): + if (value / 10) > i: + stdscr.addstr(" ".encode(code), curses.A_STANDOUT) + else: + stdscr.addstr(" ".encode(code)) + stdscr.refresh() # function that handles the incoming spacebrew range messages + + def handleRange(value): - global code, stdscr - remote_state = int(value) - displayRange(remote_state, col_remote) + global code, stdscr + remote_state = int(value) + displayRange(remote_state, col_remote) + # registering range handler method with appropriate subscription feed brew.subscribe("graph", handleRange) try: - brew.start() - - # set app information message - info_msg = "This is the pySpacebrew library range example. This app sends out a range value, between 0 and 1023. The value \n" - info_msg += "increases and decreases in response to '+'/'=' and `-`/'_' key presses. App also displays a range value received\n" - info_msg += "from Spacebrew. Connected as: " + name + "\n" - info_msg += "IMPORTANT: don't shrink the Terminal window as it may cause app to crash (bug with curses lib)." - stdscr.addstr(0, 0, info_msg.encode(code)) - stdscr.refresh() - - # update the location of the local and remote range linds - col_local = stdscr.getyx()[0] + 2 - col_remote = col_local + 2 - - # display the label for the remote and local range states - stdscr.addstr(col_local, 0, "local range: ".encode(code), curses.A_BOLD) - stdscr.addstr(col_remote, 0, "remote range: ".encode(code), curses.A_BOLD) - - # display the starting state for remote and local range states - displayRange(local_state, col_local) - displayRange(remote_state, col_remote) - stdscr.refresh() - - # listen for keypresses and handle input - while 1: - c = stdscr.getch() - - new_data = False - if c == ord('+') or c == ord('='): - local_state += 10 - if local_state > 1023: local_state = 1023 - new_data = True - elif c == ord('-') or c == ord('_'): - local_state -= 10 - if local_state < 0: local_state = 0 - new_data = True - - if new_data: - brew.publish("slider", local_state) - displayRange(local_state, col_local) - - stdscr.refresh() + brew.start() + + # set app information message + info_msg = "This is the pySpacebrew library range example. This app sends out a range value, between 0 and 1023. The value \n" + info_msg += "increases and decreases in response to '+'/'=' and `-`/'_' key presses. App also displays a range value received\n" + info_msg += "from Spacebrew. Connected as: " + name + "\n" + info_msg += "IMPORTANT: don't shrink the Terminal window as it may cause app to crash (bug with curses lib)." + stdscr.addstr(0, 0, info_msg.encode(code)) + stdscr.refresh() + + # update the location of the local and remote range linds + col_local = stdscr.getyx()[0] + 2 + col_remote = col_local + 2 + + # display the label for the remote and local range states + stdscr.addstr(col_local, 0, "local range: ".encode(code), curses.A_BOLD) + stdscr.addstr(col_remote, 0, "remote range: ".encode(code), curses.A_BOLD) + + # display the starting state for remote and local range states + displayRange(local_state, col_local) + displayRange(remote_state, col_remote) + stdscr.refresh() + + # listen for keypresses and handle input + while 1: + c = stdscr.getch() + + new_data = False + if c == ord('+') or c == ord('='): + local_state += 10 + if local_state > 1023: + local_state = 1023 + new_data = True + elif c == ord('-') or c == ord('_'): + local_state -= 10 + if local_state < 0: + local_state = 0 + new_data = True + + if new_data: + brew.publish("slider", local_state) + displayRange(local_state, col_local) + + stdscr.refresh() # closing out the app and returning terminal to old settings finally: - brew.stop() - curses.nocbreak() - stdscr.keypad(0) - curses.echo() - curses.endwin() + brew.stop() + curses.nocbreak() + stdscr.keypad(0) + curses.echo() + curses.endwin() diff --git a/examples/string_client.py b/examples/string_client.py index 2954a06..8546a0f 100644 --- a/examples/string_client.py +++ b/examples/string_client.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 import time import locale @@ -21,28 +21,30 @@ server = "sandbox.spacebrew.cc" for cur_ele in sys.argv: - if "name" in cur_ele: - name = cur_ele[5:] - if "server" in cur_ele: - server = cur_ele[7:] + if "name" in cur_ele: + name = cur_ele[5:] + if "server" in cur_ele: + server = cur_ele[7:] # configure the spacebrew client brew = Spacebrew(name, server=server) brew.addPublisher("chat outgoing", "string") brew.addSubscriber("chat incoming", "string") + def handleString(value): - global pos, code, stdscr - stdscr.addstr(pos_in, 0, "incoming: ".encode(code), curses.A_BOLD) - stdscr.addstr(pos_in, pos_msg, (" " * pos_max).encode(code)) - stdscr.addstr(pos_in, pos["x"] + pos_msg, value.encode(code)) - stdscr.refresh() - pos["y"] += 1 + global pos, code, stdscr + stdscr.addstr(pos_in, 0, "incoming: ".encode(code), curses.A_BOLD) + stdscr.addstr(pos_in, pos_msg, (" " * pos_max).encode(code)) + stdscr.addstr(pos_in, pos["x"] + pos_msg, value.encode(code)) + stdscr.refresh() + pos["y"] += 1 + brew.subscribe("chat incoming", handleString) # set-up a variables to hold current position -pos = { "x":0, "y":0 } +pos = {"x": 0, "y": 0} # line positions pos_type = 0 @@ -55,60 +57,66 @@ def handleString(value): pos_con = pos_msg + pos_max + 5 try: - brew.start() - - info_msg = "This is the pySpacebrew library string example. It functions like a chat program. It can\n" - info_msg += "send messages up 60 char long and it displays the first 60 chars of incoming messages.\n" - info_msg += "Connected to Spacebrew as: " + name + "\n" - info_msg += "IMPORTANT: don't shrink the Terminal window as it may cause app to crash (bug with curses lib)." - - stdscr.addstr(0, 0, info_msg.encode(code)) - stdscr.refresh() - - pos_type = stdscr.getyx()[0] + 2 - pos_out = pos_type + 2 - pos_in = pos_out + 1 - stdscr.addstr(pos_type, 0, "new msg: ".encode(code), curses.A_BOLD) - stdscr.refresh() - - column_str = stdscr.getyx()[1] - cur_line = "" - - while 1: - c = stdscr.getch() - - if (c == 10 or c == 13) and len(cur_line) > 0: - brew.publish('chat outgoing',cur_line) - stdscr.addstr(pos_type, pos_con, " cur_line sent ".encode(code), curses.A_STANDOUT) - stdscr.addstr(pos_out, 0, "outgoing: ".encode(code), curses.A_BOLD) - stdscr.addstr(pos_out, pos_msg, (" " * pos_max).encode(code)) - stdscr.addstr(pos_out, pos_msg, cur_line.encode(code)) - - cur_line = "" - stdscr.addstr(pos_type, pos_msg, (" " * pos_max).encode(code)) - - elif (c == 10 or c == 13) and len(cur_line) == 0: - stdscr.addstr(pos_type, pos_con, " no message to send ".encode(code), curses.A_STANDOUT) - - elif c == curses.KEY_DC or c == curses.KEY_BACKSPACE or c == 127: - cur_line = cur_line[0:-1] - stdscr.addstr(pos_type, (pos["x"] + pos_msg + len(cur_line)), " ".encode(code)) - stdscr.addstr(pos_type, pos_con, " ".encode(code)) - - elif len(cur_line) >= (pos_max): - stdscr.addstr(pos_type, pos_con, " your at my limit ".encode(code), curses.A_STANDOUT) - - elif c < 256 and c > 0: - cur_line += chr(c) - stdscr.addstr(pos_type, pos["x"] + pos_msg, cur_line.encode(code)) - stdscr.addstr(pos_type, pos_con, " ".encode(code)) - - stdscr.refresh() + brew.start() + + info_msg = "This is the pySpacebrew library string example. It functions like a chat program. It can\n" + info_msg += "send messages up 60 char long and it displays the first 60 chars of incoming messages.\n" + info_msg += "Connected to Spacebrew as: " + name + "\n" + info_msg += "IMPORTANT: don't shrink the Terminal window as it may cause app to crash (bug with curses lib)." + + stdscr.addstr(0, 0, info_msg.encode(code)) + stdscr.refresh() + + pos_type = stdscr.getyx()[0] + 2 + pos_out = pos_type + 2 + pos_in = pos_out + 1 + stdscr.addstr(pos_type, 0, "new msg: ".encode(code), curses.A_BOLD) + stdscr.refresh() + + column_str = stdscr.getyx()[1] + cur_line = "" + + while 1: + c = stdscr.getch() + + if (c == 10 or c == 13) and len(cur_line) > 0: + brew.publish('chat outgoing', cur_line) + stdscr.addstr(pos_type, pos_con, " cur_line sent ".encode( + code), curses.A_STANDOUT) + stdscr.addstr(pos_out, 0, "outgoing: ".encode(code), curses.A_BOLD) + stdscr.addstr(pos_out, pos_msg, (" " * pos_max).encode(code)) + stdscr.addstr(pos_out, pos_msg, cur_line.encode(code)) + + cur_line = "" + stdscr.addstr(pos_type, pos_msg, (" " * pos_max).encode(code)) + + elif (c == 10 or c == 13) and len(cur_line) == 0: + stdscr.addstr(pos_type, pos_con, " no message to send ".encode( + code), curses.A_STANDOUT) + + elif c == curses.KEY_DC or c == curses.KEY_BACKSPACE or c == 127: + cur_line = cur_line[0:-1] + stdscr.addstr( + pos_type, (pos["x"] + pos_msg + len(cur_line)), " ".encode(code)) + stdscr.addstr(pos_type, pos_con, + " ".encode(code)) + + elif len(cur_line) >= (pos_max): + stdscr.addstr(pos_type, pos_con, " your at my limit ".encode( + code), curses.A_STANDOUT) + + elif c < 256 and c > 0: + cur_line += chr(c) + stdscr.addstr(pos_type, pos["x"] + pos_msg, cur_line.encode(code)) + stdscr.addstr(pos_type, pos_con, + " ".encode(code)) + + stdscr.refresh() # closing out the app and returning terminal to old settings finally: - brew.stop() - curses.nocbreak() - stdscr.keypad(0) - curses.echo() - curses.endwin() + brew.stop() + curses.nocbreak() + stdscr.keypad(0) + curses.echo() + curses.endwin() diff --git a/pySpacebrew/spacebrew.py b/pySpacebrew/spacebrew.py index a27f124..786385a 100644 --- a/pySpacebrew/spacebrew.py +++ b/pySpacebrew/spacebrew.py @@ -4,134 +4,144 @@ import logging import time + class Spacebrew(object): - # Define any runtime errors we'll need - class ConfigurationError(Exception): - def __init__(self, brew, explanation): - self.brew = brew - self.explanation = explanation - def __str__(self): - return repr(self.explanation) - - class Slot(object): - def __init__(self, name, brewType, default = None): - self.name = name - self.type = brewType - self.value = None - self.default = default - def makeConfig(self): - d = { 'name':self.name, 'type':self.type, 'default':self.default } - return d - - class Publisher(Slot): - pass - - class Subscriber(Slot): - def __init__(self, name, brewType, default = None): - super(Spacebrew.Subscriber,self).__init__(name,brewType,default) - self.callbacks=[] - def subscribe(self, target): - self.callbacks.append(target) - def unsubscribe(self, target): - self.callbacks.remove(target) - def disseminate(self, value): - for target in self.callbacks: - target(value) - - def __init__(self, name, description="", server="sandbox.spacebrew.cc", port=9000): - self.server = server - self.port = port - self.name = name - self.description = description - self.connected = False - self.started = False - self.publishers = {} - self.subscribers = {} - self.ws = None - - def addPublisher(self, name, brewType="string", default=None): - if self.connected: - raise ConfigurationError(self,"Can not add a new publisher to a running Spacebrew instance (yet).") - else: - self.publishers[name]=self.Publisher(name, brewType, default) - - def addSubscriber(self, name, brewType="string", default=None): - if self.connected: - raise ConfigurationError(self,"Can not add a new subscriber to a running Spacebrew instance (yet).") - else: - self.subscribers[name]=self.Subscriber(name, brewType, default) - - def makeConfig(self): - subs = map(lambda x:x.makeConfig(),self.subscribers.values()) - pubs = map(lambda x:x.makeConfig(),self.publishers.values()) - d = {'config':{ - 'name':self.name, - 'description':self.description, - 'publish':{'messages':pubs}, - 'subscribe':{'messages':subs}, - }} - return d - - def on_open(self,ws): - logging.info("Opening brew.") - ws.send(json.dumps(self.makeConfig())) - self.connected = True - - def on_message(self,ws,message): - msg = json.loads(message)['message'] - sub=self.subscribers[msg['name']] - sub.disseminate(msg['value']) - - def on_error(self,ws,error): - logging.error("[on_error] ERROR: {0}".format(error)) - logging.error("[on_error] self started " + str(self.started)) - self.on_close(ws) - - def on_close(self, ws): - self.connected = False - while self.started and not self.connected: - time.sleep(5) - self.run() - - def publish(self,name,value): - publisher = self.publishers[name] - message = {'message': { - 'clientName':self.name, - 'name':publisher.name, - 'type':publisher.type, - 'value':value } } - self.ws.send(json.dumps(message)) - - def subscribe(self,name,target): - subscriber = self.subscribers[name] - subscriber.subscribe(target) - - def run(self): - self.ws = websocket.WebSocketApp( "ws://{0}:{1}".format(self.server,self.port), - on_message = lambda ws, msg: self.on_message(ws, msg), - on_error = lambda ws, err: self.on_error(ws,err), - on_close = lambda ws: self.on_close(ws) ) - self.ws.on_open = lambda ws: self.on_open(ws) - self.ws.run_forever() - self.ws = None - - def start(self): - def run(*args): - self.run() - self.started = True - self.thread = threading.Thread(target=run) - self.thread.start() - - def stop(self): - self.started = False - if self.ws is not None: - self.ws.close() - self.thread.join() + # Define any runtime errors we'll need + class ConfigurationError(Exception): + def __init__(self, brew, explanation): + self.brew = brew + self.explanation = explanation + + def __str__(self): + return repr(self.explanation) + + class Slot(object): + def __init__(self, name, brewType, default=None): + self.name = name + self.type = brewType + self.value = None + self.default = default + + def makeConfig(self): + d = {'name': self.name, 'type': self.type, 'default': self.default} + return d + + class Publisher(Slot): + pass + + class Subscriber(Slot): + def __init__(self, name, brewType, default=None): + super(Spacebrew.Subscriber, self).__init__(name, brewType, default) + self.callbacks = [] + + def subscribe(self, target): + self.callbacks.append(target) + + def unsubscribe(self, target): + self.callbacks.remove(target) + + def disseminate(self, value): + for target in self.callbacks: + target(value) + + def __init__(self, name, description="", server="sandbox.spacebrew.cc", port=9000): + self.server = server + self.port = port + self.name = name + self.description = description + self.connected = False + self.started = False + self.publishers = {} + self.subscribers = {} + self.ws = None + + def addPublisher(self, name, brewType="string", default=None): + if self.connected: + raise ConfigurationError( + self, "Can not add a new publisher to a running Spacebrew instance (yet).") + else: + self.publishers[name] = self.Publisher(name, brewType, default) + + def addSubscriber(self, name, brewType="string", default=None): + if self.connected: + raise ConfigurationError( + self, "Can not add a new subscriber to a running Spacebrew instance (yet).") + else: + self.subscribers[name] = self.Subscriber(name, brewType, default) + + def makeConfig(self): + subs = map(lambda x: x.makeConfig(), self.subscribers.values()) + pubs = map(lambda x: x.makeConfig(), self.publishers.values()) + d = {'config': { + 'name': self.name, + 'description': self.description, + 'publish': {'messages': list(pubs)}, + 'subscribe': {'messages': list(subs)}, + }} + return d + + def on_open(self, ws): + logging.info("Opening brew.") + ws.send(json.dumps(self.makeConfig())) + self.connected = True + + def on_message(self, ws, message): + msg = json.loads(message)['message'] + sub = self.subscribers[msg['name']] + sub.disseminate(msg['value']) + + def on_error(self, ws, error): + logging.error("[on_error] ERROR: {0}".format(error)) + logging.error("[on_error] self started " + str(self.started)) + self.on_close(ws) + + def on_close(self, ws): + self.connected = False + while self.started and not self.connected: + time.sleep(5) + self.run() + + def publish(self, name, value): + publisher = self.publishers[name] + message = {'message': { + 'clientName': self.name, + 'name': publisher.name, + 'type': publisher.type, + 'value': value}} + self.ws.send(json.dumps(message)) + + def subscribe(self, name, target): + subscriber = self.subscribers[name] + subscriber.subscribe(target) + + def run(self): + self.ws = websocket.WebSocketApp("ws://{0}:{1}".format(self.server, self.port), + on_message=lambda ws, msg: self.on_message( + ws, msg), + on_error=lambda ws, err: self.on_error( + ws, err), + on_close=lambda ws: self.on_close(ws)) + self.ws.on_open = lambda ws: self.on_open(ws) + self.ws.run_forever() + self.ws = None + + def start(self): + def run(*args): + self.run() + self.started = True + self.thread = threading.Thread(target=run) + self.thread.start() + + def stop(self): + self.started = False + if self.ws is not None: + self.ws.close() + self.thread.join() + if __name__ == "__main__": - print """ + print (""" This is the Spacebrew module. See spacebrew_ex.py for usage examples. -""" - +""")