RealTime OpenControl’s documentation

Welcome to RealTime OpenControl’s documentation

RealTime OpenControl (RTOC) is a free software for recording measurement data of various measuring instruments. It offers the following components

RTLogger backend

The core element of RTOC. More information here: Backend Source-code

Expandable with plugins

You can write plugins by your own or use a plugin from the repository. More information here: Collecting data

Telegram-Bot

The Telegram-Bot offers access to RTOC from any device with Telegram installed. More information here: Telegram Communication

Websocket-Server

Communication with RTLogger from other processes or devices. Suitable for embedded devices with graphical user interface. More information here: /Websocket

Graphical user interface (RTOC-GUI)

Used, when running RTOC on computers/laptops to view and edit data. More information here Graphical user interface.

Scripting/Automation

You can write scripts and run/edit and stop them during runtime. You have full access to all data stored in RTOC and access to all plugins. A event/action system gives a simple solution for very custom automisations. More information here: Controlling and automation

Getting started

Follow one of the installation-instructions (pip, builds, source): Installation

See this Example: Embedded to get an idea of the capabilities of RTOC.

Have a look at the plugin-documentation: Collecting data

RTOC will create a directory in the home-directory, where all user-data is stored. More information here: Userdata

FAQ

Feel free to buy me some coffee with milk

Indices and tables

Table of contents

Installation

Python3 needs to be installed on your target-computer. Download python3 from the official website: www.python.org

After installing python3, you are able to run python in a terminal. (cmd.exe on windows) .. code-block:: bash

$ python3 –version Python 3.6.3

Installing from builds

Download the latest release builds for Windows here.

Extract the .zip file into a directory. RTOC is started by double-clicking on “RTOC.exe”. Alternatively via command line:

// local RTOC-instance including GUI
./RTOC

Install manually

To use the basic RTOC, the following dependencies must be installed:

pip3 install numpy pycryptodomex requests python-nmap whaaaaat prompt_toolkit psycopg2

If you want to use the GUI you must also install the following packages:

pip3 install pyqt5 pyqtgraph markdown2 xlsxwriter scipy pandas ezodf pyGithub

If you want full functionality, then you still need the following packages (for telegram bot):

pip3 install python-telegram-bot matplotlib

You can use different stylesheets for the GUI if you want. Just install one of these with pip: ‘QDarkStyle’, ‘qtmodern’, ‘qdarkgraystyle’.

The RTOC repository can then be cloned with:

git clone git@github.com:Haschtl/RealTimeOpenControl.git

Long-time measurements in postgreSQL database (optional)

If you want to store measurements for a long period of time, I would recommend to use RTOC with a postgreSQL database. Therefore you need to setup postgreSQL on your system and change the postgresql parameters in your config.json file.

Setup postgreSQL on linux

  1. Open a terminal window
  2. Issue the command sudo apt install postgresql
  3. Follow the instructions to change the default postgresql-password.
  4. Add a new user. You need to switch to the root user to create a new postgres-user
$ sudo bash
$ su - postgres
$ createuser --interactive --pwprompt
$ Enter name of role to add: <NEWUSERNAME>
$ Enter password for new role: <PASSWORD>
$ Enter it again: <PASSWORD>
$ Shall the new role be a superuser? (y/n) n
$ Shall the new role be allowed to create databases? (y/n) y
$ Shall the new role be allowed to create more new roles? (y/n) n
$ exit  // to return to root user
$ exit // to return to your user
  1. Create a database
$ createdb -O <NEWUSERNAME> <DATABASE_NAME>
  1. Enter your postgresql username, port, database and password in your config.json file.

Setup postgreSQL on windows

  1. Download postgresql one-click installer from this website
  2. Double click on the downloaded file and follow the setup instructions.
  3. Add a new user and create a database (google for that)
  4. Enter your postgresql username, port, database and password in your config.json file.

First steps

First run

After installing RTOC, you can run it with:

// local RTOC-instance including GUI
python3 -m RTOC

// local RTOC-instance without GUI
// I would recommend starting RTOC as a service and not to use this.
python3 -m RTOC.RTLogger -s start/stop

// local RTOC-Configuration from Console
python3 -m RTOC.RTLogger -c

// remote RTOC-instance with GUI
python3 -m RTOC -r <ADRESS>

// explicit local RTOC GUI (even if database is enabled)
python3 -m RTOC -l

Collecting data

Writing Plugins

To integrate a new device, you have to program your own plugin.

Each user-plugin must be stored in the user-directory ~/.RTOC/devices/. Each plugin must has its own folder.

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, *args, **kwargs):
        super(Plugin, self).__init__(*args, **kwargs))

        # start your code here

The above lines must be copied exactly into the plugin!

All functions and parameters defined in the main class are available for scripts. To prevent them from being displayed in the Script Help (and Telegram-bot), parameters and functions must begin with ‘_’.

Refer to LoggerPlugin for a full list of available functions.

The following example plugin sends data to RTOC every second and loads an empty GUI

Template.py

"""
This template shows, how to implement plugins in RTOC

RTOC version 2.0

A plugin needs to import RTOC.LoggerPlugin to be recognized by RTOC.
"""
try:
    from LoggerPlugin import LoggerPlugin
