Skip to content

Commit fa8a7e0

Browse files
author
Sebastian
committed
release 2.1.1
- added plugin-functions to send telegram messages, photos and files - PLUGIN __INIT__ changed!!! Please review the examples - Added more info in signal-tooltip (GUI) - GUI signal-list looks nicer now - Possibility to select timerange for Remote-Signals. - Increased remote-TCP-timeout to 20s due to slow database on some devices (raspberrypi)
1 parent b65a86f commit fa8a7e0

25 files changed

+418
-154
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
__pycache__/
22
RTOC/__pycache__/
33
RTOC/RTLogger/__pycache__/
4+
RTOC/RTLogger/plugins/test.py
45
RTOC/RTLogger/plugins/__pycache__/
56
RTOC/RTOC_GUI/__pycache__/
67
RTOC/lib/__pycache__/

RTOC.egg-info/PKG-INFO

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
Metadata-Version: 2.1
22
Name: RTOC
3-
Version: 2.1.0
3+
Version: 2.1.1
44
Summary: RealTime OpenControl
55
Home-page: https://github.com/Haschtl/RealTimeOpenControl
66
Author: Sebastian Keller
77
Author-email: [email protected]
88
License: GNU
99
Description: # RealTime OpenControl (RTOC)
1010

11-
| [![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/) |
11+
| [![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/) |
1212

1313

14-
### Version 2.1.0
14+
### Version 2.1.1
1515
![Usecase](screenshots/RTOC-schematik.png)
1616
[Documentation](https://realtimeopencontrol.readthedocs.io/en/latest/)
1717

@@ -160,6 +160,6 @@ Classifier: Topic :: Software Development :: User Interfaces
160160
Requires-Python: >=3
161161
Description-Content-Type: text/markdown
162162
Provides-Extra: Telegram
163-
Provides-Extra: ALL
164163
Provides-Extra: GUI
165164
Provides-Extra: Webserver
165+
Provides-Extra: ALL

RTOC/LoggerPlugin.py

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# LoggerPlugin v2.5
1+
# LoggerPlugin v2.6
22
import traceback
33
import time
44
import sys
@@ -40,18 +40,20 @@ def __init__(self, stream=None, plot=None, event=None):
4040
event (method): The callback-method for the event-method
4141
4242
"""
43-
def __init__(self, stream=None, plot=None, event=None):
43+
def __init__(self, stream=None, plot=None, event=None, telegramBot=None, *args, **kwargs):
4444
# Plugin setup
4545
# self.setDeviceName()
4646
self._deviceName = "noDevice"
4747
self._cb = stream
4848
self._ev = event
4949
self._plt = plot
50+
self._bot = telegramBot
5051
self._sock = None
5152
self._tcppassword = ''
5253
self._tcpport = 5050
5354
self._tcpaddress = ''
5455
self._tcpthread = False
56+
self.widget = None
5557
self._pluginThread = None
5658
self._oldPerpetualTimer = False
5759
self.lockPerpetialTimer = Lock()
@@ -159,12 +161,12 @@ def stream(self, y=[], snames=[], dname=None, unit=None, x=None, slist=None, sdi
159161
unit.append(sdict[dev][sig][1])
160162
x.append(now)
161163
else:
162-
logging.error('STREAM ERROR, signal has not this format: [y, "unit"]')
164+
logging.error('STREAM ERROR, signal {}.{} has not this format: [y, "unit"]'.format(dname, sig))
163165
else:
164-
logging.error('STREAM_ERROR: One signal was malformed')
166+
logging.error('STREAM_ERROR:signal {}.{} was malformed.'.format(dname, sig))
165167
self._cb(y=y, snames=snames, dname=dname, unit=unit, x=x)
166168
else:
167-
logging.error('STREAM_ERROR: One device was malformed')
169+
logging.error('STREAM_ERROR: device {} was malformed.'.format(dname))
168170
return True
169171
else:
170172
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
265267
self._tcppassword = password
266268
self._sock.setKeyword(password)
267269

268-
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):
270+
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):
269271
"""
270272
Use any of the arguments described in :doc:`TCP`.
271273
@@ -289,6 +291,7 @@ def sendTCP(self, y=None, sname=None, dname=None, unit=None, x=None, getSignal=N
289291
getEventList: :py:meth:`.NetworkFunctions.getEventList`
290292
getPluginList: :py:meth:`.NetworkFunctions.getPluginList`
291293
getSession: :py:meth:`.RT_data.generateSessionJSON`
294+
timeout: TCP-Client Timeout (Default: 5s)
292295
293296
Returns:
294297
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
297300
298301
"""
299302
if self._tcpthread:
300-
t = Thread(target=self._sendTCP, args=(y, sname, dname, unit, x, getSignal, getLatest, getEvent, getSignalList, getEventList, getPluginList, getSession, plot, event, remove, plugin, logger, stream,))
303+
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))
301304
t.start()
302305
else:
303-
return self._sendTCP(y, sname, dname, unit, x, getSignal, getLatest, getEvent, getSignalList, getEventList, getPluginList, getSession, plot, event, remove, plugin, logger, stream)
306+
return self._sendTCP(y, sname, dname, unit, x, getSignal, getLatest, getEvent, getSignalList, getEventList, getPluginList, getSession, plot, event, remove, plugin, logger, stream, timeout)
304307

305-
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):
308+
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):
306309
with lock:
307310

308311
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=
343346
# dicti['password'] = hex_dig
344347
if self._sock:
345348
try:
346-
self._sock.connect(self._tcpaddress, self._tcpport, self._tcppassword)
349+
self._sock.connect(self._tcpaddress, self._tcpport, self._tcppassword, timeout=timeout)
347350
self._sock.send(dicti)
348351
response = self._sock.recv()
349352
# self._sock.close()
@@ -540,6 +543,45 @@ def __updateT(self, func):
540543
func()
541544
diff = (time.time() - start_time)
542545

546+
def telegram_send_message(self, text, onlyAdmin=False):
547+
"""
548+
Sends a message to all clients (or only admins).
549+
550+
Args:
551+
text (str): Text to be send to the clients.
552+
onlyAdmin (bool): If True, only admins will get this message
553+
"""
554+
if self._bot is not None:
555+
self._bot.send_message_to_all(text, onlyAdmin)
556+
else:
557+
logging.warning('TelegramBot is not enabled or wrong configured! Can not send message "{}"'.format(text))
558+
559+
def telegram_send_photo(self, path, onlyAdmin=False):
560+
"""
561+
Sends the picture at a given path to all clients (or only admins).
562+
563+
Args:
564+
path (str): Path to the picture to send.
565+
onlyAdmin (bool): If True, only admins will get this message
566+
"""
567+
if self._bot is not None:
568+
self._bot.send_photo(path, onlyAdmin)
569+
else:
570+
logging.warning('TelegramBot is not enabled or wrong configured! Can not send photo "{}"'.format(path))
571+
572+
def telegram_send_document(self, path, onlyAdmin=False):
573+
"""
574+
Sends any document at a given path to all clients (or only admins).
575+
576+
Args:
577+
path (str): Path to the file to send.
578+
onlyAdmin (bool): If True, only admins will get this message
579+
"""
580+
if self._bot is not None:
581+
self._bot.send_document(path, onlyAdmin)
582+
else:
583+
logging.warning('TelegramBot is not enabled or wrong configured! Can not send file "{}"'.format(path))
584+
543585

544586

545587
class _perpetualTimer():
@@ -571,7 +613,8 @@ def _handle_function(self):
571613
timedelta = timedelta + self._correction
572614
if timedelta < 0:
573615
timedelta = 0
574-
if not self._lock.locked() and not self._cancel:
616+
if not self._cancel and not self._lock.locked():
617+
# with self._lock:
575618
self._thread = Timer(timedelta, self._handle_function)
576619
self.thread_counter += 1
577620
self._thread.start()

RTOC/RTLogger/DeviceFunctions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def startPlugin(self, name, remote=True):
8686
# if callback is None:
8787
self.pluginObjects[
8888
name] = importlib.import_module(
89-
fullname).Plugin(self.database.addDataCallback, self.database.plot, self.database.addNewEvent)
89+
fullname).Plugin(stream=self.database.addDataCallback, plot=self.database.plot, event=self.database.addNewEvent, telegramBot=self.telegramBot)
9090
# else:
9191
# self.pluginObjects[name] = importlib.import_module(
9292
# fullname).Plugin(callback, self.addNewEvent)

RTOC/RTLogger/NetworkFunctions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ def getPluginDict(self):
343343
dict[name]['parameters'] = []
344344
dict[name]['status'] = False
345345
for fun in self.pluginFunctions.keys():
346-
hiddenFuncs = ["loadGUI", "updateT", "stream", "plot", "event", "createTCPClient", "sendTCP", "close", "cancel", "start", "setSamplerate","setDeviceName",'setPerpetualTimer','setInterval','getDir']
346+
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']
347347

348348
if fun.startswith(name+".") and fun not in [name+'.'+i for i in hiddenFuncs]:
349349
dict[name]['functions'].append(fun.replace(name+".", ''))

RTOC/RTLogger/RTLogger.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,9 +347,9 @@ def _load_config(self):
347347
if key2 not in self.__config[key].keys():
348348
self.__config[key][key2] = defaultconfig[key][key2]
349349

350-
except Exception:
350+
except Exception as error:
351351
logging.debug(traceback.format_exc())
352-
logging.error('Error loading config.json')
352+
logging.error('Error loading config.json\n{}'.format(error))
353353
self.__config = _Config(defaultconfig)
354354
else:
355355
logging.warning('No config-file found.')

RTOC/RTLogger/RTRemote.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def getAllSignals(self):
9797
selection.append(".".join(i))
9898
for s in selection:
9999
ssplit = s.split('.')
100-
ans = self._sendTCP(getSignal={'dname':ssplit[0], 'sname':ssplit[1], 'xmin': self.xmin, 'xmax': self.xmax, 'maxN': self.maxN})
100+
ans = self._sendTCP(getSignal={'dname':ssplit[0], 'sname':ssplit[1], 'xmin': self.xmin, 'xmax': self.xmax, 'maxN': self.maxN}, timeout=20)
101101
if ans != False:
102102
if 'signals' in ans.keys():
103103
for sig in ans['signals'].keys():
@@ -280,15 +280,17 @@ def connect(self, hostname='127.0.0.1', port=5050, name='RemoteDevice', password
280280
self.connections.append(newConnection)
281281
self.getRemoteDeviceList()
282282

283-
def disconnect(self, hostname):
283+
def disconnect(self, hostname, port):
284284
if len(hostname.split(':')) == 2:
285285
hostname = hostname.split(':')[0]
286286

287+
index = -1
287288
for idx, c in enumerate(self.connections):
288-
if c.host == hostname:
289+
if c.host == hostname and c.port ==port:
289290
self.connections[idx].stop()
290-
self.connections.pop(idx)
291291
self.devices.pop(c.name)
292+
# self.devicenames.pop(c.name)
293+
# self.pluginStatus.pop(c.name)
292294
devs = []
293295
for dev in self.devicenames.keys():
294296
namesplit = dev.split(':')
@@ -299,12 +301,17 @@ def disconnect(self, hostname):
299301
self.pluginStatus.pop(dev)
300302
if self.logger.reloadDevicesCallback is not None:
301303
self.logger.reloadDevicesCallback()
302-
return True
303-
return False
304+
index = idx
305+
break
306+
if index != -1:
307+
self.connections.pop(index)
308+
return True
309+
else:
310+
return False
304311

305-
def getConnection(self, host):
312+
def getConnection(self, host, port):
306313
for idx, c in enumerate(self.connections):
307-
if host == c.host:
314+
if host == c.host and port == c.port:
308315
return self.connections[idx]
309316
return None
310317

RTOC/RTLogger/RT_data.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,9 +1128,9 @@ def _getSQLSignalUnit(self, sigID):
11281128

11291129
def _appendSQLSignal(self, sigID, x, y):
11301130
sql = 'UPDATE '+SIGNAL_TABLE_NAME + \
1131-
' SET X = array_cat(X, ARRAY'+str(list(x))+') WHERE ID ='+str(sigID)+';'
1131+
' SET X = array_cat(X, ARRAY'+str(list(x))+'::NUMERIC[]) WHERE ID ='+str(sigID)+';'
11321132
sql += '\nUPDATE '+SIGNAL_TABLE_NAME + \
1133-
' SET Y = array_cat(Y,ARRAY'+str(list(y))+') WHERE ID ='+str(sigID)+';'
1133+
' SET Y = array_cat(Y,ARRAY'+str(list(y))+'::NUMERIC[]) WHERE ID ='+str(sigID)+';'
11341134
# sql += '\nUPDATE '+SIGNAL_TABLE_NAME+' SET UNIT = \'' + \
11351135
# str(dataunit)+'\' WHERE ID ='+str(sigID)+';'
11361136
self._execute_n_commit(sql)
@@ -1360,9 +1360,9 @@ def _SQLplotNewData(self, x, y, dataunit, devicename, signalname, createCallback
13601360
logging.warning('autoResize is DEPRECATED for postgresql')
13611361
if hold == 'on':
13621362
sql = 'UPDATE '+SIGNAL_TABLE_NAME + \
1363-
' SET X = array_cat(X, '+str(x)+') WHERE ID ='+str(sigID)+';'
1363+
' SET X = array_cat(X, '+str(x)+'::NUMERIC[]) WHERE ID ='+str(sigID)+';'
13641364
sql += '\nUPDATE '+SIGNAL_TABLE_NAME + \
1365-
' SET Y = array_cat(Y,'+str(y)+') WHERE ID ='+str(sigID)+';'
1365+
' SET Y = array_cat(Y,'+str(y)+'::NUMERIC[]) WHERE ID ='+str(sigID)+';'
13661366
sql += '\nUPDATE '+SIGNAL_TABLE_NAME+' SET UNIT = \'' + \
13671367
str(dataunit)+'\' WHERE ID ='+str(sigID)+';'
13681368
elif hold == 'mergeX':

RTOC/RTLogger/__main__.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,37 @@
2424
__package__ = "RTOC.RTLogger"
2525
__main__ = __name__
2626

27+
try:
28+
from PyQt5 import QtCore
29+
30+
app = QtCore.QCoreApplication(sys.argv)
31+
# from PyQt5 import QtWidgets
32+
userpath = os.path.expanduser('~/.RTOC')
33+
if os.path.exists(userpath+"/config.json"):
34+
try:
35+
with open(userpath+"/config.json", encoding="UTF-8") as jsonfile:
36+
config = json.load(jsonfile, encoding="UTF-8")
37+
except Exception:
38+
logging.debug(traceback.format_exc())
39+
config = {'global': {'language': 'en'}}
40+
else:
41+
config = {'global': {'language': 'en'}}
42+
if config['global']['language'] == 'de':
43+
translator = QtCore.QTranslator()
44+
if getattr(sys, 'frozen', False):
45+
# frozen
46+
packagedir = os.path.dirname(sys.executable)
47+
else:
48+
# unfrozen
49+
packagedir = os.path.dirname(os.path.realpath(__file__))
50+
translator.load(packagedir+"/RTOC/locales/de_de.qm")
51+
app.installTranslator(translator)
52+
# QtCore.QCoreApplication.installTranslator(translator)
53+
print('German language selected')
54+
except (ImportError, SystemError):
55+
logging.warning('Cannot set language')
56+
57+
2758

2859
class RTOCDaemon(Daemon):
2960
def __init__(self, pidfile, port=5050):
@@ -56,7 +87,7 @@ def main():
5687
'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')
5788
sys.exit(0)
5889
elif opt == '-v':
59-
logging.info("2.1.0")
90+
logging.info("2.1.1")
6091
elif opt in ('-s', '--server'):
6192
if os.name == 'nt':
6293
logging.info(

RTOC/RTLogger/plugins/Generator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212

1313

1414
class Plugin(LoggerPlugin):
15-
def __init__(self, stream=None, plot=None, event=None):
15+
def __init__(self, *args, **kwargs):
1616
# Plugin setup
17-
super(Plugin, self).__init__(stream, plot, event)
17+
super(Plugin, self).__init__(*args, **kwargs)
1818
self.setDeviceName(devicename)
1919
self.smallGUI = True
2020

0 commit comments

Comments
 (0)