Skip to content

Commit fcc5066

Browse files
author
Sebastian
committed
release 2.1.8
various fixes (e.g. RTRemote) added customConfigPath adjusted imports for better compatibility (android)
1 parent 5cfe15d commit fcc5066

18 files changed

+138
-59
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# RealTime OpenControl (RTOC)
22

3-
| [![Documentation Status](https://readthedocs.org/projects/realtimeopencontrol/badge/?version=latest)](https://realtimeopencontrol.readthedocs.io/en/latest/) | [![Builds v2.0](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/) |
3+
| [![Documentation Status](https://readthedocs.org/projects/realtimeopencontrol/badge/?version=latest)](https://realtimeopencontrol.readthedocs.io/en/latest/) | [![Builds 2.1.8v](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/) |
44

55

6-
### Version 2.0
6+
### Version 2.1.8
77
![Usecase](screenshots/RTOC-schematik.png)
88
[Documentation](https://realtimeopencontrol.readthedocs.io/en/latest/)
99

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.1
3+
Version: 2.1.8
44
Summary: RealTime OpenControl
55
Home-page: https://github.com/Haschtl/RealTimeOpenControl
66
Author: Sebastian Keller
77
Author-email: sebastiankeller@online.de
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.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/) |
11+
| [![Documentation Status](https://readthedocs.org/projects/realtimeopencontrol/badge/?version=latest)](https://realtimeopencontrol.readthedocs.io/en/latest/) | [![Builds 2.1.8v](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.1
14+
### Version 2.1.8
1515
![Usecase](screenshots/RTOC-schematik.png)
1616
[Documentation](https://realtimeopencontrol.readthedocs.io/en/latest/)
1717

@@ -159,7 +159,7 @@ Classifier: Topic :: Scientific/Engineering :: Visualization
159159
Classifier: Topic :: Software Development :: User Interfaces
160160
Requires-Python: >=3
161161
Description-Content-Type: text/markdown
162-
Provides-Extra: Telegram
163162
Provides-Extra: GUI
164163
Provides-Extra: Webserver
164+
Provides-Extra: Telegram
165165
Provides-Extra: ALL

RTOC.egg-info/requires.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ python-nmap
44
whaaaaat
55
prompt_toolkit
66
pycryptodomex
7-
psycopg2
87

98
[ALL]
109
pyqt5
@@ -23,6 +22,11 @@ flask
2322
matplotlib
2423
python-telegram-bot
2524
dash_table
25+
statsmodels
26+
scikit-learn
27+
scikit-metrics
28+
patsy
29+
psycopg2
2630

2731
[GUI]
2832
pyqt5
@@ -45,3 +49,4 @@ dash_daq
4549
dash_table
4650
plotly
4751
flask
52+
psycopg2

RTOC/RTLogger/NetworkFunctions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
# import sys
77
# import subprocess
88

9+
910
try:
1011
from .. import jsonsocket
1112
except (ImportError, SystemError, ValueError):
1213
jsonsocket = None
1314

15+
1416
import logging as log
1517
log.basicConfig(level=log.DEBUG)
1618
logging = log.getLogger(__name__)
@@ -28,7 +30,6 @@
2830
'Telegram for python not installed. Please install with "pip3 install python-telegram-bot"')
2931
telegramBot = None
3032

31-
3233
class NetworkFunctions:
3334
"""
3435
This class contains all tcp-specific functions of RTLogger

RTOC/RTLogger/RTLogger.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,12 @@ def __getitem__(self, key):
119119

120120
def __setitem__(self, key, value):
121121
dict.__setitem__(self, key, value)
122-
with open(self['documentfolder']+"/config.json", 'w', encoding="utf-8") as fp:
123-
json.dump(self, fp, sort_keys=False, indent=4, separators=(',', ': '))
124-
logging.info('Config saved')
122+
try:
123+
with open(self['documentfolder']+"/config.json", 'w', encoding="utf-8") as fp:
124+
json.dump(self, fp, sort_keys=False, indent=4, separators=(',', ': '))
125+
logging.info('Config saved')
126+
except Exception:
127+
logging.warning('Config could not be saved')
125128

126129
def __delitem__(self, key):
127130
dict.__delitem__(self, key)
@@ -152,12 +155,12 @@ class RTLogger(DeviceFunctions, EventActionFunctions, ScriptFunctions, NetworkFu
152155
RT_data
153156
telegramBot
154157
"""
155-
def __init__(self, enableTCP=None, tcpport=None, isGUI=False, forceLocal=False):
158+
def __init__(self, enableTCP=None, tcpport=None, isGUI=False, forceLocal=False, customConfigPath=None):
156159
self.run = True
157160
self.forceLocal = forceLocal
158161
self.isGUI = isGUI
159162
self.__config = _Config({})
160-
self._load_config()
163+
self._load_config(customConfigPath)
161164
self.pluginObjects = {} # dict with all plugins
162165
self.pluginFunctions = {}
163166
self.pluginParameters = {}
@@ -319,17 +322,22 @@ def config(self, config):
319322
logging.info('Config changed and saved!')
320323
self.save_config()
321324

322-
def _load_config(self):
325+
def _load_config(self, customPath=None):
323326
# self.lastEditedList = []
327+
if customPath is None:
328+
userpath = os.path.expanduser('~/.RTOC')
329+
if not os.path.exists(userpath):
330+
os.mkdir(userpath)
331+
if not os.path.exists(userpath+'/backup'):
332+
os.mkdir(userpath+'/backup')
333+
configpath = userpath+"/config.json"
334+
else:
335+
configpath = os.path.realpath(customPath)
336+
userpath = os.path.dirname(configpath)
324337

325-
userpath = os.path.expanduser('~/.RTOC')
326-
if not os.path.exists(userpath):
327-
os.mkdir(userpath)
328-
if not os.path.exists(userpath+'/backup'):
329-
os.mkdir(userpath+'/backup')
330-
if os.path.exists(userpath+"/config.json"):
338+
if os.path.exists(configpath):
331339
try:
332-
with open(userpath+"/config.json", encoding="UTF-8") as jsonfile:
340+
with open(configpath, encoding="UTF-8") as jsonfile:
333341
self.__config = _Config(json.load(jsonfile, encoding="UTF-8"))
334342
# newlist = []
335343
# self.config['global']['documentfolder'] = userpath

RTOC/RTLogger/RTRemote.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
class _SingleConnection(LoggerPlugin):
2121
def __init__(self, stream=None, plot=None, event=None, host='127.0.0.1', port=5050, name='RemoteDevice', password='', logger=None):
2222
# Plugin setup
23-
super(_SingleConnection, self).__init__(stream, plot, event)
23+
super(_SingleConnection, self).__init__(logger)
2424
self.setDeviceName(name)
2525
self.logger = logger
2626
self.host = host

RTOC/RTLogger/RT_data.py

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#!/usr/local/bin/python3
22
# coding: utf-8
3-
import psycopg2
43
import time
54
from collections import deque
65
import sys
@@ -15,6 +14,11 @@
1514
import psutil
1615
import copy
1716
import gzip
17+
try:
18+
import psycopg2
19+
except Exception:
20+
print('WARNING: psycopg2 is not installed, database disabled')
21+
psycopg2 = None
1822

1923
from PyQt5.QtCore import QTimer
2024

@@ -822,6 +826,11 @@ def deviceNames(self):
822826
# Datenbank und backupJSON
823827

824828
def _connect(self):
829+
if psycopg2 is None:
830+
self.status = 'psycopg2 not installed'
831+
self._connection = None
832+
self._cursor = None
833+
return False
825834
try:
826835
self._connection = psycopg2.connect(
827836
user=self.config['postgresql']['user'],
@@ -1430,6 +1439,10 @@ def __updateT(self):
14301439
self.logger.clearCB()
14311440
# self.clear()
14321441

1442+
def createLocalBackupNow(self):
1443+
self.backupJSON(self.config['backup']['path'])
1444+
logging.info('Backup saved in path: '+self.config['backup']['path'])
1445+
14331446
def stop(self):
14341447
"""
14351448
Stops the repeated backup-thread.
@@ -1802,10 +1815,15 @@ def getGlobalXmin(self, database=True, fast=False):
18021815
else:
18031816
ans = time.time()
18041817
#else:
1805-
x_local = column(self._signals.values(), 2)
1806-
x_local = [x for l in list(x_local) for x in l]
1807-
if list(x_local) != []:
1808-
ans = min(min(list(x_local)), ans)
1818+
try:
1819+
with localLock:
1820+
xdata = list(self._signals.values())
1821+
x_local = column(xdata, 2)
1822+
x_local = [x for l in list(x_local) for x in l]
1823+
if list(x_local) != []:
1824+
ans = min(min(list(x_local)), ans)
1825+
except Exception:
1826+
logging.warning('Could not get local xmin, because _signals are in use')
18091827

18101828
return ans
18111829

@@ -1840,11 +1858,15 @@ def getGlobalXmax(self, database=True, fast=False):
18401858

18411859
# x_local = column(self._signals.values(), 2)
18421860
# ans = max(max(x_local), ans)
1843-
1844-
x_local = column(self._signals.values(), 2)
1845-
x_local = [x for l in list(x_local) for x in l]
1846-
if list(x_local) != []:
1847-
ans = max(max(list(x_local)), ans)
1861+
try:
1862+
with localLock:
1863+
xdata = list(self._signals.values())
1864+
x_local = column(xdata, 2)
1865+
x_local = [x for l in list(x_local) for x in l]
1866+
if list(x_local) != []:
1867+
ans = max(max(list(x_local)), ans)
1868+
except Exception:
1869+
logging.warning('Could not get local xmax, because _signals are in use')
18481870

18491871
return ans
18501872

RTOC/RTLogger/scriptLibrary.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def lsfit(self, x, y, func="linear", x0=None, n=None):
4141
y (list): Fitted y-values
4242
params (list): Identified parameters
4343
"""
44-
if n is None:
44+
if n is None and self is not None:
4545
n = self.config['global']['recordLength']
4646
# Initial guess.
4747
if type(func) == str: # automatic mode
@@ -384,7 +384,7 @@ def CCN():
384384
385385
https://machinelearningmastery.com/time-series-prediction-lstm-recurrent-neural-networks-python-keras/
386386
387-
387+
388388
Estimate n values in future for a signal using Convolutional Neural Network model. You need to install the following python packages with pip3: ``pip3 install tensorflow keras``
389389
390390
Args:
@@ -421,3 +421,35 @@ def CCN():
421421
x_input = x_input.reshape((1, 3, 1))
422422
yhat = model.predict(x_input, verbose=0)
423423
print(yhat)
424+
425+
426+
427+
def PT2_estimation(x,y, n, k=100, t1=101, t2=100, t0=25):
428+
"""
429+
Estimate n values in future for a signal with PT2-behavior.
430+
431+
Args:
432+
x (list): List of x-values
433+
y (list): List of y-values
434+
n (int): Number of values to be estimated
435+
k (float): Initial parameter for PT2
436+
t1 (float): Initial parameter for PT2
437+
t2 (float): Initial parameter for PT2
438+
t0 (float): Initial parameter for PT2
439+
440+
Returns:
441+
x (list): List of estimated x-values
442+
y (list): List of estimated y-values
443+
"""
444+
def _pt2(x, k, t1, t2, t0):
445+
"""
446+
Verzögerungs-Übertragungsglied zweiter Ordnung. Z.b.: zur Temperaturanalyse
447+
"""
448+
y = k*(1-(1/(t1-t2))*(t1*np.exp(-x/t1)-t2*np.exp(-x/t2)))+t0
449+
return y
450+
451+
x, y, params = lsfit(None, x, y, func=_pt2, x0=[k, t1, t2, t0], n=None)
452+
# perform fitting
453+
454+
# generate additional x-data
455+
# calculate y-data

RTOC/RTOC.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
if os.name == 'nt':
4040
import ctypes
41-
myappid = 'RTOC.2.1.1' # arbitrary string
41+
myappid = 'RTOC.2.1.8' # arbitrary string
4242
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
4343

4444
try:
@@ -163,7 +163,7 @@ class RTOC(QtWidgets.QMainWindow, Actions):
163163
"""GUI-code is not documented."""
164164
foundRTOCServerCallback = QtCore.pyqtSignal(list)
165165

166-
def __init__(self, tcp=None, port=5050, forceLocal = False):
166+
def __init__(self, tcp=None, port=5050, forceLocal = False, customConfigPath=None):
167167
super(RTOC, self).__init__()
168168
if getattr(sys, 'frozen', False):
169169
# frozen
@@ -181,7 +181,7 @@ def __init__(self, tcp=None, port=5050, forceLocal = False):
181181
self.forceQuit = False
182182
self.initPlotWidgets()
183183

184-
self.logger = RTLogger.RTLogger(tcp, port, True, forceLocal = forceLocal)
184+
self.logger = RTLogger.RTLogger(tcp, port, True, forceLocal = forceLocal, customConfigPath=customConfigPath)
185185
self.config = self.logger.config
186186
self.settings = None # window position settings
187187
self.loadPlotStyles()

RTOC/RTOC_GUI/RTOC_Import.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import os
88
from io import StringIO
99
import sys
10-
from PyQt5 import QtCore, QtGui, QtWidgets, QtPrintSupport
10+
from PyQt5 import QtCore, QtGui, QtWidgets
1111
from PyQt5.QtCore import QCoreApplication
1212
from PyQt5 import uic
1313
import traceback
@@ -16,6 +16,11 @@
1616
log.basicConfig(level=log.INFO)
1717
logging = log.getLogger(__name__)
1818

19+
try:
20+
from PyQt5 import QtPrintSupport
21+
except:
22+
QtPrintSupport=None
23+
print('QtPrintSupport is not available')
1924
try:
2025
import pandas as pd
2126
except ImportError:
@@ -96,8 +101,12 @@ def connectGUI(self):
96101
self.model.dataChanged.connect(self.finishedEdit)
97102
self.pushButtonLoad.triggered.connect(self.loadCsvAction)
98103
self.pushButtonWrite.triggered.connect(self.writeCsv)
99-
self.pushButtonPreview.triggered.connect(self.handlePreview)
100-
self.pushButtonPrint.triggered.connect(self.handlePrint)
104+
if QtPrintSupport is not None:
105+
self.pushButtonPreview.triggered.connect(self.handlePreview)
106+
self.pushButtonPrint.triggered.connect(self.handlePrint)
107+
else:
108+
self.pushButtonPreview.setVisible(False)
109+
self.pushButtonPrint.setVisible(False)
101110
self.pushClear.triggered.connect(self.clearList)
102111

103112
self.pushAddRow.clicked.connect(self.addRow)

RTOC/RTOC_GUI/RTPlotWidget.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ def __init__(self, logger, selfself, id):
4343
self.self = selfself
4444
self.id = id
4545
self.logger = logger
46-
self.config = self.self.config
46+
self.config = self.logger.config
4747
self.plotStyles = self.self.plotStyles
4848
self.active = True
4949
self.lastActive = time.time()
50-
self.updatePlotSamplerate = 10
50+
self.updatePlotSamplerate = self.logger.config['GUI']['plotRate']
5151
self.signalObjects = []
5252
self.lastUpdate = time.time()
5353
self.deviceTreeWidgetItems = []

RTOC/RTOC_GUI/signalWidget.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,9 +512,9 @@ def updateEvents(self, offsetX, offsetY, scaleX, scaleY, ctime, current_time):
512512
for idx0, e in enumerate(self.plotWidget.self.eventWidget.events):
513513
# priority, time("%H:%M:%S %d.%m.%Y", text, devicename, signalname, value, id
514514
event = [e[1], e[2], e[0], e[5], e[6], e[3], e[4], e[7]]
515-
event[0] = float(datetime.datetime.strptime(event[0], '%H:%M:%S %d.%m.%Y').timestamp())
516515
oldEvent = False
517516
if event[6] == self.signalname and event[5] == self.devicename:
517+
event[0] = float(datetime.datetime.strptime(event[0], '%H:%M:%S %d.%m.%Y').timestamp())
518518
for idx, eventItem in enumerate(self.eventItems):
519519
if eventItem.id == event[7]:
520520
# wenn schon geplottet, dann position anpassen
@@ -580,9 +580,9 @@ def updatePlot(self):
580580
# # logging.info('')
581581
# pass
582582
# else:
583+
self.updateEvents(offsetX, offsetY, scaleX, scaleY, ctime, current_time)
583584
if len(clock) > 0 and len(y) > 0:
584585
self.updateLabel(clock, y, signal[4])
585-
self.updateEvents(offsetX, offsetY, scaleX, scaleY, ctime, current_time)
586586
if self.active:
587587
# if clock[len(clock)-1] <= current_time+10000 and clock[len(clock)-1] > current_time-10000:
588588
if self.logger.config['GUI']['xRelative']:

0 commit comments

Comments
 (0)