except ImportError:
    from RTOC.LoggerPlugin import LoggerPlugin

import sys
import time
from PyQt5 import uic
from PyQt5 import QtWidgets
import numpy as np

DEVICENAME = "Template"
"""Definition of the devicename outside of plugin-class"""

AUTORUN = True
"""If true, the thread to collect data will run right after initializing this plugin"""
SAMPLERATE = 1
"""The thread,which is supposed to collect data will be executed with 1 Hz"""


class Plugin(LoggerPlugin):
    def __init__(self, *args, **kwargs):
        super(Plugin, self).__init__(*args, **kwargs)
        """Call this to initialize RTOC.LoggerPlugin"""

        self.setDeviceName(DEVICENAME)
        """
        Set a default devicename.
        This will be used for all signals sent by this plugin as default
        """

        self.smallGUI = True
        """This plugin has a GUI, which is small. GUI can be shown in two different ways."""

        self._firstrun = True

        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. 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 '
        ``self.run`` with ``while self.run:``.
        """

        if AUTORUN:
            self.start()
            """
            Start the configured perpetualTimer. You can stop it with ``self.cancel()``. If you want to start data collection, when this plugin is started, you need to call ``self.start()`` in the plugin-initialization.
            """

    def _updateT(self):
        """
        This function is called periodically after calling ``self.start()``.

        This example will generate a sinus and a cosinus curve. And send them to RTOC.


        """
        y1 = np.sin(time.time())
        y2 = np.cos(time.time())

        self.stream([y1, y2], snames=['Sinus', 'Cosinus'], unit=["kg", "m"])
        """
        Use this function to send data to RTOC: :py:meth:`.LoggerPlugin.stream`
        """

        self.plot([-10, 0], [2, 1], sname='Plot', unit='Wow')
        """
        Use this function to send data to RTOC: :py:meth:`.LoggerPlugin.plot`
        """
        if self._firstrun:
            self.event('Test event', sname='Plot', id='testID')
            """
            Use this function to send an event to RTOC: :py:meth:`.LoggerPlugin.event`
            """
            self._firstrun = False

    def loadGUI(self):
        """
        This function is used to initialize the Plugin-GUI, which will be available in :doc:`GUI`.

        This is optional.

        Returns:
            PyQt5.QWidget: A widget containing optional plugin-GUI
        """
        self.widget = QtWidgets.QWidget()
        """
        Create an empty QWidget
        """
        packagedir = self.getDir(__file__)
        """Get filepath of this file"""
        uic.loadUi(packagedir+"/Template/template.ui", self.widget)
        """
        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

if __name__ == "__main__":
    """
    Sometimes you want to use plugins standalone also. This is very useful for testing.
    """
    if hasGUI:
        app = QtWidgets.QApplication(sys.argv)
        myapp = QtWidgets.QMainWindow()

    widget = Plugin()

    if hasGUI:
        widget.loadGUI()
        myapp.setCentralWidget(widget.widget)

        myapp.show()
        app.exec_()

    widget.run = False

    sys.exit()

Plugin repository

This repository contains some plugins for RealTime OpenControl (RTOC).

Installing plugins

You can either install plugins manually or - if you are using the GUI - you can install it with the built-in installer: Plugin-Downloader

Manually

To add a plugin to RTOC you need to do the following steps:

  1. Install RTOC (pip3 install RTOC) - You will need to run RTOC once
  2. Copy the folder of the desired plugin to your RTOC-Userpath: ~/.RTOC/devices/
  3. Now restart RTOC (python3 -m RTOC)
List of plugins
  • Template: An example, showing how to create a simple plugin to send data to RTOC
  • DPS5020: Plugin for DPS powersupplies. It can monitor all data and you can set Voltage, Current and switch it on/off. Uses USB to read data.
  • Funktionsgenerator: Default-plugin of RTOC. Generates common signals.
  • holdPeak_VC820: Plugin for VC820 multimeters. It can monitor the measured values with correct units. Uses USB/Minimalmodbus to read data.
  • INA219_Modul: Plugin for INA219 solar module. Monitors voltage, current, power and shunt_voltage
  • Octotouch: Plugin for 3D-printer-software Octotouch. It can just monitor the temperatures. Uses HTTP/JSON to read data.
  • PIKO_Solarmodules: Plugin for PIKO solar modules. Monitors voltage, current and power
  • System: Plugin to monitor system-information like CPU, Memory, …
  • ReflowOfen/ReflowPlatte: Plugin, which reads data from local network-devices HTTP-address.
  • Heliotherm: Plugin, which reads data from Heliotherm heat pump using TCP/Modbus.
  • Futtertrocknung: Embedded-Plugin. Plugin, which is used to run on a RaspberryPi. Monitors some sensor-data.
Plugin descriptions
Template

GUI: Yes, if you want to

_images/template.png

Dependencies: -

Info: Use this plugin as a starting point

DPS5020

GUI: Yes

Dependencies: pip3 install minimalmodbus

Target system: Each OS (connected to DPS with USB)

