Lots of regression, refactor and port to Highway networking

Inital refactored version with only one network route. Major changes incoming.
This commit is contained in:
Philip Trauner 2016-10-03 20:38:59 +02:00
parent 9596fa2815
commit eb7692ac9d

View file

@ -17,17 +17,15 @@ from watchdog.events import FileSystemEventHandler
import sublime import sublime
import sublime_plugin import sublime_plugin
import socket from Highway import Client
from ESock import ESock
from Utils import capture_trace
from Sync import SyncClient
import Routing import Routing
from SublimeMenu import * from SublimeMenu import *
import Logging import Logging
import webbrowser import webbrowser
CHANNEL = "s" CHANNEL = 1
def plugin_unloaded(): def plugin_unloaded():
for window in windows: for window in windows:
@ -36,28 +34,32 @@ def plugin_unloaded():
def sock_handler(sock, routes, handler): class Fl0wClient(Client):
while 1: def setup(self, routes, fl0w, debug=False):
try: super().setup(routes, debug=debug)
data = sock.recv() self.fl0w = fl0w
if data[1] in routes:
routes[data[1]].run(data[0], handler)
except (OSError, socket.error, Exception) as e:
if str(e) != "[Errno 9] Bad file descriptor":
capture_trace()
handler.invoke_disconnect()
break
class ReloadHandler(FileSystemEventHandler): def ready(self):
def on_modified(self, event): self.fl0w.connected = True
print("Modified: %s" % event) Logging.info("Connection ready!")
def closed(self, code, reason):
self.fl0w.connected = False
Logging.info("Connection closed: %s (%s)" % (reason, code))
class Info(Routing.ClientRoute):
def run(self, data, handler):
info = ""
for key in data:
info += "%s: %s\n" % (key.capitalize(), ", ".join(data[key]))
sublime.message_dialog(info)
handler.fl0w.invoke_main_menu()
def on_created(self, event):
print("Created: %s" % event)
def on_deleted(self, event):
print("Deleted: %s" % event)
class Fl0w: class Fl0w:
@ -65,201 +67,58 @@ class Fl0w:
self.connected = False self.connected = False
self.window = window self.window = window
self.folder = window.folders()[0] self.folder = window.folders()[0]
self.selected_wallaby = None
self.sock = None
self.s_sync = None
self.start_menu = Menu() self.start_menu = Menu()
self.start_menu.add(Entry("Connect", "Connect to a fl0w server", action=self.invoke_connect)) self.start_menu.add(Entry("Connect", "Connect to a fl0w server", action=self.invoke_connect))
self.start_menu.add(Entry("About", "Information about fl0w", action=self.invoke_about)) self.start_menu.add(Entry("About", "Information about fl0w", action=self.invoke_about))
self.main_menu = Menu() self.main_menu = Menu()
self.main_menu.add(Entry("Wallaby Control", "Control a connected Wallaby", action=self.invoke_wallaby_control)) #self.main_menu.add(Entry("Wallaby Control", "Control a connected Wallaby", action=self.invoke_wallaby_control))
self.main_menu.add(Entry("Info", "Server info", action=self.invoke_info)) self.main_menu.add(Entry("Info", "Server info", action=lambda: self.ws.send(None, "info")))
self.main_menu.add(Entry("Debug", "Debug options", action=self.invoke_debug_options)) #self.main_menu.add(Entry("Debug", "Debug options", action=self.invoke_debug_options))
self.main_menu.add(Entry("Disconnect", "Disconnect from server", action=self.invoke_disconnect)) self.main_menu.add(Entry("Disconnect", "Disconnect from server", action=self.invoke_disconnect))
def invoke_start_menu(self):
self.start_menu.invoke(self.window)
def invoke_main_menu(self):
self.main_menu.invoke(self.window)
def invoke_about(self): def invoke_about(self):
if sublime.ok_cancel_dialog("fl0w by @robot0nfire", "robot0nfire.com"): if sublime.ok_cancel_dialog("fl0w by @robot0nfire", "robot0nfire.com"):
webbrowser.open("http://robot0nfire.com") webbrowser.open("http://robot0nfire.com")
def invoke_info(self):
self.sock.send("", "info")
class Info(Routing.ClientRoute):
def run(self, data, handler):
sublime.message_dialog(data)
handler.main_menu.invoke(handler.window)
class GetInfo(Routing.ClientRoute):
def run(self, data, handler):
if data == "":
handler.sock.send({"type" : CHANNEL}, "get_info")
class ErrorReport(Routing.ClientRoute):
def run(self, data, handler):
sublime.error_message(data)
class Compile(Routing.ClientRoute):
def run(self, data, handler):
status = ""
if data["failed"]:
status = "Compile failed (%s) (%s)." % (data["relpath"], strftime("%H:%M:%S"))
else:
status = "Compile completed successfully. (%s) (%s)." % (data["relpath"], strftime("%H:%M:%S"))
view = handler.window.create_output_panel('compile_panel')
view.set_syntax_file("Packages/fl0w/CompileHighlight.sublime-syntax")
view.settings().set("draw_white_space", "none")
view.settings().set("draw_indent_guides", False)
view.settings().set("gutter", False)
view.settings().set("line_numbers", False)
view.set_read_only(False)
view.run_command("append", {"characters": data["returned"] + status, "scroll_to_end" : True})
view.set_read_only(True)
handler.window.run_command("show_panel", {"panel": "output.compile_panel"})
class StdStream(Routing.ClientRoute):
def __init__(self):
self.view = None
self.buffer = ""
def setup_view(self, handler):
self.view = handler.window.create_output_panel('std_stream_panel')
self.view.settings().set("draw_white_space", "none")
self.view.settings().set("draw_indent_guides", False)
self.view.settings().set("gutter", False)
self.view.settings().set("line_numbers", False)
def run(self, data, handler):
self.setup_view(handler)
handler.window.run_command("show_panel", {"panel": "output.std_stream_panel"})
if type(data) is str:
self.buffer += data
self.append_to_view(self.buffer)
elif type(data) is dict:
if "return_code" in data:
self.append_to_view(self.buffer + "\nProgram finished with return code %d" % data["return_code"])
self.buffer = ""
def append_to_view(self, text):
self.view.set_read_only(False)
self.view.run_command("append", {"characters" : text, "scroll_to_end" : True})
self.view.set_read_only(True)
def connect(self, connect_details): def connect(self, connect_details):
connect_details_raw = connect_details
connect_details = connect_details.split(":")
compression_level = int(sublime.load_settings("fl0w.sublime-settings").get("compression_level")) compression_level = int(sublime.load_settings("fl0w.sublime-settings").get("compression_level"))
try:
if len(connect_details) == 2: self.ws = Fl0wClient('ws://%s' % connect_details)
try: self.ws.setup({"info" : Fl0wClient.Info()}, self, debug=False)
# Establish connection to the server self.ws.connect()
self.sock = ESock(socket.create_connection((connect_details[0], int(connect_details[1]))), sublime.set_timeout_async(self.ws.run_forever, 0)
disconnect_callback=self.invoke_disconnect, debug=False, compression_level=compression_level) except OSError as e:
sublime.status_message("Connected to %s:%s." % (connect_details[0], connect_details[1])) sublime.error_message("Error during connection creation:\n %s" % str(e))
# Initialize all routes
error_report = Fl0w.ErrorReport()
info = Fl0w.Info()
wallaby_control = Fl0w.WallabyControl()
get_info = Fl0w.GetInfo()
std_stream = Fl0w.StdStream()
self.s_sync = SyncClient(self.sock, self.folder, "s_sync")
compile = Fl0w.Compile()
_thread.start_new_thread(sock_handler, (self.sock, {"error_report": error_report,
"info" : info, "wallaby_control" : wallaby_control, "get_info" : get_info,
"s_sync" : self.s_sync, "compile" : compile, "std_stream" : std_stream}, self))
self.s_sync.start()
self.connected = True
# Saving last server address
sublime.load_settings("fl0w.sublime-settings").set("server_address", connect_details_raw)
sublime.save_settings("fl0w.sublime-settings")
except OSError as e:
sublime.error_message("Error during connection creation:\n %s" % str(e))
else:
sublime.error_message("Invalid input.")
# Input invokers
def invoke_connect(self): def invoke_connect(self):
address = sublime.load_settings("fl0w.sublime-settings").get("server_address") # Will be removed once autoconnect works
address = "" if type(address) is not str else address self.connect("127.0.0.1:3077")
Input("Address:Port", initial_text=address, on_done=self.connect).invoke(self.window)
def invoke_disconnect(self): def invoke_disconnect(self):
if self.connected: if self.connected:
self.connected = False self.ws.close()
sublime.message_dialog("Connection closed ('%s')" % self.folder) sublime.message_dialog("Connection closed ('%s')" % self.folder)
self.s_sync.observer.stop()
self.sock.close()
def invoke_wallaby_control(self):
self.sock.send("list_wallaby_controllers", "wallaby_control")
def invoke_run_menu(self):
self.sock.send("list_programs", "wallaby_control")
class WallabyControl(Routing.ClientRoute):
def run(self, data, handler):
if "wallaby_controllers" in data:
wallaby_menu = Menu()
entry_count = 0
for wallaby in data["wallaby_controllers"]:
wallaby_menu.add(Entry(wallaby, str(data["wallaby_controllers"][wallaby]), action=handler.wallaby_control_submenu, kwargs={"wallaby" : wallaby}))
entry_count += 1
if entry_count != 0:
wallaby_menu.invoke(handler.window, back=handler.main_menu)
else:
sublime.error_message("No Wallaby Controllers connected.")
elif "programs" in data:
run_menu = Menu(subtitles=False)
entry_count = 0
for program in data["programs"]:
run_menu.add(Entry(program, action=handler.run_program, kwargs={"program" : program}))
entry_count += 1
if entry_count != 0:
run_menu.invoke(handler.window, back=handler.main_menu)
else:
sublime.error_message("No programs avaliable.")
def run_program(self, program):
self.sock.send({self.selected_wallaby : {"run" : program}}, "wallaby_control")
def wallaby_control_submenu(self, wallaby):
self.selected_wallaby = wallaby
menu = Menu(subtitles=False)
menu.add(Entry("Run", action=self.invoke_run_menu))
for action in ("Stop", "Shutdown", "Reboot", "Disconnect"):
menu.add(Entry(action, action=self.sock.send, kwargs={"data" : {wallaby : [action.lower()]}, "route" : "wallaby_control"}))
menu.invoke(self.window)
def invoke_debug_options(self):
debug_menu = Menu(subtitles=False)
debug_menu.add(Entry("On", action=self.set_debug, kwargs={"debug" : True}))
debug_menu.add(Entry("Off", action=self.set_debug, kwargs={"debug" : False}))
debug_menu.invoke(self.window, back=self.main_menu)
def set_debug(self, debug):
sublime.status_message("fl0w: Debug now '%s'" % debug)
self.sock.debug = debug
sublime.load_settings("fl0w.sublime-settings").set("debug", debug)
sublime.save_settings("fl0w.sublime-settings")
class Fl0wCommand(sublime_plugin.WindowCommand): class Fl0wCommand(sublime_plugin.WindowCommand):
def run(self): def run(self):
@ -277,15 +136,10 @@ class Fl0wCommand(sublime_plugin.WindowCommand):
files = os.listdir(folder) files = os.listdir(folder)
if not ".no-fl0w" in files: if not ".no-fl0w" in files:
if not ".fl0w" in files: if not ".fl0w" in files:
if sublime.ok_cancel_dialog("""We've detected that this is your first time using fl0w in your current directory. open(folder + "/.fl0w", 'a').close()
We don't want to be responsible for any lost work so please backup your files before proceding. self.window.fl0w = Fl0w(self.window)
(An empty project directory is recommended but not necessary.)""", "Yes"): windows.append(self.window)
open(folder + "/.fl0w", 'a').close() self.window.fl0w.start_menu.invoke(self.window)
self.window.fl0w = Fl0w(self.window)
windows.append(self.window)
self.window.fl0w.start_menu.invoke(self.window)
else:
sublime.error_message("fl0w can only be run once you've allowed it to operate in your current directory.")
else: else:
self.window.fl0w = Fl0w(self.window) self.window.fl0w = Fl0w(self.window)
windows.append(self.window) windows.append(self.window)
@ -294,9 +148,9 @@ class Fl0wCommand(sublime_plugin.WindowCommand):
sublime.error_message("fl0w can't be opened in your current directory (.no-fl0w file exists)") sublime.error_message("fl0w can't be opened in your current directory (.no-fl0w file exists)")
else: else:
if not self.window.fl0w.connected: if not self.window.fl0w.connected:
self.window.fl0w.start_menu.invoke(self.window) self.window.fl0w.invoke_start_menu()
else: else:
self.window.fl0w.main_menu.invoke(self.window) self.window.fl0w.invoke_main_menu()
else: else:
if hasattr(self.window, "fl0w"): if hasattr(self.window, "fl0w"):
sublime.error_message("Window setup was invalidated (Don't close or open any additional folders in a fl0w window)") sublime.error_message("Window setup was invalidated (Don't close or open any additional folders in a fl0w window)")