diff --git a/.gitignore b/.gitignore
index 74b844b..f628654 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
__pycache__/
RTOC/__pycache__/
RTOC/RTLogger/__pycache__/
+RTOC/RTLogger/plugins/test.py
RTOC/RTLogger/plugins/__pycache__/
RTOC/RTOC_GUI/__pycache__/
RTOC/lib/__pycache__/
diff --git a/RTOC.egg-info/PKG-INFO b/RTOC.egg-info/PKG-INFO
index 2c2305e..17f46b8 100644
--- a/RTOC.egg-info/PKG-INFO
+++ b/RTOC.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: RTOC
-Version: 2.1.0
+Version: 2.1.1
Summary: RealTime OpenControl
Home-page: https://github.com/Haschtl/RealTimeOpenControl
Author: Sebastian Keller
@@ -8,10 +8,10 @@ Author-email: sebastiankeller@online.de
License: GNU
Description: # RealTime OpenControl (RTOC)
- | [![Documentation Status](https://readthedocs.org/projects/realtimeopencontrol/badge/?version=latest)](https://realtimeopencontrol.readthedocs.io/en/latest/) | [![Builds 2.1.0v](https://img.shields.io/badge/Builds%20version-1.6-brightgreen.svg?style=flat)](https://github.com/Haschtl/RealTimeOpenControl/releases) | [![PyPI pyversions](https://img.shields.io/pypi/pyversions/ansicolortags.svg)](https://pypi.python.org/pypi/RTOC/) | [![PyPI version fury.io](https://badge.fury.io/py/ansicolortags.svg)](https://pypi.python.org/pypi/RTOC/) | [![PyPI license](https://img.shields.io/pypi/l/ansicolortags.svg)](https://pypi.python.org/pypi/RTOC/) | [![GitHub release](https://img.shields.io/github/release/Naereen/StrapDown.js.svg)](https://github.com/Haschtl/RealTimeOpenControl/releases/) | [![Github all releases](https://img.shields.io/github/downloads/Naereen/StrapDown.js/total.svg)](https://github.com/Haschtl/RealTimeOpenControl/releases/) |
+ | [![Documentation Status](https://readthedocs.org/projects/realtimeopencontrol/badge/?version=latest)](https://realtimeopencontrol.readthedocs.io/en/latest/) | [![Builds 2.1.1v](https://img.shields.io/badge/Builds%20version-1.6-brightgreen.svg?style=flat)](https://github.com/Haschtl/RealTimeOpenControl/releases) | [![PyPI pyversions](https://img.shields.io/pypi/pyversions/ansicolortags.svg)](https://pypi.python.org/pypi/RTOC/) | [![PyPI version fury.io](https://badge.fury.io/py/ansicolortags.svg)](https://pypi.python.org/pypi/RTOC/) | [![PyPI license](https://img.shields.io/pypi/l/ansicolortags.svg)](https://pypi.python.org/pypi/RTOC/) | [![GitHub release](https://img.shields.io/github/release/Naereen/StrapDown.js.svg)](https://github.com/Haschtl/RealTimeOpenControl/releases/) | [![Github all releases](https://img.shields.io/github/downloads/Naereen/StrapDown.js/total.svg)](https://github.com/Haschtl/RealTimeOpenControl/releases/) |
- ### Version 2.1.0
+ ### Version 2.1.1
![Usecase](screenshots/RTOC-schematik.png)
[Documentation](https://realtimeopencontrol.readthedocs.io/en/latest/)
@@ -160,6 +160,6 @@ Classifier: Topic :: Software Development :: User Interfaces
Requires-Python: >=3
Description-Content-Type: text/markdown
Provides-Extra: Telegram
-Provides-Extra: ALL
Provides-Extra: GUI
Provides-Extra: Webserver
+Provides-Extra: ALL
diff --git a/RTOC/LoggerPlugin.py b/RTOC/LoggerPlugin.py
index 6ee622c..643423b 100644
--- a/RTOC/LoggerPlugin.py
+++ b/RTOC/LoggerPlugin.py
@@ -1,4 +1,4 @@
-# LoggerPlugin v2.5
+# LoggerPlugin v2.6
import traceback
import time
import sys
@@ -40,18 +40,20 @@ def __init__(self, stream=None, plot=None, event=None):
event (method): The callback-method for the event-method
"""
- def __init__(self, stream=None, plot=None, event=None):
+ def __init__(self, stream=None, plot=None, event=None, telegramBot=None, *args, **kwargs):
# Plugin setup
# self.setDeviceName()
self._deviceName = "noDevice"
self._cb = stream
self._ev = event
self._plt = plot
+ self._bot = telegramBot
self._sock = None
self._tcppassword = ''
self._tcpport = 5050
self._tcpaddress = ''
self._tcpthread = False
+ self.widget = None
self._pluginThread = None
self._oldPerpetualTimer = False
self.lockPerpetialTimer = Lock()
@@ -159,12 +161,12 @@ def stream(self, y=[], snames=[], dname=None, unit=None, x=None, slist=None, sdi
unit.append(sdict[dev][sig][1])
x.append(now)
else:
- logging.error('STREAM ERROR, signal has not this format: [y, "unit"]')
+ logging.error('STREAM ERROR, signal {}.{} has not this format: [y, "unit"]'.format(dname, sig))
else:
- logging.error('STREAM_ERROR: One signal was malformed')
+ logging.error('STREAM_ERROR:signal {}.{} was malformed.'.format(dname, sig))
self._cb(y=y, snames=snames, dname=dname, unit=unit, x=x)
else:
- logging.error('STREAM_ERROR: One device was malformed')
+ logging.error('STREAM_ERROR: device {} was malformed.'.format(dname))
return True
else:
logging.error('STREAM_ERROR: The data you provided with in your plugin was wrong."')
@@ -265,7 +267,7 @@ def createTCPClient(self, address="localhost", password=None, tcpport=5050, thre
self._tcppassword = password
self._sock.setKeyword(password)
- def sendTCP(self, y=None, sname=None, dname=None, unit=None, x=None, getSignal=None, getLatest=None, getEvent=None, getSignalList=False, getEventList=False, getPluginList=False, getSession=False, plot=False, event=None, remove=None, plugin=None, logger=None, stream=None):
+ def sendTCP(self, y=None, sname=None, dname=None, unit=None, x=None, getSignal=None, getLatest=None, getEvent=None, getSignalList=False, getEventList=False, getPluginList=False, getSession=False, plot=False, event=None, remove=None, plugin=None, logger=None, stream=None, timeout=5):
"""
Use any of the arguments described in :doc:`TCP`.
@@ -289,6 +291,7 @@ def sendTCP(self, y=None, sname=None, dname=None, unit=None, x=None, getSignal=N
getEventList: :py:meth:`.NetworkFunctions.getEventList`
getPluginList: :py:meth:`.NetworkFunctions.getPluginList`
getSession: :py:meth:`.RT_data.generateSessionJSON`
+ timeout: TCP-Client Timeout (Default: 5s)
Returns:
tcp_response (dict), if createTCPClient(threaded=False)
@@ -297,12 +300,12 @@ def sendTCP(self, y=None, sname=None, dname=None, unit=None, x=None, getSignal=N
"""
if self._tcpthread:
- t = Thread(target=self._sendTCP, args=(y, sname, dname, unit, x, getSignal, getLatest, getEvent, getSignalList, getEventList, getPluginList, getSession, plot, event, remove, plugin, logger, stream,))
+ t = Thread(target=self._sendTCP, args=(y, sname, dname, unit, x, getSignal, getLatest, getEvent, getSignalList, getEventList, getPluginList, getSession, plot, event, remove, plugin, logger, stream, timeout))
t.start()
else:
- return self._sendTCP(y, sname, dname, unit, x, getSignal, getLatest, getEvent, getSignalList, getEventList, getPluginList, getSession, plot, event, remove, plugin, logger, stream)
+ return self._sendTCP(y, sname, dname, unit, x, getSignal, getLatest, getEvent, getSignalList, getEventList, getPluginList, getSession, plot, event, remove, plugin, logger, stream, timeout)
- def _sendTCP(self, y=None, sname=None, dname=None, unit=None, x=None, getSignal=None, getLatest=None, getEvent=None, getSignalList=False, getEventList=False, getPluginList=False, getSession=False, plot=False, event=None, remove=None, plugin=None, logger=None, stream=None):
+ def _sendTCP(self, y=None, sname=None, dname=None, unit=None, x=None, getSignal=None, getLatest=None, getEvent=None, getSignalList=False, getEventList=False, getPluginList=False, getSession=False, plot=False, event=None, remove=None, plugin=None, logger=None, stream=None, timeout=5):
with lock:
if x is None and y is not None and not plot:
@@ -343,7 +346,7 @@ def _sendTCP(self, y=None, sname=None, dname=None, unit=None, x=None, getSignal=
# dicti['password'] = hex_dig
if self._sock:
try:
- self._sock.connect(self._tcpaddress, self._tcpport, self._tcppassword)
+ self._sock.connect(self._tcpaddress, self._tcpport, self._tcppassword, timeout=timeout)
self._sock.send(dicti)
response = self._sock.recv()
# self._sock.close()
@@ -540,6 +543,45 @@ def __updateT(self, func):
func()
diff = (time.time() - start_time)
+ def telegram_send_message(self, text, onlyAdmin=False):
+ """
+ Sends a message to all clients (or only admins).
+
+ Args:
+ text (str): Text to be send to the clients.
+ onlyAdmin (bool): If True, only admins will get this message
+ """
+ if self._bot is not None:
+ self._bot.send_message_to_all(text, onlyAdmin)
+ else:
+ logging.warning('TelegramBot is not enabled or wrong configured! Can not send message "{}"'.format(text))
+
+ def telegram_send_photo(self, path, onlyAdmin=False):
+ """
+ Sends the picture at a given path to all clients (or only admins).
+
+ Args:
+ path (str): Path to the picture to send.
+ onlyAdmin (bool): If True, only admins will get this message
+ """
+ if self._bot is not None:
+ self._bot.send_photo(path, onlyAdmin)
+ else:
+ logging.warning('TelegramBot is not enabled or wrong configured! Can not send photo "{}"'.format(path))
+
+ def telegram_send_document(self, path, onlyAdmin=False):
+ """
+ Sends any document at a given path to all clients (or only admins).
+
+ Args:
+ path (str): Path to the file to send.
+ onlyAdmin (bool): If True, only admins will get this message
+ """
+ if self._bot is not None:
+ self._bot.send_document(path, onlyAdmin)
+ else:
+ logging.warning('TelegramBot is not enabled or wrong configured! Can not send file "{}"'.format(path))
+
class _perpetualTimer():
@@ -571,7 +613,8 @@ def _handle_function(self):
timedelta = timedelta + self._correction
if timedelta < 0:
timedelta = 0
- if not self._lock.locked() and not self._cancel:
+ if not self._cancel and not self._lock.locked():
+ # with self._lock:
self._thread = Timer(timedelta, self._handle_function)
self.thread_counter += 1
self._thread.start()
diff --git a/RTOC/RTLogger/DeviceFunctions.py b/RTOC/RTLogger/DeviceFunctions.py
index 20e00ab..ac1768d 100644
--- a/RTOC/RTLogger/DeviceFunctions.py
+++ b/RTOC/RTLogger/DeviceFunctions.py
@@ -86,7 +86,7 @@ def startPlugin(self, name, remote=True):
# if callback is None:
self.pluginObjects[
name] = importlib.import_module(
- fullname).Plugin(self.database.addDataCallback, self.database.plot, self.database.addNewEvent)
+ fullname).Plugin(stream=self.database.addDataCallback, plot=self.database.plot, event=self.database.addNewEvent, telegramBot=self.telegramBot)
# else:
# self.pluginObjects[name] = importlib.import_module(
# fullname).Plugin(callback, self.addNewEvent)
diff --git a/RTOC/RTLogger/NetworkFunctions.py b/RTOC/RTLogger/NetworkFunctions.py
index 0016fba..d436745 100644
--- a/RTOC/RTLogger/NetworkFunctions.py
+++ b/RTOC/RTLogger/NetworkFunctions.py
@@ -343,7 +343,7 @@ def getPluginDict(self):
dict[name]['parameters'] = []
dict[name]['status'] = False
for fun in self.pluginFunctions.keys():
- hiddenFuncs = ["loadGUI", "updateT", "stream", "plot", "event", "createTCPClient", "sendTCP", "close", "cancel", "start", "setSamplerate","setDeviceName",'setPerpetualTimer','setInterval','getDir']
+ hiddenFuncs = ["loadGUI", "updateT", "stream", "plot", "event", "createTCPClient", "sendTCP", "close", "cancel", "start", "setSamplerate","setDeviceName",'setPerpetualTimer','setInterval','getDir', 'telegram_send_message', 'telegram_send_photo', 'telegram_send_document']
if fun.startswith(name+".") and fun not in [name+'.'+i for i in hiddenFuncs]:
dict[name]['functions'].append(fun.replace(name+".", ''))
diff --git a/RTOC/RTLogger/RTLogger.py b/RTOC/RTLogger/RTLogger.py
index b42285b..d661e8a 100644
--- a/RTOC/RTLogger/RTLogger.py
+++ b/RTOC/RTLogger/RTLogger.py
@@ -347,9 +347,9 @@ def _load_config(self):
if key2 not in self.__config[key].keys():
self.__config[key][key2] = defaultconfig[key][key2]
- except Exception:
+ except Exception as error:
logging.debug(traceback.format_exc())
- logging.error('Error loading config.json')
+ logging.error('Error loading config.json\n{}'.format(error))
self.__config = _Config(defaultconfig)
else:
logging.warning('No config-file found.')
diff --git a/RTOC/RTLogger/RTRemote.py b/RTOC/RTLogger/RTRemote.py
index 27c7b06..f29e154 100644
--- a/RTOC/RTLogger/RTRemote.py
+++ b/RTOC/RTLogger/RTRemote.py
@@ -97,7 +97,7 @@ def getAllSignals(self):
selection.append(".".join(i))
for s in selection:
ssplit = s.split('.')
- ans = self._sendTCP(getSignal={'dname':ssplit[0], 'sname':ssplit[1], 'xmin': self.xmin, 'xmax': self.xmax, 'maxN': self.maxN})
+ ans = self._sendTCP(getSignal={'dname':ssplit[0], 'sname':ssplit[1], 'xmin': self.xmin, 'xmax': self.xmax, 'maxN': self.maxN}, timeout=20)
if ans != False:
if 'signals' in ans.keys():
for sig in ans['signals'].keys():
@@ -280,15 +280,17 @@ def connect(self, hostname='127.0.0.1', port=5050, name='RemoteDevice', password
self.connections.append(newConnection)
self.getRemoteDeviceList()
- def disconnect(self, hostname):
+ def disconnect(self, hostname, port):
if len(hostname.split(':')) == 2:
hostname = hostname.split(':')[0]
+ index = -1
for idx, c in enumerate(self.connections):
- if c.host == hostname:
+ if c.host == hostname and c.port ==port:
self.connections[idx].stop()
- self.connections.pop(idx)
self.devices.pop(c.name)
+ # self.devicenames.pop(c.name)
+ # self.pluginStatus.pop(c.name)
devs = []
for dev in self.devicenames.keys():
namesplit = dev.split(':')
@@ -299,12 +301,17 @@ def disconnect(self, hostname):
self.pluginStatus.pop(dev)
if self.logger.reloadDevicesCallback is not None:
self.logger.reloadDevicesCallback()
- return True
- return False
+ index = idx
+ break
+ if index != -1:
+ self.connections.pop(index)
+ return True
+ else:
+ return False
- def getConnection(self, host):
+ def getConnection(self, host, port):
for idx, c in enumerate(self.connections):
- if host == c.host:
+ if host == c.host and port == c.port:
return self.connections[idx]
return None
diff --git a/RTOC/RTLogger/RT_data.py b/RTOC/RTLogger/RT_data.py
index b2c8fa5..2482d4d 100644
--- a/RTOC/RTLogger/RT_data.py
+++ b/RTOC/RTLogger/RT_data.py
@@ -1128,9 +1128,9 @@ def _getSQLSignalUnit(self, sigID):
def _appendSQLSignal(self, sigID, x, y):
sql = 'UPDATE '+SIGNAL_TABLE_NAME + \
- ' SET X = array_cat(X, ARRAY'+str(list(x))+') WHERE ID ='+str(sigID)+';'
+ ' SET X = array_cat(X, ARRAY'+str(list(x))+'::NUMERIC[]) WHERE ID ='+str(sigID)+';'
sql += '\nUPDATE '+SIGNAL_TABLE_NAME + \
- ' SET Y = array_cat(Y,ARRAY'+str(list(y))+') WHERE ID ='+str(sigID)+';'
+ ' SET Y = array_cat(Y,ARRAY'+str(list(y))+'::NUMERIC[]) WHERE ID ='+str(sigID)+';'
# sql += '\nUPDATE '+SIGNAL_TABLE_NAME+' SET UNIT = \'' + \
# str(dataunit)+'\' WHERE ID ='+str(sigID)+';'
self._execute_n_commit(sql)
@@ -1360,9 +1360,9 @@ def _SQLplotNewData(self, x, y, dataunit, devicename, signalname, createCallback
logging.warning('autoResize is DEPRECATED for postgresql')
if hold == 'on':
sql = 'UPDATE '+SIGNAL_TABLE_NAME + \
- ' SET X = array_cat(X, '+str(x)+') WHERE ID ='+str(sigID)+';'
+ ' SET X = array_cat(X, '+str(x)+'::NUMERIC[]) WHERE ID ='+str(sigID)+';'
sql += '\nUPDATE '+SIGNAL_TABLE_NAME + \
- ' SET Y = array_cat(Y,'+str(y)+') WHERE ID ='+str(sigID)+';'
+ ' SET Y = array_cat(Y,'+str(y)+'::NUMERIC[]) WHERE ID ='+str(sigID)+';'
sql += '\nUPDATE '+SIGNAL_TABLE_NAME+' SET UNIT = \'' + \
str(dataunit)+'\' WHERE ID ='+str(sigID)+';'
elif hold == 'mergeX':
diff --git a/RTOC/RTLogger/__main__.py b/RTOC/RTLogger/__main__.py
index bd8be56..10892b9 100644
--- a/RTOC/RTLogger/__main__.py
+++ b/RTOC/RTLogger/__main__.py
@@ -24,6 +24,37 @@
__package__ = "RTOC.RTLogger"
__main__ = __name__
+try:
+ from PyQt5 import QtCore
+
+ app = QtCore.QCoreApplication(sys.argv)
+ # from PyQt5 import QtWidgets
+ userpath = os.path.expanduser('~/.RTOC')
+ if os.path.exists(userpath+"/config.json"):
+ try:
+ with open(userpath+"/config.json", encoding="UTF-8") as jsonfile:
+ config = json.load(jsonfile, encoding="UTF-8")
+ except Exception:
+ logging.debug(traceback.format_exc())
+ config = {'global': {'language': 'en'}}
+ else:
+ config = {'global': {'language': 'en'}}
+ if config['global']['language'] == 'de':
+ translator = QtCore.QTranslator()
+ if getattr(sys, 'frozen', False):
+ # frozen
+ packagedir = os.path.dirname(sys.executable)
+ else:
+ # unfrozen
+ packagedir = os.path.dirname(os.path.realpath(__file__))
+ translator.load(packagedir+"/RTOC/locales/de_de.qm")
+ app.installTranslator(translator)
+ # QtCore.QCoreApplication.installTranslator(translator)
+ print('German language selected')
+except (ImportError, SystemError):
+ logging.warning('Cannot set language')
+
+
class RTOCDaemon(Daemon):
def __init__(self, pidfile, port=5050):
@@ -56,7 +87,7 @@ def main():
'RTOC.RTLogger [-h, -s, -l, -w]\n -h: Hilfe\n-s (--server) [COMMAND]: TCP-Server ohne GUI\n\t- start: Starts the RTOC-daemon\n\t- stop: Stops the RTOC-daemon\n\t- restart: Restarts the RTOC-daemon\n-w Startet RTLogger mit Website\n-p (--port): Starte TCP-Server auf anderem Port (Standart: 5050)\n-c (--config [OPTION=value]): Configure RTOC, type "-c list" to see all options')
sys.exit(0)
elif opt == '-v':
- logging.info("2.1.0")
+ logging.info("2.1.1")
elif opt in ('-s', '--server'):
if os.name == 'nt':
logging.info(
diff --git a/RTOC/RTLogger/plugins/Generator.py b/RTOC/RTLogger/plugins/Generator.py
index 2009cf9..762ddb1 100644
--- a/RTOC/RTLogger/plugins/Generator.py
+++ b/RTOC/RTLogger/plugins/Generator.py
@@ -12,9 +12,9 @@
class Plugin(LoggerPlugin):
- def __init__(self, stream=None, plot=None, event=None):
+ def __init__(self, *args, **kwargs):
# Plugin setup
- super(Plugin, self).__init__(stream, plot, event)
+ super(Plugin, self).__init__(*args, **kwargs)
self.setDeviceName(devicename)
self.smallGUI = True
diff --git a/RTOC/RTLogger/telegramBot.py b/RTOC/RTLogger/telegramBot.py
index 8922b7c..9aef06f 100644
--- a/RTOC/RTLogger/telegramBot.py
+++ b/RTOC/RTLogger/telegramBot.py
@@ -189,13 +189,26 @@ def sendEvent(self, message, devicename, signalname, priority):
def send_message_to_all(self, message, onlyAdmin=False):
for id in self.telegram_clients.keys():
- if onlyAdmin and self.telegram_clients[id]['permission'] == 'admin':
+ if (onlyAdmin and self.telegram_clients[id]['permission'] == 'admin') or not onlyAdmin:
self.send_message(chat_id=int(id), text=message, delete=False)
- # try:
- # self.bot.send_message(chat_id=int(id), text=message,
- # parse_mode=ParseMode.MARKDOWN)
- # except Exception:
- # self.bot.send_message(chat_id=int(id), text=message)
+
+ def send_photo(self, path, onlyAdmin=False):
+ try:
+ for id in self.telegram_clients.keys():
+ if (onlyAdmin and self.telegram_clients[id]['permission'] == 'admin') or not onlyAdmin:
+ self.bot.send_photo(chat_id=int(id), photo=open(path, 'rb'))
+ except Exception as error:
+ text = translate('RTOC', 'Error while sending photo:\n{}').format(error)
+ self.send_message_to_all(text, onlyAdmin)
+
+ def send_document(self, path, onlyAdmin=False):
+ try:
+ for id in self.telegram_clients.keys():
+ if (onlyAdmin and self.telegram_clients[id]['permission'] == 'admin') or not onlyAdmin:
+ self.bot.send_document(chat_id=int(id), document=open(path, 'rb'))
+ except Exception as error:
+ text = translate('RTOC', 'Error while sending file:\n{}').format(error)
+ self.send_message_to_all(text, onlyAdmin)
def connect(self):
idler = Thread(target=self.connectThread)
@@ -637,7 +650,7 @@ def deviceFunctionsHandler(self, bot, chat_id):
name = self.current_plugin[chat_id]
commands = []
for fun in self.logger.pluginFunctions.keys():
- hiddenFuncs = ["loadGUI", "updateT", "stream", "plot", "event", "createTCPClient", "sendTCP", "close", "cancel", "start", "setSamplerate","setDeviceName",'setPerpetualTimer','setInterval','getDir']
+ hiddenFuncs = ["loadGUI", "updateT", "stream", "plot", "event", "createTCPClient", "sendTCP", "close", "cancel", "start", "setSamplerate","setDeviceName",'setPerpetualTimer','setInterval','getDir','telegram_send_message', 'telegram_send_photo', 'telegram_send_document']
hiddenFuncs = [name+'.'+i for i in hiddenFuncs]
if fun.startswith(name+".") and fun not in hiddenFuncs:
parStr = ', '.join(self.logger.pluginFunctions[fun][1])
diff --git a/RTOC/RTOC.py b/RTOC/RTOC.py
index 91db735..35fbb06 100644
--- a/RTOC/RTOC.py
+++ b/RTOC/RTOC.py
@@ -38,7 +38,7 @@
if os.name == 'nt':
import ctypes
- myappid = 'RTOC.2.1.0' # arbitrary string
+ myappid = 'RTOC.2.1.1' # arbitrary string
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
try:
diff --git a/RTOC/RTOC_GUI/Actions.py b/RTOC/RTOC_GUI/Actions.py
index 4d8f277..5068c73 100644
--- a/RTOC/RTOC_GUI/Actions.py
+++ b/RTOC/RTOC_GUI/Actions.py
@@ -62,6 +62,7 @@ def connectButtons(self):
self.scriptWidgetToggle.triggered.connect(self.toggleScriptWidget)
self.eventWidgetToggle.triggered.connect(self.toggleEventWidget)
self.deviceRAWWidgetToggle.triggered.connect(self.toggleRAWWidget)
+ self.refreshDevicesButton.clicked.connect(self.reloadDevices)
self.actionDeutsch.triggered.connect(
partial(self.toggleLanguage, "de", self.actionDeutsch, [self.actionEnglish], False))
@@ -578,9 +579,9 @@ def connectNewHost(self):
return connected
return False
- def addRemoteHostWidget(self, host, name):
+ def addRemoteHostWidget(self, host, name, port):
remoteHostWidget = QtWidgets.QDockWidget(name, self)
- widget = RemoteWidget(self, host, remoteHostWidget, name)
+ widget = RemoteWidget(self, host, remoteHostWidget, name, port)
remoteHostWidget.setWidget(widget)
self.remoteHostWidgets.append(remoteHostWidget)
self.tabifyDockWidget(self.graphWidget, self.remoteHostWidgets[-1])
@@ -621,15 +622,15 @@ def connectHost(self, host, name, password=''):
self.logger.remote.connect(host, port, name, password)
retry = True
host = hostsplit[0]
- self.logger.remote.getConnection(host).tcppassword = password
+ self.logger.remote.getConnection(host, port).tcppassword = password
while retry:
retry = False
- status = self.logger.remote.getConnection(host).status
+ status = self.logger.remote.getConnection(host, port).status
if status == "protected":
text, ok2 = pyqtlib.text_message(None, translate('RTOC', 'Password'), translate('RTOC',
"The RTOC server {} is password-protected. Please enter your password.").format(hostname), translate('RTOC', 'TCP-Password'))
if ok2:
- self.logger.remote.getConnection(host).tcppassword = text
+ self.logger.remote.getConnection(host, port).tcppassword = text
self.logger.remote.connect(host, port, name, text)
retry = True
ok3 = pyqtlib.alert_message(translate('RTOC', 'Save password'), translate('RTOC',
@@ -640,12 +641,12 @@ def connectHost(self, host, name, password=''):
pyqtlib.info_message(translate('RTOC', 'Connection established'), translate('RTOC',
'Connection to {} on port {} established.').format(host, port), '')
- self.addRemoteHostWidget(host, name)
+ self.addRemoteHostWidget(host, name, port)
return True
elif status == "wrongPassword":
text, ok = pyqtlib.text_message(None, translate('RTOC', 'Protected'), translate('RTOC', 'Connection to {} on port {} not established').format(host, port), translate('RTOC', 'Password is wrong.'))
if ok:
- self.logger.remote.getConnection(host).tcppassword = text
+ self.logger.remote.getConnection(host, port).tcppassword = text
self.logger.remote.connect(host, port, name, text)
retry = True
ok3 = pyqtlib.alert_message(translate('RTOC', 'Save password'), translate('RTOC',
@@ -662,7 +663,7 @@ def connectHost(self, host, name, password=''):
pyqtlib.info_message(translate('RTOC', 'Connection established'), translate('RTOC',
'Connection to {} on port {} established.').format(host, port), '')
- self.addRemoteHostWidget(host, name)
+ self.addRemoteHostWidget(host, name, port)
return True
else:
print(status)
diff --git a/RTOC/RTOC_GUI/remoteWidget.py b/RTOC/RTOC_GUI/remoteWidget.py
index cd38205..64ae514 100644
--- a/RTOC/RTOC_GUI/remoteWidget.py
+++ b/RTOC/RTOC_GUI/remoteWidget.py
@@ -27,7 +27,7 @@ def _(text):
class RemoteWidget(QtWidgets.QWidget):
update = QtCore.pyqtSignal()
- def __init__(self, selfself, remotehost="", parent=None, name="Remote"):
+ def __init__(self, selfself, remotehost="", parent=None, name="Remote", port=5050):
super(RemoteWidget, self).__init__()
if getattr(sys, 'frozen', False):
# frozen
@@ -44,6 +44,7 @@ def __init__(self, selfself, remotehost="", parent=None, name="Remote"):
self.hostname = remotehost
self.parent = parent
self.name = name
+ self.port = port
self.listWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.listWidget.customContextMenuRequested.connect(self.listItemRightClicked)
@@ -54,7 +55,7 @@ def __init__(self, selfself, remotehost="", parent=None, name="Remote"):
self.clearButton.clicked.connect(self.clear)
self.pauseButton.clicked.connect(self.pause)
self.saveButton.clicked.connect(self.saveRemoteSession)
- self.remote = self.self.logger.remote.getConnection(self.hostname)
+ self.remote = self.self.logger.remote.getConnection(self.hostname, self.port)
self.remote.updateRemoteCallback = self.update.emit
self.update.connect(self.updateRemote)
if self.remote is not None:
@@ -114,7 +115,7 @@ def disconnect(self):
else:
ans = pyqtlib.alert_message(translate('RTOC', 'Disconnect'), translate('RTOC', 'Do you want to disconnect {}?').format(self.hostname), translate('RTOC', 'Transferred signals will remain.'), "", translate('RTOC', "Yes"), translate('RTOC', "No"))
if ans:
- ans = self.self.logger.remote.disconnect(self.hostname)
+ ans = self.self.logger.remote.disconnect(self.hostname, self.port)
if ans is False:
ans = pyqtlib.info_message(translate('RTOC', 'Error'), translate('RTOC', 'Could not disconnect from {}.').format(self.hostname), '')
self.close()
diff --git a/RTOC/RTOC_GUI/signalWidget.py b/RTOC/RTOC_GUI/signalWidget.py
index 3221218..c95b619 100644
--- a/RTOC/RTOC_GUI/signalWidget.py
+++ b/RTOC/RTOC_GUI/signalWidget.py
@@ -459,29 +459,33 @@ def createToolTip(self, id, xdata):
maxduration = self.calcDuration(list(xdata))
duration = xdata[-1]-xdata[0]
try:
- if self.logger.config['postgresql']['active']:
- line1 = str(timedelta(seconds=duration))
- line2 = str(len(list(xdata)))
- else:
- line1 = str(timedelta(seconds=duration)) + '/ ~ ' + \
- str(timedelta(seconds=maxduration))
- line2 = str(
- len(list(xdata)))+"/"+str(self.logger.config['global']['recordLength'])
+ # if self.logger.config['postgresql']['active']:
+ # line1 = translate('RTOC', 'Duration: ')+str(timedelta(seconds=duration))
+ # line2 = translate('RTOC', 'Values: ')+str(len(list(xdata)))
+ # else:
+ line1 = translate('RTOC', 'Duration: {}/~{}').format(str(timedelta(seconds=round(duration))), str(timedelta(seconds=round(maxduration))))
+ line2 = translate('RTOC', 'Values: {}/{}').format(len(list(xdata)), str(self.logger.config['global']['recordLength']))
count = 20
if len(xdata) <= count:
count = len(xdata)
if count > 1:
meaner = list(xdata)[-count:]
diff = 0
- for idx, m in enumerate(meaner[:-1]):
- diff += meaner[idx+1]-m
+ # for idx, m in enumerate(meaner[:-1]):
+ # diff += meaner[idx+1]-m
+ diff = meaner[-1]-meaner[0]
+ diff2 = xdata[-1]-xdata[0]
if diff != 0:
- line3 = str(round((len(meaner)-1)/diff, 2))+" Hz"
+ latestSamplerate = str(round((len(meaner)-1)/diff, 2))
+ samplerate = str(round((len(xdata)-1)/diff2, 2))
else:
- line3 = "? Hz"
+ latestSamplerate = "?"
+ samplerate = "?"
else:
- line3 = "? Hz"
- return line1+"\n"+line2 + "\n" + line3
+ latestSamplerate = "?"
+ samplerate = "?"
+ line3 = translate('RTOC', 'Samplerate (latest): {} ({}) Hz').format(samplerate, latestSamplerate)
+ return self.devicename+'.'+self.signalname+'\n'+line1+"\n"+line2 + "\n" + line3
except Exception:
logging.debug(traceback.format_exc())
print(traceback.format_exc())
diff --git a/RTOC/RTOC_GUI/ui/rtoc.ui b/RTOC/RTOC_GUI/ui/rtoc.ui
index ce4f78c..3dfe072 100644
--- a/RTOC/RTOC_GUI/ui/rtoc.ui
+++ b/RTOC/RTOC_GUI/ui/rtoc.ui
@@ -10,6 +10,12 @@
664
+
+
+ 0
+ 0
+
+
RealTime OpenControl
@@ -88,20 +94,44 @@
0
-
-
-
-
- 0
- 0
-
-
-
- Search devices
-
-
- true
+
+
+ 0
-
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Search devices
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ icons/blinking.pngicons/blinking.png
+
+
+
+
-
@@ -126,7 +156,7 @@
0
0
398
- 97
+ 88
@@ -192,7 +222,7 @@
-
+
false
@@ -403,10 +433,10 @@
-
-
+
-
+
Values
@@ -606,7 +636,7 @@
-
+
diff --git a/RTOC/Template.py b/RTOC/Template.py
index 27e5a5d..1a7036f 100644
--- a/RTOC/Template.py
+++ b/RTOC/Template.py
@@ -1,7 +1,7 @@
"""
This template shows, how to implement plugins in RTOC
-RTOC version 2.1.0
+RTOC version 2.0
A plugin needs to import RTOC.LoggerPlugin to be recognized by RTOC.
"""
@@ -26,8 +26,8 @@
class Plugin(LoggerPlugin):
- def __init__(self, stream=None, plot=None, event=None):
- super(Plugin, self).__init__(stream, plot, event)
+ def __init__(self, *args, **kwargs):
+ super(Plugin, self).__init__(*args, **kwargs)
"""Call this to initialize RTOC.LoggerPlugin"""
self.setDeviceName(DEVICENAME)
@@ -43,13 +43,10 @@ def __init__(self, stream=None, plot=None, event=None):
self.setPerpetualTimer(self._updateT, samplerate=SAMPLERATE)
"""You will need to collect data periodically in many applications. You need to start that in a seperate thread.
- RTOC provides a simple way to start a repeated thread with :py:meth:`.LoggerPlugin.setPerpetualTimer`.
- The first parameter is the function, which collects data and sends it to RTOC.
+ RTOC provides a simple way to start a repeated thread with :py:meth:`.LoggerPlugin.setPerpetualTimer`. The first parameter is the function, which collects data and sends it to RTOC. You can define a ``samplerate`` or an ``interval`` to set the samplerate.
- You can define a ``samplerate`` or an ``interval`` to set the samplerate.
- You can still use normal threads to do the same thing, but in this way, the plugin can be stopped properly.
- If you are using normal threads, make sure, to have a loop limited by '
+ You can still use normal threads to do the same thing, but in this way, the plugin can be stopped properly. If you are using normal threads, make sure, to have a loop limited by '
``self.run`` with ``while self.run:``.
"""
@@ -105,8 +102,27 @@ def loadGUI(self):
"""
This example will load a QWidget designed with QDesigner
"""
+ self.widget.teleMessageButton.clicked.connect(self._teleMessageAction)
+ self.widget.telePhotoButton.clicked.connect(self._telePhotoAction)
+ self.widget.teleFileButton.clicked.connect(self._teleFileAction)
+ """
+ Connect GUI-buttons with python-functions
+ """
return self.widget # This function needs to return a QWidget
+ def _teleMessageAction(self):
+ text = 'Hello world!'
+ self.telegram_send_message(text, onlyAdmin=False)
+
+ def _telePhotoAction(self):
+ path = self.getDir(__file__)+'/examplePhoto.png'
+ self.telegram_send_photo(path, onlyAdmin=False)
+
+ def _teleFileAction(self):
+ path = self.getDir(__file__)+'/examplePhoto.png'
+ self.telegram_send_document(path, onlyAdmin=False)
+
+
hasGUI = True # If your plugin has a widget do this
diff --git a/RTOC/__init__.py b/RTOC/__init__.py
index 75ad07c..eaaab0f 100644
--- a/RTOC/__init__.py
+++ b/RTOC/__init__.py
@@ -12,7 +12,7 @@
# __package__ = "RTOC"
# __main__ = __name__
name = "RTOC"
-__version__ = "2.1.0"
+__version__ = "2.1.1"
def main():
@@ -149,35 +149,35 @@ def setStyleSheet(app, myapp):
return app, myapp
-def setLanguage(app):
-
- import gettext
- from PyQt5 import QtCore
- # from PyQt5 import QtWidgets
- userpath = os.path.expanduser('~/.RTOC')
- if os.path.exists(userpath+"/config.json"):
- try:
- with open(userpath+"/config.json", encoding="UTF-8") as jsonfile:
- config = json.load(jsonfile, encoding="UTF-8")
- except Exception:
- logging.debug(traceback.format_exc())
- config = {'global': {'language': 'en'}}
- else:
- config = {'global': {'language': 'en'}}
- if config['global']['language'] == 'de':
- translator = QtCore.QTranslator()
- if getattr(sys, 'frozen', False):
- # frozen
- packagedir = os.path.dirname(sys.executable)
- else:
- # unfrozen
- packagedir = os.path.dirname(os.path.realpath(__file__))
- translator.load(packagedir+"/locales/de_de.qm")
- app.installTranslator(translator)
-
- el = gettext.translation('base', localedir='locales', languages=['de'])
- el.install()
- _ = el.gettext
+# def setLanguage(app):
+#
+# import gettext
+# from PyQt5 import QtCore
+# # from PyQt5 import QtWidgets
+# userpath = os.path.expanduser('~/.RTOC')
+# if os.path.exists(userpath+"/config.json"):
+# try:
+# with open(userpath+"/config.json", encoding="UTF-8") as jsonfile:
+# config = json.load(jsonfile, encoding="UTF-8")
+# except Exception:
+# logging.debug(traceback.format_exc())
+# config = {'global': {'language': 'en'}}
+# else:
+# config = {'global': {'language': 'en'}}
+# if config['global']['language'] == 'de':
+# translator = QtCore.QTranslator()
+# if getattr(sys, 'frozen', False):
+# # frozen
+# packagedir = os.path.dirname(sys.executable)
+# else:
+# # unfrozen
+# packagedir = os.path.dirname(os.path.realpath(__file__))
+# translator.load(packagedir+"/locales/de_de.qm")
+# app.installTranslator(translator)
+
+ # el = gettext.translation('base', localedir='locales', languages=['de'])
+ # el.install()
+ # _ = el.gettext
# more info here: http://kuanyui.github.io/2014/09/03/pyqt-i18n/
# generate translationfile: % pylupdate5 RTOC.py -ts lang/de_de.ts
# compile translationfile: % lrelease-qt5 lang/de_de.ts
@@ -203,7 +203,31 @@ def startRemoteRTOC(remotepath):
app = QtWidgets.QApplication(sys.argv)
- app = setLanguage(app)
+ # app = setLanguage(app)
+ from PyQt5 import QtCore
+ # from PyQt5 import QtWidgets
+ userpath = os.path.expanduser('~/.RTOC')
+ if os.path.exists(userpath+"/config.json"):
+ try:
+ with open(userpath+"/config.json", encoding="UTF-8") as jsonfile:
+ config = json.load(jsonfile, encoding="UTF-8")
+ except Exception:
+ logging.debug(traceback.format_exc())
+ config = {'global': {'language': 'en'}}
+ else:
+ config = {'global': {'language': 'en'}}
+ if config['global']['language'] == 'de':
+ translator = QtCore.QTranslator()
+ if getattr(sys, 'frozen', False):
+ # frozen
+ packagedir = os.path.dirname(sys.executable)
+ else:
+ # unfrozen
+ packagedir = os.path.dirname(os.path.realpath(__file__))
+ translator.load(packagedir+"/locales/de_de.qm")
+ app.installTranslator(translator)
+
+
myapp = RTOC(False)
myapp.config['tcp']['active'] = True
@@ -226,7 +250,31 @@ def startRTOC(tcp=None, port=None, local =False):
app = QtWidgets.QApplication(sys.argv)
- app = setLanguage(app)
+ # app = setLanguage(app)
+ from PyQt5 import QtCore
+ # from PyQt5 import QtWidgets
+ userpath = os.path.expanduser('~/.RTOC')
+ if os.path.exists(userpath+"/config.json"):
+ try:
+ with open(userpath+"/config.json", encoding="UTF-8") as jsonfile:
+ config = json.load(jsonfile, encoding="UTF-8")
+ except Exception:
+ logging.debug(traceback.format_exc())
+ config = {'global': {'language': 'en'}}
+ else:
+ config = {'global': {'language': 'en'}}
+ if config['global']['language'] == 'de':
+ translator = QtCore.QTranslator()
+ if getattr(sys, 'frozen', False):
+ # frozen
+ packagedir = os.path.dirname(sys.executable)
+ else:
+ # unfrozen
+ packagedir = os.path.dirname(os.path.realpath(__file__))
+ translator.load(packagedir+"/locales/de_de.qm")
+ app.installTranslator(translator)
+
+
myapp = RTOC(tcp, port, local)
app, myapp = setStyleSheet(app, myapp)
diff --git a/RTOC/__main__.py b/RTOC/__main__.py
index 31aa743..3ee51b9 100644
--- a/RTOC/__main__.py
+++ b/RTOC/__main__.py
@@ -44,7 +44,7 @@ def main():
'RTOC.py [-h] [-r ]\n -h: Help\n-r (--remote) : TCP client for RTOC server\nFor options without GUI, run "python3 -m RTOC.RTLogger -h"')
sys.exit(0)
elif opt == '-v':
- logging.info("2.1.0")
+ logging.info("2.1.1")
elif opt in ("-r", "--remote"):
remotepath = arg
startRemoteRTOC(remotepath)
diff --git a/RTOC/jsonsocket.py b/RTOC/jsonsocket.py
index 0520e7f..251b5ea 100644
--- a/RTOC/jsonsocket.py
+++ b/RTOC/jsonsocket.py
@@ -87,7 +87,7 @@ class Server(object):
reuse_port (bool): Enable/disable reuse_port (default: True)
"""
- def __init__(self, host, port, keyword=None, reuse_port=True):
+ def __init__(self, host, port, keyword=None, reuse_port=True, timeout=5):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client = None
@@ -100,7 +100,7 @@ def __init__(self, host, port, keyword=None, reuse_port=True):
# self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 5 * 60)
# self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 10)
# self.socket.setblocking(0)
- self.socket.settimeout(5.0)
+ self.socket.settimeout(timeout)
self.socket.bind((host, port))
self.socket.listen(BACKLOG)
@@ -201,7 +201,7 @@ def setKeyword(self, keyword=None):
# def __del__(self):
# self.close()
- def connect(self, host, port, keyword=None, reuse_port=True):
+ def connect(self, host, port, keyword=None, reuse_port=True, timeout=5):
"""
Establish a connection to a host (server)
@@ -218,7 +218,7 @@ def connect(self, host, port, keyword=None, reuse_port=True):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
# self.socket.setblocking(0)
- self.socket.settimeout(5.0)
+ self.socket.settimeout(timeout)
self.socket.connect((host, port))
self.keyword = keyword
self.host = host
diff --git a/docs/PLUGINS.rst b/docs/PLUGINS.rst
index 9f08244..7a356bb 100644
--- a/docs/PLUGINS.rst
+++ b/docs/PLUGINS.rst
@@ -13,8 +13,8 @@ Plugins are written in Python3 and need to follow some rules::
from LoggerPlugin import LoggerPlugin # contains all plugin-functions
class Plugin(LoggerPlugin): # This must be the main class of your function
- def __init__(self, stream=None, plot= None, event=None):
- super(Plugin, self).__init__(stream, plot, event))
+ def __init__(self, *args, **kwargs):
+ super(Plugin, self).__init__(*args, **kwargs))
# start your code here
diff --git a/loggerServer.py b/loggerServer.py
index c65aa9f..4cb0cfe 100644
--- a/loggerServer.py
+++ b/loggerServer.py
@@ -1,19 +1,53 @@
#!/usr/bin/python3 -u
import sys
+import os
+import json
+import logging
+import traceback
-#sys.path.insert(0,'/home/pi/kellerlogger/')
+# sys.path.insert(0,'/home/pi/kellerlogger/')
from RTOC.RTLogger.RTLogger import RTLogger
+try:
+ from PyQt5 import QtCore
+
+ app = QtCore.QCoreApplication(sys.argv)
+ # from PyQt5 import QtWidgets
+ userpath = os.path.expanduser('~/.RTOC')
+ if os.path.exists(userpath+"/config.json"):
+ try:
+ with open(userpath+"/config.json", encoding="UTF-8") as jsonfile:
+ config = json.load(jsonfile, encoding="UTF-8")
+ except Exception:
+ logging.debug(traceback.format_exc())
+ config = {'global': {'language': 'en'}}
+ else:
+ config = {'global': {'language': 'en'}}
+ if config['global']['language'] == 'de':
+ translator = QtCore.QTranslator()
+ if getattr(sys, 'frozen', False):
+ # frozen
+ packagedir = os.path.dirname(sys.executable)
+ else:
+ # unfrozen
+ packagedir = os.path.dirname(os.path.realpath(__file__))
+ translator.load(packagedir+"/RTOC/locales/de_de.qm")
+ app.installTranslator(translator)
+ # QtCore.QCoreApplication.installTranslator(translator)
+ print('German language selected')
+except (ImportError, SystemError):
+ logging.warning('Cannot set language')
+
logger = RTLogger(True)
try:
- #logger.startPlugin('Heliotherm')
- #logger.getPlugin('Heliotherm').start('192.168.178.72')
- #logger.startPlugin('Futtertrocknung')
- a=logger.getThread()
- if a is not None:
- a.join()
+ # logger.startPlugin('Heliotherm')
+ # logger.getPlugin('Heliotherm').start('192.168.178.72')
+ # logger.startPlugin('Futtertrocknung')
+ a = logger.getThread()
+ if a is not None:
+ a.join()
except KeyboardInterrupt:
- logger.stop()
- print("LoggerServer stopped by user")
+ logger.stop()
+ print("LoggerServer stopped by user")
diff --git a/loggerWebServer.py b/loggerWebServer.py
index 9700762..e3838b0 100644
--- a/loggerWebServer.py
+++ b/loggerWebServer.py
@@ -2,15 +2,50 @@
import sys
-#sys.path.insert(0,'/home/pi/kellerlogger/')
+import os
+import json
+import logging
+import traceback
+
+# sys.path.insert(0,'/home/pi/kellerlogger/')
from RTOC.RTLogger import RTOC_Web_standalone
try:
- RTOC_Web_standalone.start(debug=False)
- # a=logger.getThread()
- # if a is not None:
- # a.join()
+ from PyQt5 import QtCore
+
+ app = QtCore.QCoreApplication(sys.argv)
+ # from PyQt5 import QtWidgets
+ userpath = os.path.expanduser('~/.RTOC')
+ if os.path.exists(userpath+"/config.json"):
+ try:
+ with open(userpath+"/config.json", encoding="UTF-8") as jsonfile:
+ config = json.load(jsonfile, encoding="UTF-8")
+ except Exception:
+ logging.debug(traceback.format_exc())
+ config = {'global': {'language': 'en'}}
+ else:
+ config = {'global': {'language': 'en'}}
+ if config['global']['language'] == 'de':
+ translator = QtCore.QTranslator()
+ if getattr(sys, 'frozen', False):
+ # frozen
+ packagedir = os.path.dirname(sys.executable)
+ else:
+ # unfrozen
+ packagedir = os.path.dirname(os.path.realpath(__file__))
+ translator.load(packagedir+"/RTOC/locales/de_de.qm")
+ app.installTranslator(translator)
+ QtCore.QCoreApplication.installTranslator(translator)
+ print('German language selected')
+except (ImportError, SystemError):
+ logging.warning('Cannot set language')
+
+try:
+ RTOC_Web_standalone.start(debug=False)
+ # a=logger.getThread()
+ # if a is not None:
+ # a.join()
except KeyboardInterrupt:
- #logger.close()
- print("LoggerWebServer stopped by user")
+ # logger.close()
+ print("LoggerWebServer stopped by user")
diff --git a/setup.py b/setup.py
index f361aea..7dc28d5 100644
--- a/setup.py
+++ b/setup.py
@@ -78,9 +78,9 @@
path = os.path.split(__file__)[0]
# sys.path.insert(0, os.path.join(path, 'tools'))
-version = "2.1.0"
-forcedVersion = "2.1.0"
-gitVersion = "2.1.0"
+version = "2.1.1"
+forcedVersion = "2.1.1"
+gitVersion = "2.1.1"
initVersion = 1.0
diff --git a/setupStandalone.py b/setupStandalone.py
index 00be436..27cfd7b 100644
--- a/setupStandalone.py
+++ b/setupStandalone.py
@@ -20,7 +20,7 @@
setup(
name='RealTimeOpenControl',
- version = '2.1.0',
+ version = '2.1.1',
description = 'RTOC',
options = dict(build_exe = buildOptions),
executables = executables