From bb0cbd513ebd58b72f401471a076a66ffe72cc99 Mon Sep 17 00:00:00 2001 From: Kevin Matz Date: Wed, 17 Oct 2018 20:56:59 -0400 Subject: [PATCH] Initial Commit --- .gitignore | 118 ++++++++++++++++++++++++++ CommentMacro.g4 | 107 ++++++++++++++++++++++++ README.md | 41 +++++++++ comment.py | 216 ++++++++++++++++++++++++++++++++++++++++++++++++ server.cfg | 4 + 5 files changed, 486 insertions(+) create mode 100644 .gitignore create mode 100644 CommentMacro.g4 create mode 100644 README.md create mode 100755 comment.py create mode 100644 server.cfg diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39d19d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,118 @@ +# Antlr4 +.interp +.tokens +CommentMacroLexer.py +CommentMacroListener.py +CommentMacroParser.py + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json diff --git a/CommentMacro.g4 b/CommentMacro.g4 new file mode 100644 index 0000000..c194b45 --- /dev/null +++ b/CommentMacro.g4 @@ -0,0 +1,107 @@ +grammar CommentMacro; + +/** The grammer begins here, as a series of statements. */ +prog: statement+ ; + +/** Each statement has one or many expressions */ +statement + : macro NEWLINE + | NEWLINE + ; + +macro + : 'GM' master device? // Go Master + | 'GM' master SLASH number device? // Go Master + | 'HM' master device? // Halt Master + | 'AM' master device? // Assert Master + | 'RM' master device? // Relesae Master + | 'RA' device? // Release All + | 'RO' device? // Release Others + | 'FM' master SLASH number time? device? // Fade Master + | 'FGM' number time? device? // Fade Grand Master + | 'CM' number device? // Choose Master + | 'GL' target device? // Go List + | 'GL' target SLASH number device? // Go List + | 'HL' target device? // Halt List + | 'AL' target device? // Assert List + | 'RL' target device? // Release List + | 'GB' target device? // Go Batch + | 'HB' target device? // Halt Batch + | 'AB' target device? // Assert Batch + | 'RB' target device? // Release Batch + | 'GS' target device? // Go Scene + | 'HS' target device? // Halt Scene + | 'AS' target device? // Assert Scene + | 'RS' target device? // Release Scene + | 'CP' number device? // Change Page + | 'CP' NEXT device? // Next Page + | 'CP' PREV device? // Prev Page + | 'RV' number device? // Recall View + | 'RN' device // Reset Node + | 'GK' number device? // Go Keystroke Macro + | 'HK' number device? // Halt Keystroke Macro + | 'RK' number device? // Stop Keystroke Macro + ; + +master : (target | CURRENT) ; +time : TIME number ; +device : nodeType number ; +number : NUMBER ; + +nodeType + : WHOLEHOG + | DP8K + | IOP + ; + +/** recursive targeting is non-greedy */ +target + : ( number | span ) also*? + ; + +span + : number THRU number + ; + +also + : ALSO target + ; + +SLASH : '/' ; +ALSO : ',' ; +THRU : '>' ; +NEXT : '+' ; +PREV : '-' ; +CURRENT : '*' ; +TIME : 't' ; + +WHOLEHOG : [hH] ; +DP8K : [dD] ; +IOP : 'IOP'; + +NUMBER // intigers or floats + : [0-9]+ '.' [0-9]* + | '.' [0-9]+ + | [0-9]+ + ; + +NEWLINE // return newlines to parser (end-statement signal) + : '\r'? '\n' + ; + +WS // ignore whitespace + : [ \t]+ + -> skip + ; + +COMMENT // toss c and HTML sytle block comments + : ( '' + | '/*' .*? '*/' + ) -> skip + ; + +LINE_COMMENT + : ( '//' ~[\r\n]* + | '#' ~[\r\n]* + ) -> skip + ; diff --git a/README.md b/README.md new file mode 100644 index 0000000..0034fac --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Bacon Script + + +## Installing + +> $ pip install python-osc + +Building the lexer and parser for Python3 + +> $ antlr4 -Dlanguage=Python3 CommentMacro.g4 + + + +## Unsupported Macros +These Comment Macros do not have supporting equivelents in OSC: + +* Release All +* Release Others +* Master Assert +* List Assert +* Scene Assert +* Batch GO +* Batch Halt +* Batch Assert +* Batch Release +* Node Reset +* Change Page +* Next Page +* Prev Page +* Recall View +* Keystroke Macro Go +* Keystroke Macro Halt +* Keystroke Macro Stop + +## Future Work +Pleas feel welcome to submit pull requests or patches that enable support for: + +* Sending target ranges as batches +* Multiple comment macros per line +* Send multi-macro line as a batch +* Timing on master fades diff --git a/comment.py b/comment.py new file mode 100755 index 0000000..03778ba --- /dev/null +++ b/comment.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +"""comment.py: Hog 4 comment macro interpreter and OSC bridge.""" + +__author__ = "Kevin Matz" +__copyright__ = "Copyright 2018, Company 235, LLC" +__credits__ = ["Kevin Matz"] + +__license__ = "MIT" +__version__ = "0.0.1" +__maintainer__ = "Kevin Matz" +__email__ = "kevin@company235.com" + +__status__ = "Prototype" + + +import antlr4 +import configparser +import readline +import signal +import sys + +from CommentMacroLexer import CommentMacroLexer +from CommentMacroParser import CommentMacroParser +from pythonosc import osc_message_builder +from pythonosc import udp_client +from time import sleep + + +class hog4_osc: + 'A Hog 4 OSC device on the network' + + def __init__(self, ip, port, net): + self.ip = ip + self.port = port + self.net = net + self.osc = udp_client.SimpleUDPClient(ip, port) + + +# refactor this to support multiple net#s +config = configparser.ConfigParser(allow_no_value=True) +config.read('server.cfg') +server = config['hog4'] +hog4 = hog4_osc(server.get("ip", "10.0.0.1"), + server.getint("port", 6600), + server.getint("net", 1)) + + +def _master_go(expr): + if (expr.master().getText() == "*"): + print("Main GO") + hog4.osc.send_message("/hog/hardware/maingo", 1) # button down + button_delay() + hog4.osc.send_message("/hog/hardware/maingo", 0) # button up + return 1 + + print("GO Master is a stub") + return -1 + + +def _master_halt(expr): + print("Halt Master is a stub") + return -1 + + +def _master_release(expr): + print("Release Master is a stub") + return -1 + + +def _master_fade(expr): + print("Fade Master is a stub") + return -1 + + +def _master_fade_grand(expr): + try: + level = float(expr.number().getText()) + except ValueError: + print("Expected a number...") + return -1 + + if (level < 0 or level > 100): + print("Level must be between 0 and 100.") + return -1 + + print("Fading Grand Master to " + str(level) + "%") + level *= 255 / 100 # percent in Macro, 0>255 in OSC + hog4.osc.send_message("/hog/hardware/fader/0", level) + return 1 + + +def _master_choose(expr): + try: + master = int(expr.number().getText()) + except ValueError: + print("Expected a number...") + return -1 + + if (master < 0): + print("Master must be greater than 0.") + return -1 + + print("Choose Master " + str(master)) + hog4.osc.send_message("/hog/hardware/choose/" + str(master), 1) # down + button_delay() + hog4.osc.send_message("/hog/hardware/choose/" + str(master), 0) # up + return 1 + + +def _list_go(expr): + print("Go List is a stub") + return -1 + + +def _list_halt(expr): + print("Halt List is a stub") + return -1 + + +def _list_release(expr): + print("Release List is a stub") + return -1 + + +def _scene_go(expr): + print("Go Scene is a stub") + return -1 + + +def _scene_halt(expr): + print("Halt Scene is a stub") + return -1 + + +def _scene_release(expr): + print("Release Scene is a stub") + return -1 + + +command = {"GM": _master_go, + "HM": _master_halt, + "RM": _master_release, + "FM": _master_fade, + "FGM": _master_fade_grand, + "CM": _master_choose, + "GL": _list_go, + "HL": _list_halt, + "RL": _list_release, + "GS": _scene_go, + "HS": _scene_halt, + "RS": _scene_release + } + + +def button_delay(time=0.05): + sleep(time) + + +def comment(text): + input_stream = antlr4.InputStream(text) + lexer = CommentMacroLexer(input_stream) + stream = antlr4.CommonTokenStream(lexer) + parser = CommentMacroParser(stream) + tree = parser.macro() + + lisp_tree_str = tree.toStringTree(recog=parser) + print(beautify_lisp_string(lisp_tree_str)) + + name = tree.children[0].getText() + try: + command[name](tree) + except KeyError: + print(name + " macro is not compatable with OSC.") + return -1 + return 1 + + +def orderly_exit(signal=None, frame=None): + print('Goodbye.') + sys.exit(0) + + +# https://raw.githubusercontent.com/jszheng/py3antlr4book/master/bin/pygrun +# this is a python version of TestRig +def beautify_lisp_string(in_string): + __author__ = 'jszheng' + indent_size = 2 + add_indent = ' ' * indent_size + out_string = in_string[0] # no indent for 1st ( + indent = '' + for i in range(1, len(in_string) - 1): + if in_string[i] == '(' and in_string[i + 1] != ' ': + indent += add_indent + out_string += "\n" + indent + '(' + elif in_string[i] == ')': + out_string += ')' + if len(indent) > 0: + indent = indent.replace(add_indent, '', 1) + else: + out_string += in_string[i] + return out_string + + +if __name__ == '__main__': + signal.signal(signal.SIGINT, orderly_exit) + + if len(sys.argv) > 1: + comment(sys.argv[1]) + else: + while True: + text = input("comment# ") + if text == 'exit': + orderly_exit() + comment(text) diff --git a/server.cfg b/server.cfg new file mode 100644 index 0000000..f4c45e0 --- /dev/null +++ b/server.cfg @@ -0,0 +1,4 @@ +[hog4] + ip: 10.0.0.1 + port: 6600 + net: 1