Skip to content

Commit

Permalink
Use Dragonfly's new timers and update Sikuli code (#577)
Browse files Browse the repository at this point in the history
* Use Dragonfly timers

* Make Sikuli work with the new timer

Made the whole thing a class in order to avoid globals

* Remove no longer needed TimerForWSR

* Update pip requirements

* Do not display loading socket error again
  • Loading branch information
comodoro authored and LexiconCode committed Jun 26, 2019
1 parent c72bd12 commit 45b2c03
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 175 deletions.
178 changes: 89 additions & 89 deletions castervoice/asynch/sikuli/sikuli.py
Original file line number Diff line number Diff line change
@@ -1,108 +1,108 @@
from castervoice.lib.imports import *
import xmlrpclib
from subprocess import Popen
import traceback
import socket

grammar = None
custom_rule = None
server_proxy = None

def launch_IDE():
ide_path = settings.SETTINGS["paths"]["SIKULI_IDE"]
if ide_path == "":
print("No 'SIKULI_IDE' path is available. Did you configure it in " + settings.get_filename())
else:
Popen(["java", "-jar", ide_path])
class SikuliController():
grammar = Grammar("sikuli")
custom_rule = None
server_proxy = None
timer = None

def launch_server():
runner_path = settings.SETTINGS["paths"]["SIKULI_RUNNER"]
if runner_path == "":
print("No 'SIKULI_RUNNER' path is available. Did you configure it in " + settings.get_filename())
else:
command = [] if settings.SETTINGS["sikuli"]["version"] == "1.1.3" else ["java", "-jar"]
command.extend([
settings.SETTINGS["paths"]["SIKULI_RUNNER"],
"-r", settings.SETTINGS["paths"]["SIKULI_SERVER_PATH"],
"--args", settings.SETTINGS["paths"]["SIKULI_SCRIPTS_PATH"]
])
Popen(command)
#
# Popen([
# settings.SETTINGS["paths"]["SIKULI_COMPATIBLE_JAVA_EXE_PATH"], "-jar",
# settings.SETTINGS["paths"]["SIKULI_SCRIPTS_JAR_PATH"], "-r",
# settings.SETTINGS["paths"]["SIKULI_SERVER_PATH"]
# ])
def launch_IDE(self):
ide_path = settings.SETTINGS["paths"]["SIKULI_IDE"]
if ide_path == "":
print("No 'SIKULI_IDE' path is available. Did you configure it in " + settings.get_filename())
else:
Popen(["java", "-jar", ide_path])

def execute(fname):
try:
global server_proxy
fn = getattr(server_proxy, fname)
fn()
except Exception:
utilities.simple_log()
def launch_server(self):
runner_path = settings.SETTINGS["paths"]["SIKULI_RUNNER"]
if runner_path == "":
print("No 'SIKULI_RUNNER' path is available. Did you configure it in " + settings.get_filename())
else:
command = [] if settings.SETTINGS["sikuli"]["version"] == "1.1.3" else ["java", "-jar"]
command.extend([
settings.SETTINGS["paths"]["SIKULI_RUNNER"],
"-r", settings.SETTINGS["paths"]["SIKULI_SERVER_PATH"],
"--args", settings.SETTINGS["paths"]["SIKULI_SCRIPTS_PATH"]
])
Popen(command)

def terminate_sick_command():
global server_proxy
global grammar
global custom_rule
grammar.unload()
grammar.remove_rule(custom_rule)
grammar.load()
control.nexus().comm.coms.pop('sikuli')
server_proxy.terminate()
def execute(self, fname):
try:
fn = getattr(self.server_proxy, fname)
fn()
except Exception:
utilities.simple_log()

def start_server_proxy():
global server_proxy
global grammar
server_proxy = control.nexus().comm.get_com("sikuli")
fns = server_proxy.list_functions()
# Even though bootstrap_start_server_proxy() didn't load grammar before,
# you have to unload() here because terminate_sick_command() might have been
# called before and loaded the grammar.
grammar.unload()
populate_grammar(fns)
grammar.load()
print("Caster-Sikuli server started successfully.")
def terminate_sick_command(self):
if self.custom_rule:
self.grammar.unload()
self.grammar.remove_rule(self.custom_rule)
self.grammar.load()
control.nexus().comm.coms.pop('sikuli')
self.server_proxy.terminate()

