Skip to content

Commit

Permalink
finished documentation
Browse files Browse the repository at this point in the history
and small bugfixes
  • Loading branch information
Sebastian committed May 27, 2019
1 parent e72b0ce commit 1450f60
Show file tree
Hide file tree
Showing 28 changed files with 1,085 additions and 596 deletions.
36 changes: 24 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
## Important: If you installed RTOC with `pip3`, you can now choose between different dependencies. If you need the graphical user interface, you need to install `pip3 install RTOC[ALL]`. Now you can install RTOC on a RaspberryPi with pip. Installation failed due to missing python3-qt5 package in pip. If you need the GUI on a RaspberryPi, you need to install PyQt5 with `apt install python3-qt5`.

# RealTime OpenControl (RTOC)

### Version 2.0 (PRERELEASE)
| [![Documentation Status](https://readthedocs.org/projects/realtimeopencontrol/badge/?version=latest)](https://realtimeopencontrol.readthedocs.io/en/latest/) | [![Builds v1.6](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/Naereen/StrapDown.js/releases/) | [![Github all releases](https://img.shields.io/github/downloads/Naereen/StrapDown.js/total.svg)](https://GitHub.com/Naereen/StrapDown.js/releases/) |


### Version 2.0
![Usecase](screenshots/RTOC-schematik.png)
[Documentation](https://github.com/Haschtl/RealTimeOpenControl/wiki) !!! NEW LINK !!!
[Documentation](https://realtimeopencontrol.readthedocs.io/en/latest/)

RealTime OpenControl is a simple way for real-time data recording, visualization and editing.

Expand Down Expand Up @@ -76,17 +77,17 @@ RTOC can be installed and used in different ways:
- Installation with builds (Linux/Windows)
- Installation from source

!!! MORE INFORMATION LINK !!!
[Click here for a complete setup-tutorial](https://realtimeopencontrol.readthedocs.io/en/latest/)


### Wiki
[Read the Wiki for full documentation](https://github.com/Haschtl/RealTimeOpenControl/wiki) !!! NEW LINK !!!
### Documentation
[Read the Wiki for full documentation](https://realtimeopencontrol.readthedocs.io/en/latest/)

### Default/Example Plugins:

- function generator: generates sine, square, sawtooth, random, AC, DC

You can get more plugins from the [RTOC-plugin-repository](https://github.com/Haschtl/rtoc-plugins). Simply follow the steps described in the [documentation](https://github.com/Haschtl/RealTimeOpenControl/wiki/RTOC-Repo) !!! NEW LINK !!!:
You can get more plugins from the [RTOC-plugin-repository](https://github.com/Haschtl/rtoc-plugins). Simply follow the steps described in the [documentation](https://realtimeopencontrol.readthedocs.io/en/latest/PLUGINS.html#plugin-repository):
- System: For recording many system variables (CPU, Memory, Network,...)
- Octoprint: Recording of 3D printers
- DPS5020: power supply unit recording and control (possibly also DPS5005, ...)
Expand All @@ -112,7 +113,7 @@ The graphical user interface of RTOC offers a wealth of functions for data displ
- Scaling, shifting of signals
- Run multiple scripts in parallel

[Complete GUI-tutorial here.](https://github.com/Haschtl/RealTimeOpenControl/wiki/GUI) !!! NEW LINK !!!
[Complete GUI-tutorial here.](https://realtimeopencontrol.readthedocs.io/en/latest/GUI.html)

### Write simple Python-Plugin

Expand All @@ -123,7 +124,7 @@ Python plugins are integrated into RTOC and can be used to

Plugins can **not** access all measurements. This can be done with a TCP connection to RTOC.

[Example-Plugins here.](https://github.com/Haschtl/RealTimeOpenControl/wiki/PlugIns) !!! NEW LINK !!!
[Example-Plugins here.](https://realtimeopencontrol.readthedocs.io/en/latest/PLUGINS.html)

### Simple local TCP-Datastream

Expand All @@ -141,11 +142,11 @@ The client can

The connection between RTOC server and client can be encrypted end-to-end (DES) with a password.

[Example for TCP here.](https://github.com/Haschtl/RealTimeOpenControl/wiki/clientCommunication) !!! NEW LINK !!!
[Example for TCP here.](https://realtimeopencontrol.readthedocs.io/en/latest/TCP.html)

### Include Telegram-messanger

[Tutorial for Telegram here.](https://github.com/Haschtl/RealTimeOpenControl/wiki/telegram) !!! NEW LINK !!!
[Tutorial for Telegram here.](https://realtimeopencontrol.readthedocs.io/en/latest/TELEGRAM.html)

## Screenshots

Expand Down Expand Up @@ -206,3 +207,14 @@ Please read [CONTRIBUTING.md](https://gist.github.com/PurpleBooth/b24679402957c6
## License

This project is licensed under the **GNU General Public License v3.0** - see the [LICENSE](LICENSE) file for details


# Coffee
Feel free to buy me some coffee with milk

<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="hosted_button_id" value="Y5894CRYB4L36" />
<input type="image" src="https://www.paypalobjects.com/en_US/DK/i/btn/btn_donateCC_LG.gif" border="0" name="submit" title="PayPal - The safer, easier way to pay online!" alt="Donate with PayPal button" />
<img alt="" border="0" src="https://www.paypal.com/en_DE/i/scr/pixel.gif" width="1" height="1" />
</form>
98 changes: 63 additions & 35 deletions RTOC/LoggerPlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

try:
from . import jsonsocket
except ImportError:
except (SystemError, ImportError):
import jsonsocket

lock = Lock()
Expand Down Expand Up @@ -51,8 +51,9 @@ def __init__(self, stream=None, plot=None, event=None):
self._tcppassword = ''
self._tcpport = 5050
self._tcpaddress = ''
self._tcpthread = True
self._tcpthread = False
self._pluginThread = None
self.__oldPerpetualTimer = False
self.lockPerpetialTimer = Lock()
# -------------
self.run = False # False -> stops thread
Expand All @@ -79,11 +80,11 @@ def getDir(self, dir=None):

return packagedir

def stream(self, *args, **kwargs):
def stream(self, **kwargs):
"""
Use this function to send new measurements to RTOC. You can send multiple signals at once.
This function is a wrapper for :func:`RTOC.RTLogger.RT_data.addData`
This function is a wrapper for :py:meth:`.RT_data.addData`
You can choose one of three ways to send measurements.
Expand Down Expand Up @@ -123,16 +124,13 @@ def stream(self, *args, **kwargs):
kwargs['dname'] = self._deviceName
if signallist is None:
y = kwargs.get('y', [0])
for idx, arg in enumerate(args):
if idx == 0:
y = arg
if type(y) == list:
kwargs['x'] = [now]*len(y)
else:
kwargs['x'] = [now]


self.__cb(*args, **kwargs)
self.__cb(**kwargs)
return True
elif type(signallist) == list:
kwargs.pop('list')
Expand All @@ -147,11 +145,12 @@ def stream(self, *args, **kwargs):
kwargs['snames'].append(sig[1])
kwargs['unit'].append(sig[2])
kwargs['x'].append(now)
self.__cb(*args, **kwargs)
self.__cb(**kwargs)
return True
elif type(signallist) == dict:
kwargs.pop('list')
for dev in signallist.keys():
kwargs = {}
kwargs['dname'] = dev
kwargs['y'] = []
kwargs['x'] = []
Expand All @@ -165,13 +164,15 @@ def stream(self, *args, **kwargs):
kwargs['snames'].append(sig)
kwargs['unit'].append(signallist[dev][sig][1])
kwargs['x'].append(now)
else:
logging.error('STREAM ERROR, signal has not this format: [y, "unit"]')
else:
logging.error('STREAM_ERROR: One signal was malformed')
# logging.debug(kwargs)
self.__cb(**kwargs)
return True
else:
logging.error('STREAM_ERROR: One device was malformed')
return True
else:
logging.error('STREAM_ERROR: The data you provided with in your plugin was wrong."')
else:
Expand All @@ -190,7 +191,7 @@ def plot(self, x=[], y=[], sname='noName', dname=None, unit='', hold='off', auto
`hold='mergeY'` will only add xy-pairs, if the y-value is not in the existing data.
This function is a wrapper for :func:`RTOC.RTLogger.RT_data.plot`
This function is a wrapper for :py:meth:`.RT_data.plot`
Args:
x (list): A list containing all x values, e.g: `[1, 2, 3, 4, 5, 6, 7, 8]`
Expand Down Expand Up @@ -221,7 +222,7 @@ def event(self, text='', sname=None, dname=None, priority=0, id=None, value=None
"""
Use this function to send an event to RTOC.
This function is a wrapper for :func:`RTOC.RTLogger.RT_data.addNewEvent`
This function is a wrapper for :py:meth:`.RT_data.addNewEvent`
Args:
text (str): A description of this event, e.g: `'Freezer door opened'`
Expand All @@ -248,7 +249,7 @@ def event(self, text='', sname=None, dname=None, priority=0, id=None, value=None
logging.warning("No event connected")
return False

def createTCPClient(self, address="localhost", password=None, tcpport=5050, threaded=True):
def createTCPClient(self, address="localhost", password=None, tcpport=5050, threaded=False):
'''
Creates a TCP client. Used to talk to RTOC via TCP. Read :doc:`TCP` for more information.
You need to call this once, before you can use `sendTCP()`
Expand Down Expand Up @@ -285,16 +286,16 @@ def sendTCP(self, *args, **kwargs):
unit (list or str): Signal-unit
plot (bool): False: xy-pairs are interpreted as different signals, True: xy-pairs are one signal
event ([text, id, value]): Submit an event
remove: :func:`RTOC.RTLogger.NetworkFunctions.remove`
plugin: :func:`RTOC.RTLogger.NetworkFunctions.handleTcpPlugins`
logger: :func:`RTOC.RTLogger.NetworkFunctions.handleTcpLogger`
getSignal: :func:`RTOC.RTLogger.NetworkFunctions.getSignal`
getLatest: :func:`RTOC.RTLogger.NetworkFunctions.getLatest`
getEvent: :func:`RTOC.RTLogger.NetworkFunctions.getEvent`
getSignalList: :func:`RTOC.RTLogger.NetworkFunctions.getSignalList`
getEventList: :func:`RTOC.RTLogger.NetworkFunctions.getEventList`
getPluginList: :func:`RTOC.RTLogger.NetworkFunctions.getPluginList`
getSession: :func:`RTOC.RTLogger.RT_data.generateSessionJSON`
remove: :py:meth:`.NetworkFunctions.remove`
plugin: :py:meth:`.NetworkFunctions.handleTcpPlugins`
logger: :py:meth:`.NetworkFunctions.handleTcpLogger`
getSignal: :py:meth:`.NetworkFunctions.getSignal`
getLatest: :py:meth:`.NetworkFunctions.getLatest`
getEvent: :py:meth:`.NetworkFunctions.getEvent`
getSignalList: :py:meth:`.NetworkFunctions.getSignalList`
getEventList: :py:meth:`.NetworkFunctions.getEventList`
getPluginList: :py:meth:`.NetworkFunctions.getPluginList`
getSession: :py:meth:`.RT_data.generateSessionJSON`
Returns:
tcp_response (dict), if createTCPClient(threaded=False)
Expand Down Expand Up @@ -435,9 +436,9 @@ def close(self):
if self.widget:
self.widget.hide()
self.widget.close()
if self._pluginThread:
if self._pluginThread and not self.__oldPerpetualTimer:
self._pluginThread.cancel()

# if self._sock:
# self._sock.close()

Expand Down Expand Up @@ -482,33 +483,49 @@ def samplerate(self):
@samplerate.setter
def samplerate(self, samplerate):
self.__samplerate = samplerate
if self._pluginThread:
if self._pluginThread and not self.__oldPerpetualTimer:
self._pluginThread.setSamplerate(samplerate)

def setPerpetualTimer(self, fun, samplerate=None, interval = None):
def setPerpetualTimer(self, fun, samplerate=None, interval = None, old = False):
"""
Configures a perpetual timer. A perpetual timer is a thread, which is repeated infinitly in a specified time-interval or samplerate.
Args:
fun (function): The function to be called periodically.
samplerate (float): The desired samplerate.
interval (float): The desired time-delay interval.
old (bool): If True, an old method for perpetual timer is used (based on :mod:`threading.Thread`). If False, the new method will be used (based on :mod:`threading.Timer`).
Returns:
bool: True, if perpetual timer could be configured, False, if not.
"""
if old:
self.__oldPerpetualTimer = True
else:
self.__oldPerpetualTimer = False

if samplerate is None and interval is None:
samplerate = self.samplerate
elif samplerate is None:
samplerate = 1/interval
try:
self.__samplerate = samplerate
self._pluginThread = _perpetualTimer(fun, samplerate, self.lockPerpetialTimer)
return True
except Exception:
self._pluginThread = None
return False

if not self.__oldPerpetualTimer:
try:
self.__samplerate = samplerate
self._pluginThread = _perpetualTimer(fun, samplerate, self.lockPerpetialTimer)
return True
except Exception:
self._pluginThread = None
return False
else:
try:
self.__samplerate = samplerate
self._pluginThread = Thread(target=self.__updateT, args=(fun,))
return True
except Exception:
self._pluginThread = None
return False

def start(self):
"""
Expand All @@ -535,13 +552,24 @@ def cancel(self):
"""
if self._pluginThread:
self._pluginThread.cancel()
if not self.__oldPerpetualTimer:
self._pluginThread.cancel()
self.run = False
return True
else:
self.run = False
return False

def __updateT(self, func):
diff = 0
while self.run:
if diff < 1/self.__samplerate:
time.sleep(1/self.__samplerate-diff)
start_time = time.time()
func()
diff = (time.time() - start_time)



class _perpetualTimer():
def __init__(self, hFunction, samplerate=1, lock=None):
Expand Down
18 changes: 6 additions & 12 deletions RTOC/RTLogger/NetworkFunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def toggleTcpServer(self, value=None):
if value is None:
value = self.config['tcp']['active']
self.config['tcp']['active'] = value
if value:
if value is True:
try:
password = None
if self.config['tcp']['password'] != "":
Expand Down Expand Up @@ -122,17 +122,11 @@ def setTCPPort(self, port):

def sendTCP(self, hostname="localhost", *args, **kwargs):
"""
See :func:`RTOC.LoggerPlugin.LoggerPlugin._sendTCP` for more information
See :py:meth:`.LoggerPlugin.sendTCP` for more information
"""
self.tcpclient.createTCPClient(hostname)
return self.tcpclient._sendTCP(*args, **kwargs)

def getThread(self):
"""
Returns the :mod:`threading.Thread` object of :meth:`._tcpListener`
"""
return self.__tcpserver

def _tcpListener(self):
while self.tcpRunning:
ans = {'error': False}
Expand Down Expand Up @@ -274,7 +268,7 @@ def handleTcpLogger(self, loggerDict):
loggerDict (dict): {'export':['dname.sname',...]} to export a signal
loggerDict (dict): {'info':None} to get informations about RTOC
loggerDict (dict): {'info':None} to get informations about RTOC
loggerDict (dict): {'reboot':None} to reboot RTOC-server
Expand Down Expand Up @@ -325,7 +319,7 @@ def getSignalList(self):
Returns signallist, which is used in tcp-requests
Returns:
:func:`RTOC.RTLogger.RT_data.signalNames`
:py:meth:`.RT_data.signalNames`
"""
signalNames = self.database.signalNames()
if ['RTOC', ''] in signalNames:
Expand Down Expand Up @@ -364,7 +358,7 @@ def getEventList(self):
Returns eventlist, which is used in tcp-requests
Returns:
:func:`RTOC.RTLogger.RT_data.events`
:py:meth:`.RT_data.events`
"""
return self.database.events()

Expand All @@ -376,7 +370,7 @@ def getEvent(self, nameList):
nameList (list): List of device.signalnames
Returns:
list: [:func:`RTOC.RTLogger.RT_data.getEvents`,...]
list: [:py:meth:`.RT_data.getEvents`,...]
"""
ans = {}
for device in nameList:
Expand Down

0 comments on commit 1450f60

Please sign in to comment.