Info:

  • You can set a parameters in file DPS5020.py
default_device = '/dev/ttyUSB0'
SERIAL_BAUDRATE = 9600
SERIAL_BYTESIZE = 8
SERIAL_TIMEOUT = 2
Generator

GUI: Yes

_images/generator.png

Dependencies: -

Target system: Each OS

Info:

holdPeak_VC820

GUI: Yes

Dependencies: pip3 install serial

Target system: Each OS (connected to VC820 with USB)

Info:

  • You can set a parameters in file HoldPeak VC820.py: - default_device = ‘COM7’ - SERIAL_BAUDRATE = 2400 - SERIAL_BYTESIZE = 8 - SERIAL_TIMEOUT = 1
  • You will need to run RTOC as root unless you set devices rules. See [this tutorial](http://ask.xmodulo.com/change-usb-device-permission-linux.html) for how to set device rules.
INA219_Modul

GUI: No

Dependencies: pip3 install pi-ina219

Target system: RaspberryPi (connected to INA219 via I2C)

Info:

  • You can set a parameters in file INA219_Modul.py:
    • SHUNT_OHMS = 0.1
    • MAX_EXPECTED_AMPS = 0.2
    • SAMPLERATE = 1/60# frequency in Hz (1/sec)
    • I2C_ADDRESS = 0x41
Octotouch

GUI: Yes

_images/octotouch.png

Dependencies: -

Target system: Each OS (In same network as Octotouch-server)

Info:

You can set a parameters in file OctoTouch.py:

  • devicename = “Octotouch”
  • apikey = “”
  • SAMPLERATE = 1
PIKO_Solarmodules

GUI: No

Dependencies: pip3 install lxml

Target system: Each OS (In same network as PIKO modules)

Info:

  • You can set a parameters in file INA219_Modul.py: - SAMPLERATE = 1/60# frequency in Hz (1/sec) - ADRESSES = [“IP1”, “IP2”, …] #You can specify multiple adresses
System

GUI: Yes

_images/systemPlugin.png

Dependencies: -

Target system: Each OS

Info:

ReflowOfen/ReflowPlatte

GUI: Yes

_images/reflowOven.png

Dependencies: -

Target system: Each OS

Info:

Heliotherm

GUI: Yes

_images/heliotherm.png

Dependencies: pip3 install pyModbusTCP

Target system: Each OS (In same network as Heliotherm heat pump)

Info:

Futtertrocknung

GUI: No

Dependencies: pip3 install adafruit_CCS811 adafruit_DHT board busio

Target system: RaspberryPi

Info:

Userdata

Document-Tree

User data is stored in the home directory of the current user
  • home/USER/.RTOC/ on linux
  • C:\user\USER\ .RTOC\ on windows

It contains the following files

.RTOC
├── backup
│   ├── localBackup1.json
│   ├── ...
├── devices
│   ├── Template
│   ├──   ├── ...
│   ├── ...
├── autorun_devices
├── config.json
├── globalActions.json
├── globalEvents.json
├── plotStyles.json
├── telegramActions.json
├── telegram_clients.json

backup (directory)

Used only, if backup is active and postgresql is not active. Currently this type of backup is not implemented.

devices (directory)

Place your plugins inside this folder! Each plugin must be in a folder, which should be named like the main-module of your plugin. For more information on how to make a plugin, click here: Collecting data

autorun_devices

Write plugin-names in each line of this file. These plugins will start with RTOC automatically (make sure the plugin-Thread is started in your __init__).

config.json

This file contains all RTOC configurations. Its separated in different topics:

global
Entry Default Type Information
language “en” “en”,”de” Selected language
recordLength 500000 int Local recording limit
name “RTOC-Remote” str Name displayed in Telegram
documentfolder “~/.RTOC” str ! Do not change !
globalActionsActivated False bool Global actions (in-)active
globalEventsActivated False bool Global events (in-)active
postgresql
Entry Default Type Information
active False bool De/activate PostgreSQL database
user “postgres” str PostgreSQL Username
password “” str User’s password
host “127.0.0.1” str Host of PostgreSQL-server
port 5432 int PostgreSQL port
database “postgres” str Name of PostgreSQL database
onlyPush True bool Only push data automatically, if backup active
GUI
Entry Default Type Information
darkmode True bool De/activate darkmode (inactive)
scriptWidget True bool Show/hide scriptWidget on startup
deviceWidget True bool Show/hide deviceWidget on startup
deviceRAWWidget True bool Show/hide deviceRAWWidget on startup
pluginsWidget False bool Show/hide pluginsWidget on startup
eventWidget True bool Show/hide eventWidget on startup
restoreWidgetPosition False bool Save and restore window and widget positions
newSignalSymbols True bool (inactive)
plotLabelsEnabled True bool Show/hide signal-labels in graph
plotGridEnabled True bool Show/hide grid in graph
showEvents True bool Show/hide events in graph
grid”: [True, True, 1.0] list Grid configuration: [X-lines, Y-lines, linewidth]
plotLegendEnabled False bool Show/hide legend in graph
blinkingIdentifier False bool Show/hide blue blinking of signal-labels, when they are updated
plotRate 8 float Updaterate of graph in Hz
plotInverted False bool Invert plot (black-white/white-black)
xTimeBase True bool Plot x-axis values as difference from actual timestamp
timeAxis True bool Use time-ticks for x-axis
systemTray False bool Dis/enable close to systemTray
signalInactivityTimeout 2 float Time in seconds after Signal-Label turns yellow
autoShowGraph False bool Automatically show new signals
antiAliasing True bool Dis/Enable AntiAliasing
openGL True bool Dis/Enable OpenGL
useWeave True bool Dis/Enable Weave
csv_profiles {} dict Allocation of imported signals is stored here
telegram
Entry Default Type Information  
active False bool De/activate telegram-bot
token “” str Your telegram bot-token
default_eventlevel 0 0,1 or 2 Default eventlevel for new users
default_permission ‘blocked’ ‘blocked’,’read’, ‘write’ or ‘admin’ Default user permissions for new users. First user is always admin!
inlineMenu False bool Make the telegram menu inline or in KeyboardMarkup
onlyAdmin False bool If True, only admins will be able to access the bot
websockets
Entry Default Type Information
active False bool De/activate Websocket-server
port 5050 int Websocket-port
password ‘’ str Optional password for Websocket-encryption (AES)
knownHosts {} dict Recent Websocket-hosts for remote connection are stored here
backup
Entry Default Type Information
active False bool De/activate backup-thread
path ‘~/.RTOC/backup’ str Backup-directory (does not affect backup, if postgreSQL is active!)
clear False bool Automatically clear local data after backup
autoIfFull True bool Automatically backup, if local recordLength is reached
autoOnClose True bool Automatically backup after closing RTOC
loadOnOpen True bool Automatically load data after starting RTOC (if False, signals are still shown to make sure that IDs are allocated correctly)
intervall 240 float Set backup-intervall in seconds
resample 0 float If != 0, signals will be resampled with given samplerate before creating backup

globalActions.json

This file contains all global actions. Read more about the event/action system here: Event/Action system

globalEvents.json

This file contains all global events. Read more about the event/action system here: Event/Action system

plotStyles.json

This file contains all signal styles, which are used by the GUI. Delete it, to reset all signal styles.

telegramActions.json

Use this file to add main-menu-entries in the telegram-bot. More information here: Telegram Custom-menu

telegram_clients.json

Information about telegram-clients is stored here: clientID = {eventlevel = 0, shortcuts = [[],[]], first_name = "NAME", last_name = "NAME", permission = "admin", menu = "menu"}

Telegram Communication

Telegram-Bot setup

Create a new Telegram-Bot
  • Telegram must be installed on Smartphone/PC,…
  • Open the following website: https://telegram.me/BotFather
  • Click on ‘SEND MESSAGE’ (Telegram should open and create a new contact ‘BotFather’)
  • Create a new bot with the command ‘/newbot
  • Enter a name for the bot
  • Enter a username for the bot
  • You will receive a token which has to be entered in RTOC (looks like this: 123456789:AABBAABB66AA11_uGG22GH44AA55_3)
Configure RTOC for Telegram

You can eather configure it in the settings of the RTOC GUI or edit the config.json file by yourself. Enter your Bot-Token there and activate it (active=True).

User permissions

By default any new client connected to this bot will have no permissions. You can change the default behavior in config.json.

The first client connected to the bot will automatically be admin. The admin has the permission to change the permissions of all users.

  • blocked: No permissions at all
  • custom: Can only call user-specifig shortcuts, cannot receive event-notifications
  • read: Can receive event-notifications and plot signals
  • write: Can create global Events/Actions, send Events/Signals, access devices
  • admin: Full permissions. Can delete signals, access settings menu
_images/telegramBot.png
Custom permission

The custom permission has a special role. A user with ‘custom’ permission will not receive notifications for events and will only see shortcuts, created for this user. Thereby you can provide a very simple bot to users, which will only be able to call selected functions or modify parameters. For example a bot with only two buttons to control the heating in the livingroom: ‘Set Temperature’ and ‘Enable/Disable’

Telegram Custom-menu

The file telegramActions.json contains dicts with actions, that will be shown in the main menu and can be executed by any user. If the action-name (key) starts with ‘_’ only admins will be able to see this button.

Here is an example to send a screenshot

{
       "Screenshot": """

    import pyscreenshot as ImageGrab
    dir = self.config['global']['documentfolder']
    im = ImageGrab.grab()\nim.save(dir+'/telegram_overview.png')
    return 'picture', dir+'/telegram_overview.png'
    """

}

A telegram action must return either a text, a picture or any other file.

return 'text', 'My example text' to return a text message.

return 'picture', <dir/to/picture.jpg> to return a picture.

return 'document', <dir/to/file> to return any other file.

User Shortcuts

Any Telegram client can define his own shortcuts to plugin-functions and parameters. These shortcuts will be displayed in the main menu.

To define a new shortcut, you can either modify the shortcuts manually in the telegram_clients.json file.

Clients with ‘write’ or ‘admin’ permission can create shortcuts right in the Telegram bot. You can press the ‘Create shortcut’ button in every plugin-parameter or function submenu.

**Important: ** Every telegram function (telegram_send_message,…) inside a plugin-function will send the message, file or image only to the client, who called the shortcut.

Controlling and automation

Overview

Own scripts can be used during the runtime:

  • modify the measurement data
  • Adjusting plugin parameters and calling functions
  • Create new signals
  • manual and automatic controlling, …

In general, you write a Python script like any other, you can import libraries, etc. However, you should pay attention to performance.

RTOC provides an own library for scripts: RTLogger.scriptLibrary

There are three places, where custom scripting is possible.

Available functions and libraries

Python libraries

There are several python libraries automatically imported in scripts

import math
import numpy as np
import sys
import scipy as sp
from .RTLogger import scriptLibrary as rtoc
Functions to interact with RTOC
Access to plugin parameters and signals in scripts

All signals can be references in scripts in these ways

[x],[y] = Device.Signalname
[x] = Device.Signalname.x
[y] = Device.Signalname.y
c = Device.Signalname.latest

plugin parameters and functions can be accessed in this way

Device.FUNCTION(...)
Device.PARAMETER = ...
Access to telegram functions

You can send messages, pictures, files and graphs with the following functions. (e.g. telegram.send_message_to_all('Hi')):

Special stuff

The actual timestamp is available in the global variable clock. You can use the default print() function for text-output in console, GUI and telegram.

You can define variables global. This ensures, this variable will remain until the next call of this script. Example

global VARNAME = 0

If-else conditions aren’t always suitable for real-time operations. You cannot trigger rising/falling for example. Therefore you can use a trigger to call the code inside a condition only once

trig CONDITION:
  print('Hello')

This example will only print ‘Hello’, if CONDITION changes from False to True.

RTOC library

Event/Action system

The Event/Action System allows Python code to be executed when events occur. These pieces of code have the same possibilities as scripts.

Global actions

Global actions are stored in the file globalActions.json.

Example

{
  "action2": {
    "listenID": ["testID"],
    "parameters": "",
    "script": "Generator.gen_level = 1",
    "active": False

  }

}

This JSON-file contains dicts - each describing one action.

Parameter Datatype Definition
listenID list The event-IDs of events, which will trigger this action
parameters str Unused
script str The script, which will be executed
active bool If True, this action will be active
Global events

Global events are stored in the file globalEvents.json unlike events created in plugins.

Example

{
  "myevent": {
    "cond": "Generator.Square.latest >= 2",
    "text": "It is big",
    "return": " ",
    "priority": 0,
    "id": "testID",
    'trigger': 'rising',
    'sname': '',
    'dname': '',
    'active': True

  }

}

This JSON-file contains dicts - each describing one event.

Parameter Datatype Definition
cond str The condition triggering the event.
trigger str Change trigger-mode. rising, falling, both,``true``, false. If rising, the event will be triggered if cond changes from False to True. If falling, the other way around. both for rising and falling. true for always, if condition is true. false for always, if condition is false.
active bool If True, this event will be active.
id str The event-ID used to trigger actions.
return str Unused
text str See LoggerPlugin.event()
dname str
sname str  
priority 0,1 or 2  

Global actions and events can also be configured during runtime in the telegram-bot.

Graphical user interface

_images/overview3.png

You can use the GUI to control your plugins, manipulate your measurements, try scripts, import data and configure your settings. You can also view and control RTOC-servers remotely and subscribe to signals, which will then be casted to your device.

Titlebar

Systembar-structure

Systembar
├── File
│   ├── Load session
│   ├── Save session
│   ├── Import data
│   ├── Export data
│   ├── Minimize to system tray
│   ├── Settings
|   ├── Quit
├── Devices
│   ├── Add/Remove devices
├── Network
│   ├── Websocket-Server
│   ├──   ├── Active
│   ├──   ├── Password-Protection
│   ├──   ├── Port: 5050
│   ├── Telegram-Bot
│   ├──   ├── Active
│   ├──   ├── Bot-Token
│   ├── Connect to remote host
│   ├──   ├── New host
│   ├──   ├── ...
│   ├── Active connections
│   ├──   ├── ...
│   ├── Update-Rate: 1Hz
│   ├── Search RTOC-Server
├── Database
│   ├── Save local data in database
│   ├── Reset local data with data from database
│   ├── Export database
├── Subwindows
│   ├── Devices
│   ├── Plugins
│   ├── Script
│   ├── Events
│   ├── DevicesRAW
├── Language
│   ├── German
│   ├── English
├── Help
│   ├── Clear cache
│   ├── RTOC Help
│   ├── Plugin-repository
│   ├── Check for updates
│   ├── About

Subtitlebar

  • Number-Edit for local recordLength
  • Delete data: Deletes all existing data.
  • Open new plot window. You can simply drag’n’drop signals from one window into another.

Device Widget

The device widget (left) holds all plugins. Each plugin is represented by a button. You can start/stop plugins with these buttons. If the plugin has a GUI and smallGUI is True, the gui will be available as a button-dropdown.

Signal Widget

The signal widget (right) hold all signals. You can toggle each signal by clicking it (red=hidden, green=visible). Next to the button is the latest value.

Each signal also has a dropdown-menu

_images/signalWidget.png

Check out each menu-entry by yourself, please.

Event Widget

_images/eventWidget.png

The event widget shows a list with all events. You can filter the events by text or hide specific priorities. On the right top is a button to delete all events.

DevicesRAW Widget

_images/devicesRAWWidget.png

This widget holds a list with all plugin functions and parameters

Script Widget

_images/scriptWidget.png

This widget is a simple text-editor for scripts. If you want to learn more about scripts, check out Controlling and automation.

Run scripts in GUI

Click on one of the buttons at the top of this widget to run the code either once or repeated.

You can also load/save scripts from/to file.

At the right top is a help window with lists for all signals, parameters and functions.

Trigger-System
_images/scriptWidget_dropdown.png

Scripts are executed in two different ways (can be selected from the “Start” button’s drop-down menu in the ScriptWidget):

  • Samplerate-Triggered: script is executed periodically
  • Signal-Triggered: Script is executed, if a new signaldata is received. You can select multiple trigger-signals. In this case, the latest xy-pairs of the triggered signals can still be modified.

Settings Widget

_images/settingsWidget.png

This widget can modify the config.json

Import/Export signals/sessions

Import session
  1. Open “File”->”Load session” in the menubar
  2. Select a file you want to import

or

  1. Drag’n’Drop a file or copied data into RTOC
Import XLSX, MATLAB, CSV
_images/importWidget.png

On the left side is the data-table. You can modify it to your needs.

On the right side you can define signals, that will be taken from the data-table. 1. Click (+) to add a new signal. 2. Set a signal and devicename (not needed) 3. Select columns for X and Y data. If X-Column is 0, X-data will be generated automatically 4. The color on the right of each signal indicates, if this signal can be imported (mouse-over gives more information on failure) 5. Click “Import data” to load the signals to RTOC. Invalid signals will be skipped

Remote-control via Websocket

_images/remoteWidget.png

You can connect to any remote RTOC in the ‘Network’-menu of the Titlebar.

Plugin-Downloader

_images/pluginDownloader.png

This tool can automatically download, update and remove signals from the Plugin repository.

Websocket Communication

**Websocket Port: 5050, SSL: 443 **

The content is formatted as a JSON file (Python: dict) with the following keys (all optional). For more information: RTLogger.NetworkFunctions

dict-key Description
``authorize = “hashed_password” You must always send this command in the beginning (If not password-protected: authorize=True)
plot = False True: x and y data are plotted as one signal. False: x and y data are signals in pairs. See LoggerPlugin.plot() for more information.
x = [0,1,2,3,4,5] X data to be sent. If x is not set and plot = False, time.time() is set as x-value. If plot = True, the indices of the y data are used. See LoggerPlugin.plot() for more information.
y = [1,2,3,4,5,6] Y-data to be sent. See LoggerPlugin.plot() for more information.
sname = ["signalname"] List of signal names with plot = False, only one element with plot = True. See LoggerPlugin.plot() for more information.
dname = "devicename" Device name to be transmitted. See LoggerPlugin.plot() for more information.
unit = "unit" signal unit. See LoggerPlugin.plot() for more information.
event = {text = "", dname='', sname='', x=clock, priority=0} Create an event. See LoggerPlugin.event() for more information.
getLatest= True If getLatest=True, RTOC returns a Dict of the most current measured values. The signal names are the keys. See NetworkFunctions.getLatest() for more information.
getSignalList = True If getSignalList=True, RTOC returns a list of signal names. See NetworkFunctions.getSignalList() for more information.
getEventList = True If getEventList=True, RTOC returns a list of all events. See NetworkFunctions.getEventList() for more information.
getPluginList = True If getPluginList =True, RTOC returns a Dict of the plugins containing the plugin functions, parameters and the status of the plugin. See NetworkFunctions.getPluginList() for more information.
getEvent = ['Device.Signal',...] Server request for the events of a signal. See NetworkFunctions.getEvent() for more information.
getSignal = ['Device.Signal',...] Server request for signal data. See NetworkFunctions.getSignal() for more information.
getSession = True  
remove = ['signal', 'DEVICE.SIGNAL']  
subscribe = ['signal', 'DEVICE.SIGNAL']  
unsubscribe = ['signal', 'DEVICE.SIGNAL']  
subscribeAll = True  
unsubscribeAll = True  
plugin = {...} Access to plugins with Dict. See NetworkFunctions.handleTcpPlugins() for more information.
logger = {...} RTOC default functions. See /RTLogger/RTWebsocketServer() for more information.
userAction = {...}  
automation = {...}  

As response RTOC delivers a dict with the following keys:

dict-key Description
error = False If True, an error has occurred in the transmission
sent = False Is True if data (x,y) has been transmitted to the server.
signalList = [] Contains list of devices, at getSignalList-Request
pluginList= {} Dict with plugins, with getPluginList-Request
signals = {} Dict with signals, with getSignal-Request
events = {} Dict with events, at getEvent-Request
latest = {} Dict with latest measured values, at getLatest-Request
userAction = {}  
automation = {}  
logger = {}  
plugin = {}  
remove = {}  
getSession = {}  

Python example (With /RTLogger/RTWebsocketClient)

This example uses the module /RTLogger/RTWebsocketClient:

import RTWebsocketClient

data = {'x':[0,1,2,3],'y':[1,2,3,4],'dname':'Test','sname':['T1','T2','T3','T4']}
sock = RTWebsocketClient.RTWebsocketClient()
sock.on_message = lambda msg: print(msg)
sock.connect('127.0.0.1', 5050)
sock.send(data)
input('Press enter to quit')
sock.close()

Have a look into the RTWebsocketClient-File to see, which websocket-callbacks are available.

Backend Source-code

LoggerPlugin.py

This class is imported by any plugin written for RTOC.

It includes all functions to interact with RTOC. Every plugin must inherit this class! Your plugin must look like this

::

    from RTOC.LoggerPlugin import LoggerPlugin

    class Plugin(LoggerPlugin):
        def __init__(self, *args, **kwargs):
            LoggerPlugin.__init__(self, *args, **kwargs)
            ...
        ...

If you need to pass arguments to your class initialization, use kwargs. (‘stream’, ‘plot’, ‘event’, ‘telegramBot’ cannot be used as kwarg-name)

jsonsocket.py

RTOC.RTLogger Submodules

RTOC.RTLogger.Daemon module

Generic linux daemon base class for python 3.x.

Source: https://web.archive.org/web/20160320091458/http://www.jejik.com/files/examples/daemon3x.py

class Daemon(pidfile)[source]

Bases: object

A generic daemon class.

Usage: subclass the daemon class and override the run() method.

daemonize()[source]

Deamonize class. UNIX double fork mechanism.

delpid()[source]
restart()[source]

Restart the daemon.

run()[source]

You should override this method when you subclass Daemon.

It will be called after the process has been daemonized by start() or restart().

start()[source]

Start the daemon.

stop()[source]

Stop the daemon.

RTOC.RTLogger.DeviceFunctions module
RTOC.RTLogger.EventActionFunctions module
class EventActionFunctions[source]

Bases: object

This class contains all global event/action-specific functions of RTLogger

addGlobalAction(name='testAction', listenID='testID', script='', parameters='', active=True)[source]
addGlobalEvent(name='testEvent', cond='', text='TestEvent', priority=0, retur='', id='testID', trigger='rising', sname='', dname='', active=True)[source]
checkGlobalActions(actions)[source]

Checks if actions are valid. Will add the key ‘errors’ to each event. ‘errors’ will be True, if it’s not valid.

Parameters:actions (dict) – actions to be checked.
Returns:Input-actions + ‘errors’-key
Return type:actions (dict)
checkGlobalEvents(events)[source]

Checks if events are valid. Will add the key ‘errors’ to each event. ‘errors’ will be True, if it’s not valid.

Parameters:events (dict) – events to be checked.
Returns:Input-events + ‘errors’-key
Return type:events (dict)
editGlobalAction(name='testAction', listenID='testID', script='', parameters='', active=True)[source]
editGlobalEvent(name='testEvent', cond='', text='TestEvent', priority=0, retur='', id='testID', trigger='rising', sname='', dname='', active=True)[source]
loadGlobalActions()[source]

Loads global actions from file and stores them in dict ‘self.globalActions’

loadGlobalEvents()[source]

Loads global events from file and stores them in dict ‘self.globalEvents’

performGlobalActions(id, value)[source]
performGlobalEvents(y, unit, devicename, signalname, x=None)[source]

Performs global events, if their conditions are fullfilled. This function is called any time data is added to signals.

Parameters:
  • y (float) – y-value of signal (not needed)
  • unit (str) – unit of signal (not needed)
  • devicename (str) – devicename of signal
  • signalname (str) – signalname of signal
  • x (float) – x-value of signal (not needed)
printGlobalActions()[source]
printGlobalEvents(wo=True)[source]
removeGlobalAction(key)[source]
removeGlobalEvent(key)[source]
resetGlobalEventState()[source]
saveGlobalActions()[source]

Saves global actions from ‘self.globalActions’ to file.

saveGlobalEvents()[source]

Saves global events from ‘self.globalEvents’ to file.

triggerGlobalAction(key)[source]
triggerGlobalEvent(key)[source]
translate(id, text)[source]
RTOC.RTLogger.NetworkFunctions module
RTOC.RTLogger.RTLogger module
RTOC.RTLogger.RTOC_Web module
RTOC.RTLogger.RTOC_Web_standalone module
RTOC.RTLogger.RTRemote module
RTOC.RTLogger.RT_data module
RTOC.RTLogger.ScriptFunctions module
class ScriptFunctions[source]

Bases: object

This class contains all script-execution-specific functions of RTLogger

checkCondition(conditionStr)[source]
createConditionFunction(s)[source]
createFunction(s)[source]
executeScript(scriptStr)[source]
generateCode(s, condition=False)[source]
generateTriggerCode(scriptStr)[source]
printfunction()[source]
replaceGlobalVariables(s)[source]
replaceLibraryFunctions(s)[source]
replaceLoggerFunctions(s)[source]
replacePluginMethods(s)[source]
replacePluginParameters(s)[source]
replaceSignalNames(s)[source]
replaceTelegramFunctions(s)[source]
triggerExpressionHandler(expression)[source]
RTOC.RTLogger.importCode module
importCode(code, name, add_to_sys_modules=0)[source]

Import dynamically generated code as a module. code is the object containing the code (a string, a file handle or an actual compiled code object, same types as accepted by an exec statement). The name is the name to give to the module, and the final argument says wheter to add it to sys.modules or not. If it is added, a subsequent import statement using name will return this module. If it is not added to sys.modules import will try to load it in the normal fashion.

import foo

is equivalent to

foofile = open(“/path/to/foo.py”) foo = importCode(foofile,”foo”,1)

Returns a newly generated module.

RTOC.RTLogger.loggerlib module
RTOC.RTLogger.scriptLibrary module
RTOC.RTLogger.telegramBot module

GUI - Source-code

RTOC.RTOC module

RTOC.RTOC_GUI subpackage

RTOC.RTOC_GUI.Actions module
RTOC.RTOC_GUI.RTPlotActions module
RTOC.RTOC_GUI.RTPlotWidget module
RTOC.RTOC_GUI.csvSignalWidget module
RTOC.RTOC_GUI.define module
RTOC.RTOC_GUI.eventWidget module
RTOC.RTOC_GUI.globalActionWidget module
RTOC.RTOC_GUI.globalEventWidget module
RTOC.RTOC_GUI.remoteWidget module
RTOC.RTOC_GUI.scriptHelpWidget module
RTOC.RTOC_GUI.scriptSubWidget module
RTOC.RTOC_GUI.scriptWidget module
RTOC.RTOC_GUI.settingsWidget module
RTOC.RTOC_GUI.signalEditWidget module
RTOC.RTOC_GUI.signalWidget module
RTOC.RTOC_GUI.styleMultiPlotGUI module
RTOC.RTOC_GUI.stylePlotGUI module
RTOC.RTOC.PluginDownloader module
RTOC.RTOC.RTOC_Import module

RTOC.lib package

RTOC.lib.general_lib module
RTOC.lib.pyqt_customlib module

Example: Embedded

This example uses the following RTOC-features:

  • PostgreSQL-database for long-time logging
  • Telegram-Bot with different permissions
  • Remote-Websocket connection
  • Automation (Event-Action-System)

Introduction

_images/example_heating.jpg

This example will show, how to connect a heatpump or any other heating system with an RTOC-server. This will allow remote control and long-time-measurements. (And events,… all RTOC-Features)

Person A and B are administrators of the RTOC-server. Person A from outside the local network using the Telegram-Bot and Person B in the same network with the RTOC-server using a PC.

Person C is a user, who just wants to control the heating system (Smart Home). He/she will be able to set the room-temperature or any other custom functions with the telegramBot.

Person D is just interested in your heating’s long-time-measurements (e.g. a technician) and has access with the telegramBot.

Prerequirement

  • Raspberry Pi
  • Heating system/heat pump with remote control capability
  • RTOC-Plugin to interact with the heating system (e.g. Heliotherm from RTOC_repo)

Setup

  1. Install RTOC: Installation
  2. Setup PostgreSQL Database: Long-time measurements in postgreSQL database (optional)
  3. Setup Telegram-Bot: Telegram-Bot setup
  4. You should also set a websocket-password in config.json
  5. Download/program a plugin. Refer to Plugin repository and Writing Plugins.
  6. Add the plugin to autorun_devices.
  7. Start RTOC with python3 -m RTOC. Use loggerServer.py to run RTOC as a service on linux.

Remote access

Remote-Websocket-Client (Person B)

Person B uses a computer with RTOC installed. Have a look at the Graphical user interface-Documentation for more information. He can view all of the data in the database of the RTOC-server and has access to all functions and parameters from the heating system. He can manipulate the data with the RTOC-GUI. Person B can instead also use the RTOC_Remote Android App

Admin-Telegram-Client (Person A)

Person A uses the Telegram-Bot to access the heating system. He has also access to all functions and parameters from the heating system and can view and edit all of the data. He can also modify the event-action (automation).

Custom-Telegram-Client (Person C)

Person C just wants to set the room temperature for example. But does not want to use the complexe structure of the Telegram-Bot to do this (Devices->Heatpump->Functions->SetTemp). Therefore the telegram administators can set the permissions for Person C to ‘custom’. Then Person C will only see shortcuts, which have been created for only this user. This can be done in two ways: 1. Set the permission of Person C to ‘Write’ and create shortcuts for the desired functions/parameters and change the permission to ‘Custom’ afterwards. 2. Edit the client-shortcuts in telegram_clients.json.

Read-Telegram-Client (Person D)

Person D just wants to view the measurements. Therefore an administrator can set his permission to ‘Read’.

Submit your plugin

If you want to publish your plugin in my repository, send me a an email: kellersebastian@protonmail.com