diff --git a/SConstruct b/SConstruct deleted file mode 100644 index aa3fd85bd440876410572aa052b48720bcc54b6c..0000000000000000000000000000000000000000 --- a/SConstruct +++ /dev/null @@ -1,46 +0,0 @@ -import re -import os -import sys - -import yaml - -from git_util import GitUtil -from path_helpers import path - - -def get_plugin_version(): - version = GitUtil(None).describe() - m = re.search('^v(?P<major>\d+)\.(?P<minor>\d+)(-(?P<micro>\d+))?', version) - if m.group('micro'): - micro = m.group('micro') - else: - micro = '0' - version_string = "%s.%s.%s" % (m.group('major'), - m.group('minor'), micro) - return version_string - - -SOFTWARE_VERSION = get_plugin_version() -env = Environment(tools = ["default", "disttar"], - DISTTAR_EXCLUDEDIRS=['.git'], - DISTTAR_EXCLUDERES=[r'\.sconsign\.dblite'], - DISTTAR_EXCLUDEEXTS=['.gz', '.pyc', '.tgz', '.swp']) - -plugin_root = path('.').abspath() -properties_target = plugin_root.joinpath('properties.yml') -properties = {'plugin_name': 'wheeler.%s' % plugin_root.name, - 'package_name': str(plugin_root.name), - 'version': SOFTWARE_VERSION} -properties_target.write_bytes(yaml.dump(properties)) -archive_name = '%s-%s.tar.gz' % (properties['package_name'], SOFTWARE_VERSION) - -# This will build an archive using what ever DISTTAR_FORMAT that is set. -tar = env.DistTar('%s' % properties['package_name'], [env.Dir('#')]) -renamed_tar = env.Command(env.File(archive_name), None, - Move(archive_name, tar[0])) -Depends(renamed_tar, tar) -Clean(renamed_tar, tar) - -if 'PLUGIN_ARCHIVE_DIR' in os.environ: - target_archive_dir = os.environ['PLUGIN_ARCHIVE_DIR'] - Install(target_archive_dir, renamed_tar) diff --git a/__init__.py b/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9c97ea66c3321fdd97f01b8647508d69931d5b29 100644 --- a/__init__.py +++ b/__init__.py @@ -0,0 +1,172 @@ +""" +Copyright 2011 Ryan Fobel + +This file is part of dmf_control_board. + +dmf_control_board is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +dmf_control_board is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with dmf_control_board. If not, see <http://www.gnu.org/licenses/>. +""" +from collections import OrderedDict +from datetime import datetime + +import gtk +import zmq +from flatland import String, Boolean, Float, Form +from logger import logger +from gui.protocol_grid_controller import ProtocolGridController +from plugin_helpers import (AppDataController, StepOptionsController, + get_plugin_info) +from plugin_manager import (IPlugin, IWaveformGenerator, Plugin, implements, + PluginGlobals, ScheduleRequest, emit_signal, + get_service_instance) +from app_context import get_app +from path_helpers import path + + +PluginGlobals.push_env('microdrop.managed') + +class ZeroMQServicePlugin(Plugin, AppDataController, StepOptionsController): + """ + This class is automatically registered with the PluginManager. + """ + implements(IPlugin) + version = get_plugin_info(path(__file__).parent.parent).version + plugins_name = get_plugin_info(path(__file__).parent.parent).plugin_name + + ''' + AppFields + --------- + + A flatland Form specifying application options for the current plugin. + Note that nested Form objects are not supported. + + Since we subclassed AppDataController, an API is available to access and + modify these attributes. This API also provides some nice features + automatically: + -all fields listed here will be included in the app options dialog + (unless properties=dict(show_in_gui=False) is used) + -the values of these fields will be stored persistently in the microdrop + config file, in a section named after this plugin's name attribute + ''' + AppFields = Form.of( + String.named('service_address').using(default='', optional=True), + ) + + ''' + StepFields + --------- + + A flatland Form specifying the per step options for the current plugin. + Note that nested Form objects are not supported. + + Since we subclassed StepOptionsController, an API is available to access and + modify these attributes. This API also provides some nice features + automatically: + -all fields listed here will be included in the protocol grid view + (unless properties=dict(show_in_gui=False) is used) + -the values of these fields will be stored persistently for each step + ''' + StepFields = Form.of( + Boolean.named('service_enabled').using(default=False, optional=True), + ) + + def __init__(self): + self.name = self.plugins_name + self.context = zmq.Context.instance() + self.socks = OrderedDict() + self.timeout_id = None + self._start_time = None + + def on_plugin_enable(self): + # We need to call AppDataController's on_plugin_enable() to update the + # application options data. + AppDataController.on_plugin_enable(self) + self.context = zmq.Context() + self.reset_socks() + if get_app().protocol: + pgc = get_service_instance(ProtocolGridController, env='microdrop') + pgc.update_grid() + + def close_socks(self): + # Close any currently open sockets. + for name, sock in self.socks.iteritems(): + sock.close() + self.socks = OrderedDict() + + def reset_socks(self): + self.close_socks() + app_values = self.get_app_values() + if self.timeout_id is not None: + gtk.timeout_remove(self.timeout_id) + self.timeout_id = None + if app_values['service_address']: + # Service address is available + self.socks['req'] = zmq.Socket(self.context, zmq.REQ) + self.socks['req'].connect(app_values['service_address']) + + def on_app_options_changed(self, plugin_name): + if plugin_name == self.name: + self.reset_socks() + + def on_plugin_disable(self): + self.close_socks() + if get_app().protocol: + pgc = get_service_instance(ProtocolGridController, env='microdrop') + pgc.update_grid() + + def step_complete(self, return_value=None): + app = get_app() + if app.running or app.realtime_mode: + emit_signal('on_step_complete', [self.name, return_value]) + + def on_step_run(self): + options = self.get_step_options() + self.reset_socks() + if options['service_enabled'] and self.socks['req'] is None: + # Service is supposed to be called for this step, but the socket is + # not ready. + self.step_complete(return_value='Fail') + elif options['service_enabled'] and self.socks['req'] is not None: + logger.info('[ZeroMQServicePlugin] Send signal to service to ' + 'start.') + # Request start of service. + self.socks['req'].send('start') + if not self.socks['req'].poll(timeout=4000): + self.reset_socks() + logger.error('[ZeroMQServicePlugin] Timed-out waiting for ' + 'a response.') + else: + # Response is ready. + response = self.socks['req'].recv() + if response == 'started': + logger.info('[ZeroMQServicePlugin] Service started ' + 'successfully.') + self.socks['req'].send('notify_completion') + + response = self.socks['req'].recv() + logger.info('[ZeroMQServicePlugin] Service response: %s', response) + if response == 'completed': + logger.info('[ZeroMQServicePlugin] Service completed task ' + 'successfully.') + self.step_complete() + else: + logger.error('[ZeroMQServicePlugin] Unexpected response: %s' % + response) + self.step_complete(return_value='Fail') + else: + self.step_complete() + + def enable_service(self): + pass + +PluginGlobals.pop_env() diff --git a/hooks/Linux/on_plugin_install.sh b/hooks/Linux/on_plugin_install.sh new file mode 100644 index 0000000000000000000000000000000000000000..19bf66611814afab0a6c30b04017c7eea3017334 --- /dev/null +++ b/hooks/Linux/on_plugin_install.sh @@ -0,0 +1,3 @@ +#!/bin/sh +PYTHON_EXE=$1 +$PYTHON_EXE ../../on_plugin_install.py diff --git a/hooks/Windows/on_plugin_install.bat b/hooks/Windows/on_plugin_install.bat new file mode 100644 index 0000000000000000000000000000000000000000..617a8286917f64dc54b0627f06cb4bf3d7e8ccf8 --- /dev/null +++ b/hooks/Windows/on_plugin_install.bat @@ -0,0 +1 @@ +%1 ..\..\on_plugin_install.py diff --git a/hooks/Windows/on_plugin_install.exe b/hooks/Windows/on_plugin_install.exe new file mode 100644 index 0000000000000000000000000000000000000000..2e6aa4f40e0ba6fabfae628dd3976df1f76b4eb7 Binary files /dev/null and b/hooks/Windows/on_plugin_install.exe differ diff --git a/microdrop/__init__.py b/microdrop/__init__.py deleted file mode 100644 index 9c97ea66c3321fdd97f01b8647508d69931d5b29..0000000000000000000000000000000000000000 --- a/microdrop/__init__.py +++ /dev/null @@ -1,172 +0,0 @@ -""" -Copyright 2011 Ryan Fobel - -This file is part of dmf_control_board. - -dmf_control_board is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -dmf_control_board is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with dmf_control_board. If not, see <http://www.gnu.org/licenses/>. -""" -from collections import OrderedDict -from datetime import datetime - -import gtk -import zmq -from flatland import String, Boolean, Float, Form -from logger import logger -from gui.protocol_grid_controller import ProtocolGridController -from plugin_helpers import (AppDataController, StepOptionsController, - get_plugin_info) -from plugin_manager import (IPlugin, IWaveformGenerator, Plugin, implements, - PluginGlobals, ScheduleRequest, emit_signal, - get_service_instance) -from app_context import get_app -from path_helpers import path - - -PluginGlobals.push_env('microdrop.managed') - -class ZeroMQServicePlugin(Plugin, AppDataController, StepOptionsController): - """ - This class is automatically registered with the PluginManager. - """ - implements(IPlugin) - version = get_plugin_info(path(__file__).parent.parent).version - plugins_name = get_plugin_info(path(__file__).parent.parent).plugin_name - - ''' - AppFields - --------- - - A flatland Form specifying application options for the current plugin. - Note that nested Form objects are not supported. - - Since we subclassed AppDataController, an API is available to access and - modify these attributes. This API also provides some nice features - automatically: - -all fields listed here will be included in the app options dialog - (unless properties=dict(show_in_gui=False) is used) - -the values of these fields will be stored persistently in the microdrop - config file, in a section named after this plugin's name attribute - ''' - AppFields = Form.of( - String.named('service_address').using(default='', optional=True), - ) - - ''' - StepFields - --------- - - A flatland Form specifying the per step options for the current plugin. - Note that nested Form objects are not supported. - - Since we subclassed StepOptionsController, an API is available to access and - modify these attributes. This API also provides some nice features - automatically: - -all fields listed here will be included in the protocol grid view - (unless properties=dict(show_in_gui=False) is used) - -the values of these fields will be stored persistently for each step - ''' - StepFields = Form.of( - Boolean.named('service_enabled').using(default=False, optional=True), - ) - - def __init__(self): - self.name = self.plugins_name - self.context = zmq.Context.instance() - self.socks = OrderedDict() - self.timeout_id = None - self._start_time = None - - def on_plugin_enable(self): - # We need to call AppDataController's on_plugin_enable() to update the - # application options data. - AppDataController.on_plugin_enable(self) - self.context = zmq.Context() - self.reset_socks() - if get_app().protocol: - pgc = get_service_instance(ProtocolGridController, env='microdrop') - pgc.update_grid() - - def close_socks(self): - # Close any currently open sockets. - for name, sock in self.socks.iteritems(): - sock.close() - self.socks = OrderedDict() - - def reset_socks(self): - self.close_socks() - app_values = self.get_app_values() - if self.timeout_id is not None: - gtk.timeout_remove(self.timeout_id) - self.timeout_id = None - if app_values['service_address']: - # Service address is available - self.socks['req'] = zmq.Socket(self.context, zmq.REQ) - self.socks['req'].connect(app_values['service_address']) - - def on_app_options_changed(self, plugin_name): - if plugin_name == self.name: - self.reset_socks() - - def on_plugin_disable(self): - self.close_socks() - if get_app().protocol: - pgc = get_service_instance(ProtocolGridController, env='microdrop') - pgc.update_grid() - - def step_complete(self, return_value=None): - app = get_app() - if app.running or app.realtime_mode: - emit_signal('on_step_complete', [self.name, return_value]) - - def on_step_run(self): - options = self.get_step_options() - self.reset_socks() - if options['service_enabled'] and self.socks['req'] is None: - # Service is supposed to be called for this step, but the socket is - # not ready. - self.step_complete(return_value='Fail') - elif options['service_enabled'] and self.socks['req'] is not None: - logger.info('[ZeroMQServicePlugin] Send signal to service to ' - 'start.') - # Request start of service. - self.socks['req'].send('start') - if not self.socks['req'].poll(timeout=4000): - self.reset_socks() - logger.error('[ZeroMQServicePlugin] Timed-out waiting for ' - 'a response.') - else: - # Response is ready. - response = self.socks['req'].recv() - if response == 'started': - logger.info('[ZeroMQServicePlugin] Service started ' - 'successfully.') - self.socks['req'].send('notify_completion') - - response = self.socks['req'].recv() - logger.info('[ZeroMQServicePlugin] Service response: %s', response) - if response == 'completed': - logger.info('[ZeroMQServicePlugin] Service completed task ' - 'successfully.') - self.step_complete() - else: - logger.error('[ZeroMQServicePlugin] Unexpected response: %s' % - response) - self.step_complete(return_value='Fail') - else: - self.step_complete() - - def enable_service(self): - pass - -PluginGlobals.pop_env() diff --git a/on_plugin_install.py b/on_plugin_install.py new file mode 100644 index 0000000000000000000000000000000000000000..80fb487a1cf874faf5aa0c194605fa0419322302 --- /dev/null +++ b/on_plugin_install.py @@ -0,0 +1,12 @@ +from datetime import datetime +import logging + +from path_helpers import path +from pip_helpers import install + + +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + logging.info(str(datetime.now())) + requirements = path(__file__).parent.joinpath('requirements.txt').abspath() + logging.info(install(['-U', '-r', requirements])) diff --git a/release.py b/release.py new file mode 100755 index 0000000000000000000000000000000000000000..47788c40b3d5ed0849655804a587517bda9c5c45 --- /dev/null +++ b/release.py @@ -0,0 +1,22 @@ +import tarfile +import yaml + +from microdrop_utility import Version + +package_name = 'dstat_zeromq_plugin' +plugin_name = 'wheeler.dstat_zeromq_plugin' + +# create a version sting based on the git revision/branch +version = str(Version.from_git_repository()) + +# write the 'properties.yml' file +properties = {'plugin_name': plugin_name, 'package_name': package_name, + 'version': version} +with open('properties.yml', 'w') as f: + f.write(yaml.dump(properties)) + +# create the tar.gz plugin archive +with tarfile.open("%s-%s.tar.gz" % (package_name, version), "w:gz") as tar: + for name in ['__init__.py', 'test_service.py', 'properties.yml', + 'requirements.txt']: + tar.add(name) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..02ec117e150a927f354885c99756c08c2618bf0c --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pyzmq diff --git a/site_scons/__init__.py b/site_scons/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/site_scons/git_util.py b/site_scons/git_util.py deleted file mode 100644 index 6fecf25e9a83201373ec037430c0a66ece9c5d02..0000000000000000000000000000000000000000 --- a/site_scons/git_util.py +++ /dev/null @@ -1,93 +0,0 @@ -import os -from subprocess import Popen, PIPE, check_call, CalledProcessError -import re - -from path_helpers import path - - -class GitError(Exception): - pass - - -class GitUtil(object): - def __init__(self, root_path='.'): - self.root_path = path(root_path) - if root_path is None: - dir_node = path(os.getcwd()) - while not dir_node.dirs('.git') and dir_node: - dir_node = dir_node.parent - if not dir_node: - raise GitError('No git root found.') - self.root_path = dir_node - self._git = None - assert(self.root_path.dirs('.git')) - - - @property - def git(self): - if self._git: - return self._git - - cmds = ['git'] - if os.name == 'nt': - exceptions = (WindowsError,) - cmds += ['git.cmd'] - else: - exceptions = (OSError,) - - valid_cmd = False - for cmd in cmds: - try: - check_call([cmd], stdout=PIPE, stderr=PIPE) - except exceptions: - # The command was not found, try the next one - pass - except CalledProcessError: - valid_cmd = True - break - if not valid_cmd: - raise GitError, 'No valid git command found' - self._git = cmd - return self._git - - - def command(self, x): - try: - x.__iter__ - except: - x = re.split(r'\s+', x) - cwd = os.getcwd() - - os.chdir(self.root_path) - cmd = [self.git] + x - stdout, stderr = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE).communicate() - os.chdir(cwd) - - if stderr: - raise GitError('Error executing git %s' % x) - return stdout.strip() - - - def describe(self): - return self.command('describe') - - - def summary(self, color=False): - if color: - format_ = '''--pretty=format:%Cred%h%Creset - %s %Cgreen(%cr)%Creset''' - else: - format_ = '''--pretty=format:%h - %s (%cr)''' - return self.command(['''log''', '''--graph''', format_, - '''--abbrev-commit''', '''--date=relative''']) - - - def rev_parse(self, ref='HEAD'): - return self.command(['rev-parse', ref]) - - - def show(self, ref='HEAD', color=False, extra_args=None): - extra_args = [extra_args, []][extra_args is None] - args = ['show', ref] - if color: - args += ['--color'] - return self.command(args + extra_args) diff --git a/site_scons/site_tools/disttar/__init__.py b/site_scons/site_tools/disttar/__init__.py deleted file mode 100644 index 4b0e0f07f936e7f9173a89a6a9a738505676a481..0000000000000000000000000000000000000000 --- a/site_scons/site_tools/disttar/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from disttar import * diff --git a/site_scons/site_tools/disttar/disttar.py b/site_scons/site_tools/disttar/disttar.py deleted file mode 100644 index 7f7a13e2fe67c925b275a0dad30d86196149b774..0000000000000000000000000000000000000000 --- a/site_scons/site_tools/disttar/disttar.py +++ /dev/null @@ -1,153 +0,0 @@ -# DistTarBuilder: tool to generate tar files using SCons -# Copyright (C) 2005, 2006 Matthew A. Nicholson -# Copyright (C) 2006-2010 John Pye -# -# This file is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License version 2.1 as published by the Free Software Foundation. -# -# This file is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# - -import os,sys -from SCons.Script import * -import re - - -def file_handler(fpath, source, excluderes, excludeexts): - from path import path - - fpath = path(fpath) - if not fpath.ext in excludeexts: - failre = False - for r in excluderes: - #print "Match( %s against %s)" % (r,relpath) - if r.search(fpath): - failre = True - #print "Excluding '%s' from tarball" % fpath - break - if not failre: - #print "Adding source",fpath - source.append(str(fpath)) - - -def disttar_emitter(target, source, env): - source,origsource = [], source - - excludeexts = env.Dictionary().get('DISTTAR_EXCLUDEEXTS',[]) - excludedirs = env.Dictionary().get('DISTTAR_EXCLUDEDIRS',[]) - re1 = env.Dictionary().get('DISTTAR_EXCLUDERES',[]) - excluderes = [re.compile(r) for r in re1] - - # assume the sources are directories... need to check that - for item in origsource: - if os.path.isfile(str(item)): - file_handler(item, source, excluderes, excludeexts) - else: - for root, dirs, files in os.walk(str(item)): - # don't make directory dependences as that triggers full build - # of that directory - if root in source: - #print "Removing directory %s" % root - source.remove(root) - - # loop through files in a directory - for name in files: - relpath = os.path.join(root,name) - file_handler(relpath, source, excluderes, excludeexts) - - for d in excludedirs: - if d in dirs: - dirs.remove(d) # don't visit CVS directories etc - - return target, source - -def disttar_string(target, source, env): - """This is what gets printed on the console. We'll strip out the list - or source files, since it tends to get very long. If you want to see the - contents, the easiest way is to uncomment the line 'Adding to TAR file' - below. """ - return 'DistTar(%s,...)' % target[0] - -def disttar(target, source, env): - """tar archive builder""" - - import tarfile - - env_dict = env.Dictionary() - - if env_dict.get("DISTTAR_FORMAT") in ["gz", "bz2"]: - tar_format = env_dict["DISTTAR_FORMAT"] - else: - tar_format = "" - - # split the target directory, filename, and stuffix - base_name = str(target[0]).split('.tar')[0] - (target_dir, dir_name) = os.path.split(base_name) - - # create the target directory if it does not exist - if target_dir and not os.path.exists(target_dir): - os.makedirs(target_dir) - - # open our tar file for writing - print >> sys.stderr, 'DistTar: Writing %s' % str(target[0]) - print >> sys.stderr, ' with contents: %s' % [str(s) for s in source] - tar = tarfile.open(str(target[0]), "w:%s" % tar_format) - - # write sources to our tar file - for item in source: - item = str(item) - sys.stderr.write(".") - #print "Adding to TAR file: %s/%s" % (dir_name,item) - tar.add(item,'%s/%s' % (dir_name,item)) - - # all done - sys.stderr.write("\n") #print "Closing TAR file" - tar.close() - -def disttar_suffix(env, sources): - """tar archive suffix generator""" - - env_dict = env.Dictionary() - if env_dict.has_key("DISTTAR_FORMAT") and env_dict["DISTTAR_FORMAT"] in ["gz", "bz2"]: - return ".tar." + env_dict["DISTTAR_FORMAT"] - else: - return ".tar" - -def generate(env): - """ - Add builders and construction variables for the DistTar builder. - """ - - disttar_action=SCons.Action.Action(disttar, disttar_string) - env['BUILDERS']['DistTar'] = Builder( - action=disttar_action - , emitter=disttar_emitter - , suffix = disttar_suffix - , target_factory = env.fs.Entry - ) - - env.AppendUnique( - DISTTAR_FORMAT = 'gz' - ) - -def exists(env): - """ - Make sure this tool exists. - """ - try: - import os - import tarfile - except ImportError: - return False - else: - return True - -# vim:set ts=4 sw=4 noexpandtab: