From 5dd09fcf97ada61fca215da0a77292b2af2e7e76 Mon Sep 17 00:00:00 2001 From: francozappa Date: Wed, 26 Jul 2017 17:41:51 +0800 Subject: [PATCH 01/33] Add basic files --- bin/mcps | 5 +++++ minicps/ui/__init__.py | 0 minicps/ui/commands.py | 16 ++++++++++++++++ requirements-dev.txt | 1 + requirements.txt | 1 + setup.py | 1 + 6 files changed, 24 insertions(+) create mode 100644 bin/mcps create mode 100644 minicps/ui/__init__.py create mode 100644 minicps/ui/commands.py diff --git a/bin/mcps b/bin/mcps new file mode 100644 index 0000000..89d57ae --- /dev/null +++ b/bin/mcps @@ -0,0 +1,5 @@ +#!/usr/bin/python2 + +import click + +# vim: ft=python diff --git a/minicps/ui/__init__.py b/minicps/ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/minicps/ui/commands.py b/minicps/ui/commands.py new file mode 100644 index 0000000..a5bff96 --- /dev/null +++ b/minicps/ui/commands.py @@ -0,0 +1,16 @@ +""" +This file contains the commands that can be used (with parameters) both by cli +and gui packages. For example the mcps click cli is using them as subcommands. + +This it the list of supported commands: + + - init +""" + +class Init(object): + + """Docstring for Init. """ + + def __init__(self): + """TODO: to be defined. """ + pass diff --git a/requirements-dev.txt b/requirements-dev.txt index 1788721..1a39632 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,4 +6,5 @@ cryptography pyasn1 pymodbus cpppo +click diff --git a/requirements.txt b/requirements.txt index d7aa635..57d4729 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ cryptography pyasn1 pymodbus cpppo +click diff --git a/setup.py b/setup.py index 3d14dd7..143dee0 100755 --- a/setup.py +++ b/setup.py @@ -39,6 +39,7 @@ # packages=find_packages(exclude=['docs', 'tests*', 'examples', 'temp', # 'bin']), # NOTE: for the uses, see requirements for the developer + scripts = ['bin/mcps'], install_requires=[ 'cryptography', 'pyasn1', From 30aa917edcbb0c626615d84cb264162b9906ae3a Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Wed, 2 Aug 2017 19:09:32 +0800 Subject: [PATCH 02/33] added cli command group init --- bin/mcps | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/bin/mcps b/bin/mcps index 89d57ae..428d129 100644 --- a/bin/mcps +++ b/bin/mcps @@ -2,4 +2,19 @@ import click +import os +import minicps.ui.commands as mcps + # vim: ft=python + +@click.group() +def main(): + pass + +@main.command() +def init(): + init = mcps.Init(os.getcwd()) + init.default() + +if __name__=="__main__": + main() From aacf164f786396c35af3c50558f544262af82d62 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Wed, 2 Aug 2017 19:09:57 +0800 Subject: [PATCH 03/33] added Init class for generating scaffold --- minicps/ui/commands.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/minicps/ui/commands.py b/minicps/ui/commands.py index a5bff96..c3e4907 100644 --- a/minicps/ui/commands.py +++ b/minicps/ui/commands.py @@ -2,15 +2,36 @@ This file contains the commands that can be used (with parameters) both by cli and gui packages. For example the mcps click cli is using them as subcommands. -This it the list of supported commands: +This is the list of supported commands: - init """ +import os + +from template import Template class Init(object): """Docstring for Init. """ - def __init__(self): - """TODO: to be defined. """ - pass + def __init__(self, cwd): + self._cwd = cwd + self._template = Template + + def default(self): + path = '{}/scaffold'.format(self._cwd) + os.makedirs(path) + + for x in range(1,3): + with open('{}/plc{}.py'.format(path, x), 'w') as f: + name = self._template.device('PLC{}'.format(x)) + f.write(name) + + with open('{}/topo.py'.format(path), 'w') as f: + f.write(self._template.topology()) + + with open('{}/run.py'.format(path), 'w') as f: + f.write(self._template.run()) + + with open('{}/state.py'.format(path), 'w') as f: + f.write(self._template.state()) From 49c714834776a3bf503911ad575591f165654c7e Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Wed, 2 Aug 2017 19:10:31 +0800 Subject: [PATCH 04/33] template file for generating scaffold --- minicps/ui/template.py | 94 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 minicps/ui/template.py diff --git a/minicps/ui/template.py b/minicps/ui/template.py new file mode 100644 index 0000000..f9f2dfe --- /dev/null +++ b/minicps/ui/template.py @@ -0,0 +1,94 @@ +class Base(object): + + @classmethod + def device(cls, class_name, device=None): + pass + + @classmethod + def topology(cls): + pass + + @classmethod + def run(cls): + pass + + @classmethod + def state(cls): + pass + +class Template(Base): + + @classmethod + def device(cls, class_name, device="PLC"): + return"""#{device} Template +from minicps.devices import {device} + +class {class_name}({device}): + + def pre_loop(self, sleep=0.0): + pass + + def main_loop(self, sleep=0.5): + pass""".format(class_name=class_name, device=device) + + @classmethod + def topology(cls): + return"""#Topology Template + +NETMASK = + +from mininet.topo import Topo + +class ExampleTopo(Topo): + + def build(self): + pass""" + + @classmethod + def run(cls): + return """# Run Script Template +from mininet.net import Mininet +from mininet.cli import CLI +from minicps.mcps import MiniCPS +from topo import ExampleTopo + +class ExampleCPS(MiniCPS): + + '''Main container used to run the simulation.''' + + def __init__(self, name, net): + pass""" + + @classmethod + def state(cls): + return """# state + +from minicps.states import SQLiteState + +def example_state(): + PATH = 'example_db.sqlite' + NAME = 'example_table' + + STATE = { + 'name': NAME, + 'path': PATH + } + + SCHEMA = ''' + CREATE TABLE example_table ( + name TEXT NOT NULL, + datatype TEXT NOT NULL, + value TEXT, + pid INTEGER NOT NULL, + PRIMARY KEY (name, pid) + ); + ''' + + SCHEMA_INIT = ''' + INSERT INTO example_table VALUES ('test', 'int', '0', 1); + ''' + return STATE, SCHEMA, SCHEMA_INIT + +def init_db(path, schema, schema_init): + SQLiteState._create(path, schema) + SQLiteState._init(path, schema_init)""" From 544613f8ce7283e90e84c74a6edc15efa34f499a Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Thu, 3 Aug 2017 14:48:01 +0800 Subject: [PATCH 05/33] common interface for creating scaffold in binary --- bin/mcps | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/mcps b/bin/mcps index 428d129..40f09da 100644 --- a/bin/mcps +++ b/bin/mcps @@ -12,9 +12,10 @@ def main(): pass @main.command() -def init(): +@click.option('--config', default="default", help="configuration file for generating template.") +def init(config): init = mcps.Init(os.getcwd()) - init.default() + init.make(config) if __name__=="__main__": main() From 9aa859cd0d166193aac8bc3441ed88f35d265a68 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Thu, 3 Aug 2017 14:48:53 +0800 Subject: [PATCH 06/33] template factory generates appropriate template; device class provides template for appropriate device --- minicps/ui/commands.py | 22 +++++--- minicps/ui/template.py | 111 ++++++++++++++++++++++++++++++----------- 2 files changed, 97 insertions(+), 36 deletions(-) diff --git a/minicps/ui/commands.py b/minicps/ui/commands.py index c3e4907..638bce7 100644 --- a/minicps/ui/commands.py +++ b/minicps/ui/commands.py @@ -7,8 +7,7 @@ - init """ import os - -from template import Template +from template import TemplateFactory class Init(object): @@ -16,16 +15,23 @@ class Init(object): def __init__(self, cwd): self._cwd = cwd - self._template = Template + self._template = None + + def make(self, _type="default", path=None): + if _type == "default": + self._template = TemplateFactory.getTemplate(_type) + self._default() + else: + raise NotImplementedError() - def default(self): + def _default(self): path = '{}/scaffold'.format(self._cwd) os.makedirs(path) - for x in range(1,3): - with open('{}/plc{}.py'.format(path, x), 'w') as f: - name = self._template.device('PLC{}'.format(x)) - f.write(name) + devices = self._template.devices(number=2) + for idx in range(len(devices)): + with open('{}/plc{}.py'.format(path, (idx+1)), 'w') as f: + f.write(devices[idx]) with open('{}/topo.py'.format(path), 'w') as f: f.write(self._template.topology()) diff --git a/minicps/ui/template.py b/minicps/ui/template.py index f9f2dfe..d7d39d5 100644 --- a/minicps/ui/template.py +++ b/minicps/ui/template.py @@ -1,51 +1,97 @@ -class Base(object): +class BaseTemplate(object): - @classmethod - def device(cls, class_name, device=None): + @staticmethod + def devices(number): pass - @classmethod - def topology(cls): + @staticmethod + def topology(): pass - @classmethod - def run(cls): + @staticmethod + def run(): pass - @classmethod - def state(cls): + @staticmethod + def state(): pass -class Template(Base): +class Device(object): - @classmethod - def device(cls, class_name, device="PLC"): - return"""#{device} Template -from minicps.devices import {device} + @staticmethod + def plc(suffix): + return """# PLC Template +from minicps.devices import PLC -class {class_name}({device}): +PLC_DATA = {{ + 'SENSOR1': '0', + 'SENSOR2': '0.0', + 'SENSOR3': '0', # interlock with PLC2 + 'ACTUATOR1': '1', # 0 means OFF and 1 means ON + 'ACTUATOR2': '0', +}} + +PLC_TAGS = ( + ('SENSOR1', {suffix}, 'INT'), + ('SENSOR2', {suffix}, 'REAL'), + ('SENSOR3', {suffix}, 'INT'), # interlock with PLC2 + ('ACTUATOR1', {suffix}, 'INT'), # 0 means OFF and 1 means ON + ('ACTUATOR2', {suffix}, 'INT')) + +PLC_ADDR = '10.0.0.{suffix}' + +PLC_SERVER = {{ + 'address': PLC_ADDR, + 'tags': PLC_TAGS +}} + +PLC_PROTOCOL = {{ + 'name': 'enip', + 'mode': 1, + 'server': PLC_SERVER +}} + +class PLC{suffix}(PLC): def pre_loop(self, sleep=0.0): pass - def main_loop(self, sleep=0.5): - pass""".format(class_name=class_name, device=device) + def main_loop(self, sleep=0.0): + pass""".format(suffix=suffix) - @classmethod - def topology(cls): - return"""#Topology Template +class TemplateFactory(object): + + @staticmethod + def getTemplate(_type): + if _type == "default": + return DefaultTemplate + else: + pass -NETMASK = +class DefaultTemplate(BaseTemplate): + + @staticmethod + def devices(number=1): + return [Device.plc(suffix) for suffix in range(1, number+1)] + + @staticmethod + def topology(): + return"""#Topology Template from mininet.topo import Topo +NETMASK = '/24' + +PLC1_MAC = '00:00:00:00:00:01' +PLC2_MAC = '00:00:00:00:00:02' + class ExampleTopo(Topo): def build(self): pass""" - @classmethod - def run(cls): + @staticmethod + def run(): return """# Run Script Template from mininet.net import Mininet from mininet.cli import CLI @@ -59,10 +105,9 @@ class ExampleCPS(MiniCPS): def __init__(self, name, net): pass""" - @classmethod - def state(cls): + @staticmethod + def state(): return """# state - from minicps.states import SQLiteState def example_state(): @@ -85,10 +130,20 @@ def example_state(): ''' SCHEMA_INIT = ''' - INSERT INTO example_table VALUES ('test', 'int', '0', 1); + INSERT INTO example_table VALUES ('SENSOR1', 'int', '0', 1); + INSERT INTO example_table VALUES ('SENSOR2', 'float', '0.0', 1); + INSERT INTO example_table VALUES ('SENSOR3', 'int', '1', 1); + INSERT INTO example_table VALUES ('ACTUATOR1', 'int', '1', 1); + INSERT INTO example_table VALUES ('ACTUATOR2', 'int', '0', 1); + INSERT INTO example_table VALUES ('SENSOR3', 'int', '2', 2); ''' return STATE, SCHEMA, SCHEMA_INIT def init_db(path, schema, schema_init): SQLiteState._create(path, schema) - SQLiteState._init(path, schema_init)""" + SQLiteState._init(path, schema_init) + +if __name__=="__main__": + state, schema, init = example_state() + init_db(state['path'], schema, init) +""" From 60cef08869179a51a0504405982e9415d4268ee3 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Thu, 3 Aug 2017 16:21:08 +0800 Subject: [PATCH 07/33] added exception to provide user feedback --- bin/mcps | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/bin/mcps b/bin/mcps index 40f09da..7eb3ec9 100644 --- a/bin/mcps +++ b/bin/mcps @@ -3,6 +3,9 @@ import click import os +import errno +import traceback + import minicps.ui.commands as mcps # vim: ft=python @@ -13,9 +16,22 @@ def main(): @main.command() @click.option('--config', default="default", help="configuration file for generating template.") +@click.option('--path', help="directory path") def init(config): init = mcps.Init(os.getcwd()) - init.make(config) + path = "scaffold" + try: + init.make(path=path) + click.echo(click.style("\n... Success: directory generated.", fg="green", bold=True)) + except OSError as e: + if e.errno == errno.EEXIST: + click.echo(click.style("Warning: directory ('{}') already exists.".format(path), fg="yellow")) + else: + click.echo(click.style(traceback.format_exc(), fg="red")) + click.echo("... init aborted.".format(path)) + except Exception as e: + click.echo(click.style(traceback.format_exc(), fg="red")) + click.echo("... init aborted.".format(path)) if __name__=="__main__": main() From e1c4ba1eee8df7c16c6ec9dfdd28f87337f53ae3 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Thu, 3 Aug 2017 16:21:40 +0800 Subject: [PATCH 08/33] DRY'd code --- minicps/ui/commands.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/minicps/ui/commands.py b/minicps/ui/commands.py index 638bce7..a63b9a4 100644 --- a/minicps/ui/commands.py +++ b/minicps/ui/commands.py @@ -20,24 +20,31 @@ def __init__(self, cwd): def make(self, _type="default", path=None): if _type == "default": self._template = TemplateFactory.getTemplate(_type) - self._default() + self._default(path) else: - raise NotImplementedError() + raise NotImplementedError("Only default is supported.") - def _default(self): - path = '{}/scaffold'.format(self._cwd) - os.makedirs(path) + def _default(self, path): + dir_path = '{}/{}'.format(self._cwd, path) + os.makedirs(dir_path) devices = self._template.devices(number=2) for idx in range(len(devices)): - with open('{}/plc{}.py'.format(path, (idx+1)), 'w') as f: - f.write(devices[idx]) + self._create('{}/plc{}.py'.format(path, (idx+1)), devices[idx]) + self._display("{path}/plc{idx}.py".format(folder=folder, idx=idx+1)) - with open('{}/topo.py'.format(path), 'w') as f: - f.write(self._template.topology()) + self._create('{}/run.py'.format(path), self._template.run()) + self._create('{}/state.py'.format(path), self._template.state()) + self._create('{}/topo.py'.format(path), self._template.topology()) - with open('{}/run.py'.format(path), 'w') as f: - f.write(self._template.run()) + self._display("{folder}/state.py".format(folder=folder)) + self._display("{folder}/run.py".format(folder=folder)) + self._display("{folder}/topo.py".format(folder=folder)) + + def _create(self, path, message): + with open(path, 'w') as f: + f.write(message) + + def _display(self, message): + print "{:<5} create {}".format("", message) - with open('{}/state.py'.format(path), 'w') as f: - f.write(self._template.state()) From ac8d18d903b3d472cd2a948d3727c5ee32f3e7de Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Thu, 3 Aug 2017 17:07:03 +0800 Subject: [PATCH 09/33] added custom path cli optional arg --- bin/mcps | 9 +++++---- minicps/ui/commands.py | 18 ++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/bin/mcps b/bin/mcps index 7eb3ec9..182131d 100644 --- a/bin/mcps +++ b/bin/mcps @@ -2,7 +2,6 @@ import click -import os import errno import traceback @@ -17,9 +16,11 @@ def main(): @main.command() @click.option('--config', default="default", help="configuration file for generating template.") @click.option('--path', help="directory path") -def init(config): - init = mcps.Init(os.getcwd()) - path = "scaffold" +def init(config, path): + init = mcps.Init() + + path = path if (path is not None) and (path[0] == "/") else "./scaffold" + try: init.make(path=path) click.echo(click.style("\n... Success: directory generated.", fg="green", bold=True)) diff --git a/minicps/ui/commands.py b/minicps/ui/commands.py index a63b9a4..9b467ff 100644 --- a/minicps/ui/commands.py +++ b/minicps/ui/commands.py @@ -13,11 +13,12 @@ class Init(object): """Docstring for Init. """ - def __init__(self, cwd): - self._cwd = cwd + def __init__(self): self._template = None - def make(self, _type="default", path=None): + def make(self, path, _type="default"): + os.makedirs(path) + if _type == "default": self._template = TemplateFactory.getTemplate(_type) self._default(path) @@ -25,21 +26,18 @@ def make(self, _type="default", path=None): raise NotImplementedError("Only default is supported.") def _default(self, path): - dir_path = '{}/{}'.format(self._cwd, path) - os.makedirs(dir_path) - devices = self._template.devices(number=2) for idx in range(len(devices)): self._create('{}/plc{}.py'.format(path, (idx+1)), devices[idx]) - self._display("{path}/plc{idx}.py".format(folder=folder, idx=idx+1)) + self._display("{folder}/plc{idx}.py".format(folder=path, idx=idx+1)) self._create('{}/run.py'.format(path), self._template.run()) self._create('{}/state.py'.format(path), self._template.state()) self._create('{}/topo.py'.format(path), self._template.topology()) - self._display("{folder}/state.py".format(folder=folder)) - self._display("{folder}/run.py".format(folder=folder)) - self._display("{folder}/topo.py".format(folder=folder)) + self._display("{folder}/state.py".format(folder=path)) + self._display("{folder}/run.py".format(folder=path)) + self._display("{folder}/topo.py".format(folder=path)) def _create(self, path, message): with open(path, 'w') as f: From 6f7e078d7ec0b45c1518ae71e3ad6e426dc3ec50 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Mon, 7 Aug 2017 15:01:40 +0800 Subject: [PATCH 10/33] added test point for makefile --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 8f728dc..9bf2a60 100644 --- a/Makefile +++ b/Makefile @@ -111,6 +111,11 @@ test-devices: test-device: $(TESTER) $(TESTER_OPTS) tests/devices_tests.py:TestDevice + + +test-ui: + $(TESTER) $(TESTER_OPTS) tests/ui_tests.py + # }}} # }}} From 7d479972d84e2edb71000ee017b2e4c3b37904bd Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Mon, 7 Aug 2017 15:06:05 +0800 Subject: [PATCH 11/33] checks permission for creating folders; type raises NotImplementedError --- bin/mcps | 10 ++++------ minicps/ui/commands.py | 7 +++---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/bin/mcps b/bin/mcps index 182131d..7f7ddba 100644 --- a/bin/mcps +++ b/bin/mcps @@ -18,21 +18,19 @@ def main(): @click.option('--path', help="directory path") def init(config, path): init = mcps.Init() - - path = path if (path is not None) and (path[0] == "/") else "./scaffold" + path = path if (path is not None) else "./scaffold" try: init.make(path=path) click.echo(click.style("\n... Success: directory generated.", fg="green", bold=True)) except OSError as e: if e.errno == errno.EEXIST: - click.echo(click.style("Warning: directory ('{}') already exists.".format(path), fg="yellow")) + click.echo(click.style("Warning: Directory ('{}') already exists.".format(path), fg="yellow")) + elif e.errno == errno.EACCES: + click.echo(click.style("Error: Permission Denied. Cannot write to {path}.".format(path), fg="red")) else: click.echo(click.style(traceback.format_exc(), fg="red")) click.echo("... init aborted.".format(path)) - except Exception as e: - click.echo(click.style(traceback.format_exc(), fg="red")) - click.echo("... init aborted.".format(path)) if __name__=="__main__": main() diff --git a/minicps/ui/commands.py b/minicps/ui/commands.py index 9b467ff..37da605 100644 --- a/minicps/ui/commands.py +++ b/minicps/ui/commands.py @@ -17,13 +17,13 @@ def __init__(self): self._template = None def make(self, path, _type="default"): - os.makedirs(path) + os.mkdir(path) + self._template = TemplateFactory.get_template(_type) if _type == "default": - self._template = TemplateFactory.getTemplate(_type) self._default(path) else: - raise NotImplementedError("Only default is supported.") + pass def _default(self, path): devices = self._template.devices(number=2) @@ -45,4 +45,3 @@ def _create(self, path, message): def _display(self, message): print "{:<5} create {}".format("", message) - From b5fff7bf989dc800decf7371604c517a0a85ca95 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Mon, 7 Aug 2017 15:07:54 +0800 Subject: [PATCH 12/33] devices template method filters type based on argument --- minicps/ui/template.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/minicps/ui/template.py b/minicps/ui/template.py index d7d39d5..73d80ea 100644 --- a/minicps/ui/template.py +++ b/minicps/ui/template.py @@ -1,7 +1,7 @@ class BaseTemplate(object): @staticmethod - def devices(number): + def devices(number, _type): pass @staticmethod @@ -62,21 +62,21 @@ def main_loop(self, sleep=0.0): class TemplateFactory(object): @staticmethod - def getTemplate(_type): + def get_template(_type): if _type == "default": return DefaultTemplate else: - pass + raise NotImplementedError() class DefaultTemplate(BaseTemplate): @staticmethod - def devices(number=1): - return [Device.plc(suffix) for suffix in range(1, number+1)] + def devices(number=1, _type="PLC"): + return [Device.plc(suffix) for suffix in range(1, number+1)] # always PLC @staticmethod def topology(): - return"""#Topology Template + return """#Topology Template from mininet.topo import Topo From 05cc1b6740428d25eabcd2112ec1249a340a9e18 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Mon, 7 Aug 2017 15:08:06 +0800 Subject: [PATCH 13/33] ui test cases --- tests/ui_tests.py | 95 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 tests/ui_tests.py diff --git a/tests/ui_tests.py b/tests/ui_tests.py new file mode 100644 index 0000000..5faad38 --- /dev/null +++ b/tests/ui_tests.py @@ -0,0 +1,95 @@ +import os +import shutil + +from minicps.ui.commands import Init +from minicps.ui.template import TemplateFactory, DefaultTemplate, Device + +from nose.tools import eq_ +from nose.plugins.skip import SkipTest + +class TestInit(): + + def test_initialization(self): + init = Init() + eq_(init._template, None) + + def test_make_creates_directory_with_default_files(self): + init = Init() + init.make("../scaffold") + + eq_(init._template, DefaultTemplate) + assert os.path.isdir("../scaffold".format(os.getcwd())) + + shutil.rmtree('../scaffold') + + def test_make_raises_NotImplementedError_when_type_is_not_default(self): + init = Init() + try: + init.make("./scaffold", _type="[placholder]") + assert False + except NotImplementedError as e: + assert True + shutil.rmtree('./scaffold') + + def test__default_creates_default_files(self): + os.mkdir('./default') + + init = Init() + init._template = TemplateFactory.get_template('default') + init._default('./default') + + assert os.path.exists("./default/plc1.py") + assert os.path.exists("./default/plc2.py") + assert os.path.exists("./default/run.py") + assert os.path.exists("./default/topo.py") + assert os.path.exists("./default/state.py") + + shutil.rmtree('./default') + + def test__create_makes_a_new_file(self): + init = Init() + init._create('test.py', "test") + + assert os.path.exists('./test.py') + + os.remove('./test.py') + +class TestTemplateFactory(): + + def test_factory_template_generator_for_default_path(self): + eq_(TemplateFactory.get_template("default"), DefaultTemplate) + + def test_factory_template_generator_raises_error_for_other_path(self): + try: + TemplateFactory.get_template("[placeholder]") + assert False + except NotImplementedError as e: + assert True + +class TestDefaultTemplate(): + + def test_default_devices_with_argument_number_greater_than_3(self): + test = DefaultTemplate.devices(number=3) + eq_(len(test), 3) + + def test_default_devices_with_default_argument(self): + test = DefaultTemplate.devices() + eq_(len(test), 1) + + def test_default_topology(self): + res = "#Topology Template\n\nfrom mininet.topo import Topo\n\nNETMASK = '/24'\n\nPLC1_MAC = '00:00:00:00:00:01'\nPLC2_MAC = '00:00:00:00:00:02'\n\nclass ExampleTopo(Topo):\n\n def build(self):\n pass" + eq_(DefaultTemplate.topology(), res) + + def test_default_run(self): + res = "# Run Script Template\nfrom mininet.net import Mininet\nfrom mininet.cli import CLI\nfrom minicps.mcps import MiniCPS\nfrom topo import ExampleTopo\n\nclass ExampleCPS(MiniCPS):\n\n '''Main container used to run the simulation.'''\n\n def __init__(self, name, net):\n pass" + eq_(DefaultTemplate.run(), res) + + def test_default_state(self): + res = '# state\nfrom minicps.states import SQLiteState\n\ndef example_state():\n PATH = \'example_db.sqlite\'\n NAME = \'example_table\'\n\n STATE = {\n \'name\': NAME,\n \'path\': PATH\n }\n\n SCHEMA = \'\'\'\n CREATE TABLE example_table (\n name TEXT NOT NULL,\n datatype TEXT NOT NULL,\n value TEXT,\n pid INTEGER NOT NULL,\n PRIMARY KEY (name, pid)\n );\n \'\'\'\n\n SCHEMA_INIT = \'\'\'\n INSERT INTO example_table VALUES (\'SENSOR1\', \'int\', \'0\', 1);\n INSERT INTO example_table VALUES (\'SENSOR2\', \'float\', \'0.0\', 1);\n INSERT INTO example_table VALUES (\'SENSOR3\', \'int\', \'1\', 1);\n INSERT INTO example_table VALUES (\'ACTUATOR1\', \'int\', \'1\', 1);\n INSERT INTO example_table VALUES (\'ACTUATOR2\', \'int\', \'0\', 1);\n INSERT INTO example_table VALUES (\'SENSOR3\', \'int\', \'2\', 2);\n \'\'\'\n return STATE, SCHEMA, SCHEMA_INIT\n\ndef init_db(path, schema, schema_init):\n SQLiteState._create(path, schema)\n SQLiteState._init(path, schema_init)\n\nif __name__=="__main__":\n state, schema, init = example_state()\n init_db(state[\'path\'], schema, init)\n' + eq_(DefaultTemplate.state(), res) + +class TestDevice(): + + def test_plc(self): + res = "# PLC Template\nfrom minicps.devices import PLC\n\nPLC_DATA = {\n 'SENSOR1': '0',\n 'SENSOR2': '0.0',\n 'SENSOR3': '0', # interlock with PLC2\n 'ACTUATOR1': '1', # 0 means OFF and 1 means ON\n 'ACTUATOR2': '0',\n}\n\nPLC_TAGS = (\n ('SENSOR1', 1, 'INT'),\n ('SENSOR2', 1, 'REAL'),\n ('SENSOR3', 1, 'INT'), # interlock with PLC2\n ('ACTUATOR1', 1, 'INT'), # 0 means OFF and 1 means ON\n ('ACTUATOR2', 1, 'INT'))\n\nPLC_ADDR = '10.0.0.1'\n\nPLC_SERVER = {\n 'address': PLC_ADDR,\n 'tags': PLC_TAGS\n}\n\nPLC_PROTOCOL = {\n 'name': 'enip',\n 'mode': 1,\n 'server': PLC_SERVER\n}\n\nclass PLC1(PLC):\n\n def pre_loop(self, sleep=0.0):\n pass\n\n def main_loop(self, sleep=0.0):\n pass" + eq_(Device.plc(1), res) From e229e5a361d138d53ca9b5adc2b1dc10fabf2ea1 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Mon, 7 Aug 2017 15:11:39 +0800 Subject: [PATCH 14/33] added test for travis --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 9bf2a60..f1f5cdd 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,7 @@ tests-travis: $(TESTER_TRAVIS) $(TESTER_OPTS) tests/protocols_tests.py $(TESTER_TRAVIS) $(TESTER_OPTS) tests/devices_tests.py $(TESTER_TRAVIS) $(TESTER_OPTS) tests/states_tests.py + $(TESTER_TRAVIS) $(TESTER_OPTS) tests/ui_tests.py tests: $(TESTER) $(TESTER_OPTS) tests From 0e2aa065366ff906bc91e4ea2a36b63087c2362f Mon Sep 17 00:00:00 2001 From: francozappa Date: Mon, 11 Sep 2017 10:42:57 +0800 Subject: [PATCH 15/33] Add skeleton files for the cli doc --- docs/index.rst | 1 + docs/ui.rst | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 docs/ui.rst diff --git a/docs/index.rst b/docs/index.rst index 727ca18..87debfd 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -16,6 +16,7 @@ Contents: swat-tutorial contributing tests + ui misc diff --git a/docs/ui.rst b/docs/ui.rst new file mode 100644 index 0000000..17c93bf --- /dev/null +++ b/docs/ui.rst @@ -0,0 +1,15 @@ +.. UI {{{1 +.. _ui: + +*************** +User Interface +*************** + +.. CLI {{{2 + +======================= +Command Line Interface +======================= + +Subsection bla + From e4bbc5eebc1010eb7954570a0e22f6746c78d823 Mon Sep 17 00:00:00 2001 From: francozappa Date: Mon, 11 Sep 2017 10:49:38 +0800 Subject: [PATCH 16/33] Draft new version --- Makefile | 2 +- RELEASES.md | 6 ++++++ docs/conf.py | 4 ++-- setup.py | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index f1f5cdd..76390cd 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # VARIABLES {{{1 -LATEST_VERSION = 1.1.3 +LATEST_VERSION = 1.2.0 MININET = sudo mn PYTHON = sudo python diff --git a/RELEASES.md b/RELEASES.md index 6958af6..624d07f 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,11 @@ # Releases and Changelog +## Version 1.2.0 (TODO) + +### UI + +* Describe the new added functionality that is `mcps` + ## Version 1.1.3 (2017-05-14) ### Misc diff --git a/docs/conf.py b/docs/conf.py index 3bd66f6..4abe03f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -66,9 +66,9 @@ # built documents. # # The short X.Y version. -version = '1.1' +version = '1.2' # The full version, including alpha/beta/rc tags. -release = '1.1.3' +release = '1.2.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 143dee0..07bdccf 100755 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ # NOTE: https://packaging.python.org/ setup( name='minicps', - version='1.1.3', + version='1.2.0', description='MiniCPS: a framework for Cyber-Physical Systems \ real-time simulation, built on top of mininet.', # NOTE: long_description displayed on PyPi From 97404e6ced40dec8693c26c235da44f62bd51277 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Tue, 12 Sep 2017 14:25:56 +0800 Subject: [PATCH 17/33] fixed simple string formatting for clarity --- bin/mcps | 4 ++-- docs/index.rst | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/bin/mcps b/bin/mcps index 7f7ddba..378174a 100644 --- a/bin/mcps +++ b/bin/mcps @@ -14,7 +14,7 @@ def main(): pass @main.command() -@click.option('--config', default="default", help="configuration file for generating template.") +@click.option('--config', default="default", help="configuration file for generating template") @click.option('--path', help="directory path") def init(config, path): init = mcps.Init() @@ -30,7 +30,7 @@ def init(config, path): click.echo(click.style("Error: Permission Denied. Cannot write to {path}.".format(path), fg="red")) else: click.echo(click.style(traceback.format_exc(), fg="red")) - click.echo("... init aborted.".format(path)) + click.echo("... init aborted.") if __name__=="__main__": main() diff --git a/docs/index.rst b/docs/index.rst index 87debfd..209d625 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,8 +19,6 @@ Contents: ui misc - - Indices and tables ================== From 904927b6c99b4db70470e8dd226e48f2ed0dacb1 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Tue, 12 Sep 2017 14:26:16 +0800 Subject: [PATCH 18/33] added docs for CLI --- docs/ui.rst | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/ui.rst b/docs/ui.rst index 17c93bf..5fe298d 100644 --- a/docs/ui.rst +++ b/docs/ui.rst @@ -7,9 +7,42 @@ User Interface .. CLI {{{2 +This section documents the CLI for minicps. + ======================= Command Line Interface ======================= -Subsection bla +The command line utility is called ``mcps``. To get more information on the usage: + +.. code-block:: console + + mcps --help + +Default +-------- + +The base command generates a scaffold directory which has the minimum files necessary +to set-up a simulation environment with two ``PLC``'s as the default device. + +.. code-block:: console + + mcps init + +Custom +------- + +The command has additional options. + +.. code-block:: console + + mcps init [OPTIONS] + +``path`` + Provide a custom path for generating the scaffold. + +``config`` + Provide a configuration file to generate template with custom devices and values. + + Note: This is **NOT** implemented yet. From 4fa8053b7207cec757ce03b1394e2b14b48d2d5f Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Tue, 12 Sep 2017 14:39:57 +0800 Subject: [PATCH 19/33] added new version release notes --- RELEASES.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 624d07f..bda2c01 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,10 +1,11 @@ # Releases and Changelog -## Version 1.2.0 (TODO) +## Version 1.2.0 (2017-09-12) ### UI -* Describe the new added functionality that is `mcps` +* Added CLI tool `mcps`. + * Generate default scaffold using one single command. ## Version 1.1.3 (2017-05-14) From f2ea9e5154191247c628bdb25a7cbd10fe117124 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Tue, 12 Sep 2017 14:45:39 +0800 Subject: [PATCH 20/33] removed duplicate entry in setup file --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 07bdccf..f79a756 100755 --- a/setup.py +++ b/setup.py @@ -50,6 +50,5 @@ package_data={}, # NOTE: specify files with absolute paths data_files=None, - scripts=[], ) From 2724659716ab04740f4cc40b6dd1fb86fc61b49c Mon Sep 17 00:00:00 2001 From: francozappa Date: Tue, 12 Sep 2017 16:16:24 +0800 Subject: [PATCH 21/33] Use a separate rst file to generate mcps man page --- docs/conf.py | 10 +++++++++- docs/mcps-man.rst | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 docs/mcps-man.rst diff --git a/docs/conf.py b/docs/conf.py index 4abe03f..b2f597b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -264,8 +264,16 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). + +# NOTE: we don't want one man page for each document in the toc +# man_pages = [ +# (master_doc, 'minicps', u'minicps Documentation', +# [author], 1) +# ] + +# NOTE: mcps-man is not included in the TOC man_pages = [ - (master_doc, 'minicps', u'minicps Documentation', + ('mcps-man', 'mcps', u'mcps man page', [author], 1) ] diff --git a/docs/mcps-man.rst b/docs/mcps-man.rst new file mode 100644 index 0000000..eb6d831 --- /dev/null +++ b/docs/mcps-man.rst @@ -0,0 +1,46 @@ +.. MCPS-MAN {{{1 +.. _mcps-man: + +**************************************** +mcps man page (not included in the docs) +**************************************** + +TODO + +======================= +Section +======================= + +The command line utility is called ``mcps``. To get more information on the usage: + +.. code-block:: console + + mcps --help + +Subsection +---------- + +The base command generates a scaffold directory which has the minimum files necessary +to set-up a simulation environment with two ``PLC``'s as the default device. + +.. code-block:: console + + mcps init + +Custom +------- + +The command has additional options. + +.. code-block:: console + + mcps init [OPTIONS] + +``path`` + Provide a custom path for generating the scaffold. + +``config`` + Provide a configuration file to generate template with custom devices and values. + + Note: This is **NOT** implemented yet. + From e82ee1a3994b50be8183bc0afea0669afa499501 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Wed, 13 Sep 2017 14:21:09 +0800 Subject: [PATCH 22/33] edited man for mcps to follow man structure --- docs/mcps-man.rst | 52 +++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/mcps-man.rst b/docs/mcps-man.rst index eb6d831..7f199ad 100644 --- a/docs/mcps-man.rst +++ b/docs/mcps-man.rst @@ -5,42 +5,42 @@ mcps man page (not included in the docs) **************************************** -TODO +======== +Synopsis +======== -======================= -Section -======================= +``mcps`` [``--help``] +``mcps init`` [``--path`` path] [``--config`` file] -The command line utility is called ``mcps``. To get more information on the usage: +=========== +Description +=========== -.. code-block:: console +The command line utility is called ``mcps``. It generates a scaffold directory which has +the minimum files necessary to set-up a simulation environment. - mcps --help +==================== +Command Line Options +==================== -Subsection ----------- - -The base command generates a scaffold directory which has the minimum files necessary -to set-up a simulation environment with two ``PLC``'s as the default device. - -.. code-block:: console +``init`` +-------- - mcps init +This generates a default scaffold using two PLCs in the current working directory. -Custom -------- +``init [OPTIONS]`` +------------------ -The command has additional options. +``--path`` + Provide a custom path for generating the scaffold. -.. code-block:: console +``--config`` + Provide a configuration file to generate template with custom devices and values. - mcps init [OPTIONS] + Note: This is **NOT** implemented yet. -``path`` - Provide a custom path for generating the scaffold. - -``config`` - Provide a configuration file to generate template with custom devices and values. +``--help`` +---------- - Note: This is **NOT** implemented yet. +This lists the available commands. From f20556d072ade0d4faa6185c7a88fe2e0777ab57 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Wed, 13 Sep 2017 14:21:53 +0800 Subject: [PATCH 23/33] man is now generated in the docs dir when `make man` is executed; setup should copy man to ubuntu --- docs/Makefile | 1 + setup.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/Makefile b/docs/Makefile index 530989a..7cccf28 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -152,6 +152,7 @@ text: man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + cp $(BUILDDIR)/man/mcps.1 . @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." diff --git a/setup.py b/setup.py index f79a756..df773cc 100755 --- a/setup.py +++ b/setup.py @@ -49,6 +49,6 @@ # NOTE: specify files relative to the module path package_data={}, # NOTE: specify files with absolute paths - data_files=None, + data_files=[('man/man1', ['docs/mcps.1'])], ) From 95010586eb1c71908fe852a320620de39bef9058 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Wed, 13 Sep 2017 14:22:06 +0800 Subject: [PATCH 24/33] man for mcps bin --- docs/mcps.1 | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 docs/mcps.1 diff --git a/docs/mcps.1 b/docs/mcps.1 new file mode 100644 index 0000000..4e81920 --- /dev/null +++ b/docs/mcps.1 @@ -0,0 +1,64 @@ +.\" Man page generated from reStructuredText. +. +.TH "MCPS" "1" "Sep 13, 2017" "1.2" "minicps" +.SH NAME +mcps \- mcps man page +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBmcps\fP [\fB\-\-help\fP] +\fBmcps init\fP [\fB\-\-path\fP path] [\fB\-\-config\fP file] +.SH DESCRIPTION +.sp +The command line utility is called \fBmcps\fP\&. It generates a scaffold directory which has +the minimum files necessary to set\-up a simulation environment. +.SH COMMAND LINE OPTIONS +.SS \fBinit\fP +.sp +This generates a default scaffold using two PLCs in the current working directory. +.SS \fBinit [OPTIONS]\fP +.INDENT 0.0 +.TP +.B \fB\-\-path\fP +Provide a custom path for generating the scaffold. +.TP +.B \fB\-\-config\fP +Provide a configuration file to generate template with custom devices and values. +.sp +Note: This is \fBNOT\fP implemented yet. +.UNINDENT +.SS \fB\-\-help\fP +.sp +This lists the available commands. +.SH AUTHOR +scy-phy +.SH COPYRIGHT +2017, Daniele Antonioli +.\" Generated by docutils manpage writer. +. From b6148da85b6f828fbfa2a7c81e7c61441ff9e669 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Wed, 13 Sep 2017 14:34:14 +0800 Subject: [PATCH 25/33] formatted man page and corrected man location to be copied to --- docs/mcps-man.rst | 1 + docs/mcps.1 | 1 + setup.py | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/mcps-man.rst b/docs/mcps-man.rst index 7f199ad..a684ee6 100644 --- a/docs/mcps-man.rst +++ b/docs/mcps-man.rst @@ -10,6 +10,7 @@ Synopsis ======== ``mcps`` [``--help``] + ``mcps init`` [``--path`` path] [``--config`` file] =========== diff --git a/docs/mcps.1 b/docs/mcps.1 index 4e81920..6176313 100644 --- a/docs/mcps.1 +++ b/docs/mcps.1 @@ -33,6 +33,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .SH SYNOPSIS .sp \fBmcps\fP [\fB\-\-help\fP] +.sp \fBmcps init\fP [\fB\-\-path\fP path] [\fB\-\-config\fP file] .SH DESCRIPTION .sp diff --git a/setup.py b/setup.py index df773cc..9b4a268 100755 --- a/setup.py +++ b/setup.py @@ -49,6 +49,6 @@ # NOTE: specify files relative to the module path package_data={}, # NOTE: specify files with absolute paths - data_files=[('man/man1', ['docs/mcps.1'])], + data_files=[('/usr/share/man/man1', ['docs/mcps.1'])], ) From 97c235588fb2f198a1de0a8beb91795d8f029fa6 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Fri, 15 Sep 2017 18:00:30 +0800 Subject: [PATCH 26/33] added argument for specifying folder name --- bin/mcps | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bin/mcps b/bin/mcps index 378174a..d2f3704 100644 --- a/bin/mcps +++ b/bin/mcps @@ -14,14 +14,17 @@ def main(): pass @main.command() -@click.option('--config', default="default", help="configuration file for generating template") -@click.option('--path', help="directory path") -def init(config, path): +@click.option('--config', help="configuration file for generating template") +@click.option('--path', help="path to the scaffold directory") +@click.argument('name', default="scaffold") +def init(config, path, name): init = mcps.Init() - path = path if (path is not None) else "./scaffold" + + path = path if (path is not None) else "./" + full_path = "{path}/{folder}".format(path=path.rstrip('/'), folder=name) try: - init.make(path=path) + init.make(path=full_path) click.echo(click.style("\n... Success: directory generated.", fg="green", bold=True)) except OSError as e: if e.errno == errno.EEXIST: From 97e92ab35a3f3ff75f0b4f7d15f1af6f0348f1ef Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Fri, 15 Sep 2017 18:00:58 +0800 Subject: [PATCH 27/33] updated docs for the addition of argument for mcps --- docs/mcps-man.rst | 8 +++++--- docs/ui.rst | 7 +++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/mcps-man.rst b/docs/mcps-man.rst index a684ee6..ed3770e 100644 --- a/docs/mcps-man.rst +++ b/docs/mcps-man.rst @@ -11,7 +11,7 @@ Synopsis ``mcps`` [``--help``] -``mcps init`` [``--path`` path] [``--config`` file] +``mcps init`` [NAME] [``--path`` path] [``--config`` file] =========== Description @@ -29,8 +29,10 @@ Command Line Options This generates a default scaffold using two PLCs in the current working directory. -``init [OPTIONS]`` ------------------- +``init [NAME] [OPTIONS]`` +------------------------- +``[NAME]`` + Custom name of the directory to be generated. ``--path`` Provide a custom path for generating the scaffold. diff --git a/docs/ui.rst b/docs/ui.rst index 5fe298d..4104fe6 100644 --- a/docs/ui.rst +++ b/docs/ui.rst @@ -22,7 +22,7 @@ The command line utility is called ``mcps``. To get more information on the usag Default -------- -The base command generates a scaffold directory which has the minimum files necessary +The base command generates a default scaffold directory which has the minimum files necessary to set-up a simulation environment with two ``PLC``'s as the default device. .. code-block:: console @@ -36,7 +36,10 @@ The command has additional options. .. code-block:: console - mcps init [OPTIONS] + mcps init [NAME] [OPTIONS] + +``[NAME]`` + Custom name of the directory to be generated. ``path`` Provide a custom path for generating the scaffold. From 3c00cb584626e67c4b3468433bbc3ba3fa33c07c Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Mon, 18 Sep 2017 17:23:04 +0800 Subject: [PATCH 28/33] add subcommand description and update default name --- bin/mcps | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/bin/mcps b/bin/mcps index d2f3704..9bf2bfa 100644 --- a/bin/mcps +++ b/bin/mcps @@ -11,16 +11,22 @@ import minicps.ui.commands as mcps @click.group() def main(): + """ A command line tool for MINICPS.""" pass -@main.command() -@click.option('--config', help="configuration file for generating template") -@click.option('--path', help="path to the scaffold directory") -@click.argument('name', default="scaffold") +@main.command('init', short_help="Initialize a scaffold.") +@click.option('--config', help="Configuration file for generating template.") +@click.option('--path', help="Path to the scaffold directory.") +@click.argument('name', default="myproject") def init(config, path, name): + """ + Initialize directory NAME with optional custom configuration. + + Default dir name: `myproject`. + """ init = mcps.Init() - path = path if (path is not None) else "./" + path = path if (path is not None) else "." full_path = "{path}/{folder}".format(path=path.rstrip('/'), folder=name) try: From 236d94c111b0f770384e75f667198d47a5b5563e Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Mon, 18 Sep 2017 17:23:24 +0800 Subject: [PATCH 29/33] improved man page for mcps --- docs/mcps-man.rst | 2 +- docs/mcps.1 | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/mcps-man.rst b/docs/mcps-man.rst index ed3770e..611eb4d 100644 --- a/docs/mcps-man.rst +++ b/docs/mcps-man.rst @@ -27,7 +27,7 @@ Command Line Options ``init`` -------- -This generates a default scaffold using two PLCs in the current working directory. +This generates a default scaffold directory (``myproject``) using two PLCs in the current working directory. ``init [NAME] [OPTIONS]`` ------------------------- diff --git a/docs/mcps.1 b/docs/mcps.1 index 6176313..020cf0e 100644 --- a/docs/mcps.1 +++ b/docs/mcps.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "MCPS" "1" "Sep 13, 2017" "1.2" "minicps" +.TH "MCPS" "1" "Sep 18, 2017" "1.2" "minicps" .SH NAME mcps \- mcps man page . @@ -34,7 +34,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .sp \fBmcps\fP [\fB\-\-help\fP] .sp -\fBmcps init\fP [\fB\-\-path\fP path] [\fB\-\-config\fP file] +\fBmcps init\fP [NAME] [\fB\-\-path\fP path] [\fB\-\-config\fP file] .SH DESCRIPTION .sp The command line utility is called \fBmcps\fP\&. It generates a scaffold directory which has @@ -42,10 +42,13 @@ the minimum files necessary to set\-up a simulation environment. .SH COMMAND LINE OPTIONS .SS \fBinit\fP .sp -This generates a default scaffold using two PLCs in the current working directory. -.SS \fBinit [OPTIONS]\fP +This generates a default scaffold directory (\fBmyproject\fP) using two PLCs in the current working directory. +.SS \fBinit [NAME] [OPTIONS]\fP .INDENT 0.0 .TP +.B \fB[NAME]\fP +Custom name of the directory to be generated. +.TP .B \fB\-\-path\fP Provide a custom path for generating the scaffold. .TP From 2bfbe4f54087bf202df5c48b84dfab427cb469c5 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Mon, 18 Sep 2017 17:23:52 +0800 Subject: [PATCH 30/33] update man page placement location --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9b4a268..dfee96a 100755 --- a/setup.py +++ b/setup.py @@ -49,6 +49,6 @@ # NOTE: specify files relative to the module path package_data={}, # NOTE: specify files with absolute paths - data_files=[('/usr/share/man/man1', ['docs/mcps.1'])], + data_files=[('/usr/local/man/man1', ['docs/mcps.1'])], ) From 0ac9b771bb0ea6f25dacea379104e705acb75e0d Mon Sep 17 00:00:00 2001 From: francozappa Date: Mon, 18 Sep 2017 17:55:28 +0800 Subject: [PATCH 31/33] Add click dependency --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index dfee96a..6c99ceb 100755 --- a/setup.py +++ b/setup.py @@ -45,6 +45,7 @@ 'pyasn1', 'pymodbus', 'cpppo', + 'click', ], # NOTE: specify files relative to the module path package_data={}, From 3ece10431c1989322284765a8a343d9f8341ebf3 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Mon, 18 Sep 2017 18:07:02 +0800 Subject: [PATCH 32/33] update help message for subcommand `init` --- bin/mcps | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/mcps b/bin/mcps index 9bf2bfa..6468028 100644 --- a/bin/mcps +++ b/bin/mcps @@ -14,7 +14,7 @@ def main(): """ A command line tool for MINICPS.""" pass -@main.command('init', short_help="Initialize a scaffold.") +@main.command('init', short_help="Initialize a directory with default files for MINICPS simulation.") @click.option('--config', help="Configuration file for generating template.") @click.option('--path', help="Path to the scaffold directory.") @click.argument('name', default="myproject") From 09ecbfed98226b88972f1ece0d21210afad410c7 Mon Sep 17 00:00:00 2001 From: remmihsorp Date: Mon, 18 Sep 2017 18:20:05 +0800 Subject: [PATCH 33/33] updated release date for version 1.2.0 --- RELEASES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index bda2c01..a635890 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,11 +1,11 @@ # Releases and Changelog -## Version 1.2.0 (2017-09-12) +## Version 1.2.0 (2017-09-18) ### UI * Added CLI tool `mcps`. - * Generate default scaffold using one single command. + * Generate default files for simulation using a single command. ## Version 1.1.3 (2017-05-14)