def populate_grammar(fns):
global grammar
global custom_rule
if len(fns) > 0:
mapping_custom_commands = generate_custom_commands(fns)
custom_rule = MappingRule(mapping=mapping_custom_commands, name="sikuli custom")
grammar.add_rule(custom_rule)
def start_server_proxy(self):
self.server_proxy = control.nexus().comm.get_com("sikuli")
fns = self.server_proxy.list_functions()
# Even though bootstrap_start_server_proxy() didn't load grammar before,
# you have to unload() here because terminate_sick_command() might have been
# called before and loaded the grammar.
self.grammar.unload()
self.populate_grammar(fns)
self.grammar.load()
print("Caster-Sikuli server started successfully.")

def generate_custom_commands(list_of_functions):
mapping = {}
for fname in list_of_functions:
spec = " ".join(fname.split("_"))
mapping[spec] = Function(execute, fname=fname)
return mapping
def populate_grammar(self, fns):
if len(fns) > 0:
mapping_custom_commands = self.generate_custom_commands(fns)
self.custom_rule = MappingRule(mapping=mapping_custom_commands, name="sikuli custom")
self.grammar.add_rule(self.custom_rule)

def server_proxy_timer_fn():
print("Attempting Caster-Sikuli connection [...]")
try:
start_server_proxy()
control.nexus().timer.remove_callback(server_proxy_timer_fn)
except Exception:
pass
def generate_custom_commands(self, list_of_functions):
mapping = {}
for fname in list_of_functions:
spec = " ".join(fname.split("_"))
mapping[spec] = Function(self.execute, fname=fname)
return mapping

def bootstrap_start_server_proxy():
try:
# if the server is already running, this should go off without a hitch
start_server_proxy()
except Exception:
launch_server()
seconds5 = 5
control.nexus().timer.add_callback(server_proxy_timer_fn, seconds5)
def server_proxy_timer_fn(self):
print("Attempting Caster-Sikuli connection [...]")
try:
self.start_server_proxy()
if self.timer:
self.timer.stop()
self.timer = None
except socket.error:
pass
except Exception:
traceback.print_exc()

def bootstrap_start_server_proxy(self):
try:
# if the server is already running, this should go off without a hitch
self.start_server_proxy()
except Exception:
self.launch_server()
seconds5 = 5
self.timer = get_engine().create_timer(self.server_proxy_timer_fn, seconds5)

sikuli = SikuliController()

class SikuliControlCommandsRule(MergeRule):
pronunciation = "sikuli"

mapping = {
"launch sick IDE": R(Function(launch_IDE)),
"launch sick server": R(Function(bootstrap_start_server_proxy)),
"terminate sick server": R(Function(terminate_sick_command)),
"launch sick IDE": R(Function(sikuli.launch_IDE)),
"launch sick server": R(Function(sikuli.bootstrap_start_server_proxy)),
"terminate sick server": R(Function(sikuli.terminate_sick_command)),
}

if settings.SETTINGS["sikuli"]["enabled"]:
control.non_ccr_app_rule(SikuliControlCommandsRule(), context=None, rdp=False)
bootstrap_start_server_proxy()
rule = SikuliControlCommandsRule()
sikuli.grammar.add_rule(rule)
control.non_ccr_app_rule(rule, context=None, rdp=False)
sikuli.bootstrap_start_server_proxy()
5 changes: 1 addition & 4 deletions castervoice/lib/ctrl/nexus.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from castervoice.lib import settings
from castervoice.lib.ctrl.dependencies import DependencyMan
from castervoice.lib.ctrl.wsrdf import TimerForWSR, RecognitionHistoryForWSR
from castervoice.lib.ctrl.wsrdf import RecognitionHistoryForWSR
from castervoice.lib.dfplus.communication import Communicator
from castervoice.lib.dfplus.merge.ccrmerger import CCRMerger
from castervoice.lib.dfplus.state.stack import CasterState
Expand All @@ -21,10 +21,7 @@ def __init__(self, real_merger_config=True):

