From 564be9af089b422af2bc88753999e0f29218bfe0 Mon Sep 17 00:00:00 2001 From: gratimax Date: Mon, 14 Oct 2013 17:59:27 -0700 Subject: [PATCH] begin refactor --- .gitignore | 36 ++++++ LICENSE | 22 ++++ scratra.py | 317 +++++++++++++++++++++++++++-------------------------- 3 files changed, 218 insertions(+), 157 deletions(-) create mode 100644 .gitignore create mode 100644 LICENSE diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5684153 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +*.py[cod] + +# C extensions +*.so + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg +lib +lib64 +__pycache__ + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox +nosetests.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fa7a805 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2013 scratra + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/scratra.py b/scratra.py index aeb0e99..6da04ae 100644 --- a/scratra.py +++ b/scratra.py @@ -1,12 +1,12 @@ -#scratra ~ 0.2 -#greatdane ~ easy python implementation with scratch -#inspired by sinatra(sinatrarb.com) ~ code snippets from scratch.py(bit.ly/scratchpy) +# scratra ~ 0.3 +# greatdane ~ easy python implementation with scratch +# inspired by sinatra(sinatrarb.com) ~ code snippets from scratch.py(bit.ly/scratchpy) import socket from errno import * from array import array import threading -#Errors from scratch.py +# Errors from scratch.py class ScratchConnectionError(Exception): pass class ScratchNotConnected(ScratchConnectionError): pass class ScratchConnectionRefused(ScratchConnectionError): pass @@ -23,180 +23,183 @@ class ScratchInvalidValue(Exception): pass runtime_quit = 0 scratchInterface = None -#Implementation for Scratch variables -class ScratchVars: +# Implementation for Scratch variables +class RemoteSensors: + + sensor_values = {} + + def __setitem__(self, sensor_name, value): + if isinstance(value, str): + v = Scratch.toScratchMessage('sensor-update "' + sensor_name +'" "'+value+'"') + self.sensor_valueues[sensor_name] = value + scratchSocket.send(v) + elif isinstance(value, int) or isinstance(value, float): + v = Scratch.toScratchMessage('sensor-update "' + sensor_name +'" ' + str(value)) + self.sensor_valueues[sensor_name] = value + scratchSocket.send(v) + else: + raise ScratchInvalidValue(sensor_name + ': Incorrect attempted value') - var_dict = {} + def __getitem__(self, sensor_name): + return self.sensor_values[sensor_name] - def __setitem__(self, var, val): - if isinstance(val, str): - v = Scratch.toScratchMessage('sensor-update "' + var +'" "'+val+'"') - self.var_dict[var] = val - scratchSocket.send(v) - elif isinstance(val, int): - v = Scratch.toScratchMessage('sensor-update "' + var +'" ' + str(val)) - self.var_dict[var] = val - scratchSocket.send(v) - else: - raise ScratchInvalidValue(var + ': Incorrect attempted value') - - def __getitem__(self, var): - return self.var_dict[var] - -#For general convenience, scratch interface +# For general convenience, scratch interface class Scratch: + + # Variables interface + sensor = RemoteSensors() + var_values = {} + + # Broadcast interface + def broadcast(self, *broadcasts): + for broadcast_name in broadcasts: + scratchSocket.send(self.toScratchMessage('broadcast "' + broadcast_name + '"')) - #Variables interface - val = ScratchVars() - var_dict = {} + # Variable interface + def var(self, var_name): + return self.var_values[var_name] - #Broadcast interface - def broadcast(self, *broadcast): - for br in broadcast: - scratchSocket.send(self.toScratchMessage('broadcast "' + br + '"')) - - #Variable interface - def var(self, var_name): - return self.var_dict[var_name] - - @staticmethod - def toScratchMessage(cmd): - #Taken from chalkmarrow - n = len(cmd) - a = array('c') - a.append(chr((n >> 24) & 0xFF)) - a.append(chr((n >> 16) & 0xFF)) - a.append(chr((n >> 8) & 0xFF)) - a.append(chr(n & 0xFF)) - return a.tostring() + cmd + @staticmethod + def toScratchMessage(cmd): + # Taken from chalkmarrow + n = len(cmd) + a = array('c') + a.append(chr((n >> 24) & 0xFF)) + a.append(chr((n >> 16) & 0xFF)) + a.append(chr((n >> 8) & 0xFF)) + a.append(chr(n & 0xFF)) + return a.tostring() + cmd - @staticmethod - def atom(msg): - try: - return int(msg) - except: - return msg.strip('"') + @staticmethod + def atom(msg): + try: + return int(msg) + except: + try: + return float(msg) + except: + return msg.strip('"') def run(host='localhost', poll=True, msg="Scratra -> Connected\n-> 'stop' to quit", console=True): - runClass(host, poll, msg, console).start() + runClass(host, poll, msg, console).start() -#actual threading process +# actual threading process class runClass(threading.Thread): - def __init__(self, host, poll, msg, console): - self.host = host - self.poll = poll - self.msg = msg - self.console = console - threading.Thread.__init__(self) + def __init__(self, host, poll, msg, console): + self.host = host + self.poll = poll + self.msg = msg + self.console = console + threading.Thread.__init__(self) - def run(self): - host = self.host - poll = self.poll - port = 42001 - console = self.console - while 1: - try: scratchSocket.connect((host, port)) - #Except series from scratch.py - except socket.error as error: - (err, msge) = error - if err == EISCONN: - raise ScratchConnectionEstablished('Already connected to Scratch') - elif poll == True: - continue - elif err == ECONNREFUSED: - raise ScratchConnectionRefused('Connection refused, try enabling remote sensor connections') - else: - raise ScratchConnectionError(msge) - scratchInterface = Scratch() - break - if console: - run_console(self.msg).start() - for func in start_list: - func(scratchInterface) - while not runtime_quit: - try: - msg = scratchSocket.recv(1024) - except socket.error as (errno, message): - raise ScratchConnectionError(errno, message) - if msg: - #If the message is not a sensor-update, but a broadcast - if msg.find('sensor-update')==-1 and 'broadcast' in msg: - msg = msg[15:-1] - if msg in broadcast_map: - for func in broadcast_map[msg]: - func(scratchInterface) - #Otherwise, it must be a sensor-update - else: - msg = msg[4:] - if 'sensor-update' in msg: - msg = msg.split()[1:] - i = 0 - while i < len(msg)-1: - if scratchInterface.atom(msg[i]) in update_map: - scratchInterface.var_dict[scratchInterface.atom(msg[i])] = scratchInterface.atom(msg[i+1]) - for func in update_map[scratchInterface.atom(msg[i])]: - func(scratchInterface, scratchInterface.atom(msg[i+1])) - i+=2 - + def run(self): + host = self.host + poll = self.poll + port = 42001 + console = self.console + while 1: + try: scratchSocket.connect((host, port)) + # Except series from scratch.py + except socket.error as error: + (err, msge) = error + if err == EISCONN: + raise ScratchConnectionEstablished('Already connected to Scratch') + elif poll == True: + continue + elif err == ECONNREFUSED: + raise ScratchConnectionRefused('Connection refused, try enabling remote sensor connections') + else: + raise ScratchConnectionError(msge) + scratchInterface = Scratch() + break + if console: + run_console(self.msg).start() + for func in start_list: + func(scratchInterface) + while not runtime_quit: + try: + msg = scratchSocket.recv(1024) + except socket.error as (errno, message): + raise ScratchConnectionError(errno, message) + if msg: + # If the message is not a sensor-update, but a broadcast + if msg.find('sensor-update')==-1 and 'broadcast' in msg: + msg = msg[15:-1] + if msg in broadcast_map: + for func in broadcast_map[msg]: + func(scratchInterface) + # Otherwise, it must be a sensor-update + else: + msg = msg[4:] + if 'sensor-update' in msg: + msg = msg.split()[1:] + i = 0 + while i < len(msg)-1: + if scratchInterface.atom(msg[i]) in update_map: + scratchInterface.var_values[scratchInterface.atom(msg[i])] = scratchInterface.atom(msg[i+1]) + for func in update_map[scratchInterface.atom(msg[i])]: + func(scratchInterface, scratchInterface.atom(msg[i+1])) + i+=2 + class run_console(threading.Thread): - def __init__(self, msg): - self.msg = msg - threading.Thread.__init__(self) - - def run(self): - global runtime_quit - print self.msg - while not runtime_quit: - cmd = raw_input('-> ') - if cmd == 'stop': - runtime_quit = 1 - print '-> Quitting' - for func in end_list: - func(scratchInterface) - + def __init__(self, msg): + self.msg = msg + threading.Thread.__init__(self) + + def run(self): + global runtime_quit + print self.msg + while not runtime_quit: + cmd = raw_input('-> ') + if cmd == 'stop': + runtime_quit = 1 + print '-> Quitting' + for func in end_list: + func(scratchInterface) + -#For user convenience, decorator methods +# For user convenience, decorator methods -#When Scratch broadcasts this... -#@broadcast('scratch_broadcast') -#def func(scratch): .... +# When Scratch broadcasts this... +# @broadcast('scratch_broadcast') +# def func(scratch): .... class broadcast: + + def __init__(self, broadcast): + self.b = broadcast - def __init__(self, broadcast): - self.b = broadcast - - def __call__(self, func): - if self.b in broadcast_map: - broadcast_map[self.b].append(func) - else: - broadcast_map[self.b] = [func] - -#When this variable is updated... -#@update('variable') -#def func(scratch, value): ... + def __call__(self, func): + if self.b in broadcast_map: + broadcast_map[self.b].append(func) + else: + broadcast_map[self.b] = [func] + +# When this variable is updated... +# @update('variable') +# def func(scratch, value): ... class update: + + def __init__(self, update): + self.u = update - def __init__(self, update): - self.u = update - - def __call__(self, func): - if self.u in update_map: - update_map[self.u].append(func) - else: - update_map[self.u] = [func] + def __call__(self, func): + if self.u in update_map: + update_map[self.u].append(func) + else: + update_map[self.u] = [func] -#When we start listening... -#@start -#def func(scratch): ... +# When we start listening... +# @start +# def func(scratch): ... def start(func): - if func not in start_list: - start_list.append(func) + if func not in start_list: + start_list.append(func) -#When we stop listening -#@end -#def func(scratch): ... +# When we stop listening +# @end +# def func(scratch): ... def end(func): - if func not in end_list: - end_list.append(func) \ No newline at end of file + if func not in end_list: + end_list.append(func) \ No newline at end of file