if settings.WSR or not real_merger_config:
self.history = RecognitionHistoryForWSR(20)
self.timer = TimerForWSR(0.025)
else:
from dragonfly.timer import _Timer
self.timer = _Timer(0.025)
self.history = RecognitionHistory(20)
self.history.register()
self.state.set_stack_history(self.history)
Expand Down
62 changes: 0 additions & 62 deletions castervoice/lib/ctrl/wsrdf.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
import logging
from threading import Timer
import time


class RecognitionHistoryForWSR(list):
'''
Copied verbatim from Dragonfly, but doesn't require Natlink
Expand All @@ -27,60 +22,3 @@ def on_recognition(self, words):

def _recognition_to_item(self, words):
return tuple(words)


class TimerForWSR(object):
'''
Copied verbatim from Dragonfly, but doesn't require
Natlink and has been those parts reimplemented
'''

class Callback(object):
def __init__(self, function, interval):
self.function = function
self.interval = interval
self.next_time = time.clock() + self.interval

def call(self):
self.next_time += self.interval
try:
self.function()
except Exception, e:
logging.getLogger("timer").exception("Exception during timer callback")
print("Exception during timer callback: %s (%r)" % (e, e))

def __init__(self, interval):
self.interval = interval
self.callbacks = []
self._continue = {"_continue": False}

def add_callback(self, function, interval):
self.callbacks.append(self.Callback(function, interval))
if len(self.callbacks) == 1:
self.setTimerCallback(self.callback)

def remove_callback(self, function):
for c in self.callbacks:
if c.function == function: self.callbacks.remove(c)
if len(self.callbacks) == 0:
self.setTimerCallback(None)

def callback(self):
now = time.clock()
for c in self.callbacks:
if c.next_time < now: c.call()

def setTimerCallback(self, callback):
_continue = self._continue
if callback is None:
_continue["_continue"] = False
else:
_continue["_continue"] = True
_interval = self.interval

def call():
if _continue["_continue"]:
callback()
Timer(_interval, call).start()

Timer(_interval, call).start()
8 changes: 5 additions & 3 deletions castervoice/lib/dfplus/state/stackitems.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
@author: dave
'''
from dragonfly import Pause, ActionBase
from dragonfly import Pause, ActionBase, get_engine

from castervoice.lib import settings

Expand Down Expand Up @@ -180,6 +180,7 @@ def __init__(self, continuer, data, type=TYPE):

self.time_in_seconds = continuer.time_in_seconds
self.blocking = continuer.blocking
self.timer = None

def satisfy_level(
self, level_index, is_back, stack_item
Expand Down Expand Up @@ -214,7 +215,8 @@ def execute(self, success): # this method should be what deactivates the contin

def clean(self):
StackItemSeeker.clean(self)
self.nexus.timer.remove_callback(self.closure)
self.timer.stop()
self.timer = None
self.closure = None

def begin(self):
Expand All @@ -236,7 +238,7 @@ def closure():
execute(False)

self.closure = closure
self.nexus.timer.add_callback(self.closure, self.time_in_seconds)
self.timer = get_engine().create_timer(self.closure, self.time_in_seconds)
self.closure()


Expand Down
14 changes: 7 additions & 7 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
dragonfly2>=0.14.0
wxpython>=4.0.3
pillow>=5.3.0
toml>=0.10.0
future
mock
pylint
dragonfly2==0.15.0
wxpython==4.0.4
pillow==5.3.0
toml==0.10.0
future==0.17.1
mock==3.0.5
pylint==1.9.3
10 changes: 5 additions & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
dragonfly2>=0.14.0
wxpython>=4.0.3
pillow>=5.3.0
toml>=0.10.0
future
dragonfly2==0.15.0
wxpython==4.0.4
pillow==5.3.0
toml==0.10.0
future==0.17.1
mock==3.0.5
10 changes: 5 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ def run(self):
"Operating System :: OS Independent"
],
install_requires=[
"dragonfly2>=0.11.1",
"wxpython>=4.0.4",
"pillow>=5.3.0",
"toml>=0.10.0",
"dragonfly2>=0.14.2",
"wxpython",
"pillow",
"toml",
"future",
"mock==3.0.5",
"mock",
],
cmdclass={'install': new_install,
'develop': dev_install,
Expand Down

0 comments on commit 45b2c03

Please sign in to comment.