Skip to content

Commit

Permalink
Add example python scripts
Browse files Browse the repository at this point in the history
Signed-off-by: Lukas Bromig <lukas.bromig@tum.de>
  • Loading branch information
Lbromig committed Jan 29, 2021
1 parent 8278a76 commit e112609
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 5 deletions.
6 changes: 3 additions & 3 deletions docs/source/04_How-To.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ investigate which (observable) commands and (observable) properties are implemen
commands and properties shows the user useful information on functionality and usage. Required parameters and responses
are displayed with the attributed SiLA-datatype.

.. image:: _static/figures/devices.png
.. image:: /docs/source/_static/figures/devices.png
:width: 800
:alt: A view of the main page, the devices list, including general device details

Expand All @@ -59,13 +59,13 @@ basic information it was registered with by the server. This information is used
dynamic client. The client files are stored in the local temporary folder named after the devices server-UUID:
Relative path to the directory: *[...]temp/device-manager/SiLA/<device-UUID>/*

.. image:: _static/figures/discovery.png
.. image:: docs/source/_static/figures/discovery.png
:width: 800
:alt: A view of the discovery feature for adding new devices to the manager

SiLA Explorer - The device tree
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Each device is that is added to the device manager is assigned an internal UUID. This way devices with the same server
Each device that is added to the device manager is assigned an internal UUID. This way devices with the same server
name can be uniquely identified. The device tree enables the user to run commands and request properties interactively
from within the browser. On the lowest level of the device tree, the command/property level, a run button can be clicked
to execute the function. For functions that require user input, the parameters can be entered in the corresponding text
Expand Down
36 changes: 36 additions & 0 deletions examples/database_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from influxdb import InfluxDBClient
from datetime import datetime
import numpy as np
import time

# Instantiate the database client.
influx_client = InfluxDBClient(host='localhost', port=8086, username='root', password='root', database='device_manager')

# Check connection
print(f'Checking connectivity. DB server version: {influx_client.ping()}')

for i in range(0, 100, 1):
# This is an example write operation
data_point = {
"measurement": "testMeasurement",
"tags": {
"experiment_name": "influxDB_test"
},
"time": datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ"),
"fields": {
"test_number": np.random.rand(1)
}
}

try:
influx_client.write_points([data_point])
except:
print("This did not work...")

# This is an example query.
results = influx_client.query(
'SELECT test_number FROM "device_manager"."autogen"."testMeasurement" WHERE experiment_name = \'influxDB_test\' GROUP BY position'
'ORDER BY DESC LIMIT 1')
print(results)

time.sleep(10)
16 changes: 16 additions & 0 deletions examples/device_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import devices

# WIP
# This example will show you how to import a device client
# This is work in progress

# ...
yourObject = devices['HelloSiLA_server']()
# You can call functions as described for every command and property in the device feature explorer under "Usage"
# To call the property "StartYear" of the HelloSiLA example device use:
StartYear = yourObject.call_property("GreetingProvider","StartYear")

# To run the "SayHello" command use:
response = yourObject.call_command("GreetingProvider","SayHello",parameters={"Name": 'some name'})

# Note: you need to replace the "yourObject" part of the command with the client object of that device!
122 changes: 122 additions & 0 deletions examples/fermentation_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import time
import numpy as np
import devices
import interface.DASGIP.DASGIP_Service.DASGIP_Service_client as client
import logging
import pandas


def initialize_logging():
# initialize logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.propagate = False

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# file handler
filehandler = logging.FileHandler(__file__ + '.log', mode='a')
filehandler.setLevel(logging.INFO)
filehandler.setFormatter(formatter)
logger.addHandler(filehandler)

# console handler, i.e. output to command window
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)

return logger


def start_feed_pump(reactor_client, units: list):
""" Start reactor feed pump C of the reactor """
for unit in units:
reactor_client.PumpCServicer_SetCmd(unit, 2)


def stop_feed_pump(reactor_client, units):
""" Stop reactor feed pump C of the reactor """
for unit in units:
reactor_client.PumpCServicer_SetCmd(unit, 1)


def calculate_pump_duration(elapsed_time: float, feed_rate: float, pump_rate: float):
""" Calculates the time the pump must run to transfer a discrete volume """
volume_to_pump = (elapsed_time / 60) * feed_rate # feed rate has the unit ml/h, elapsed time has the unit minutes
logger.info(f'The volume to pump is {volume_to_pump}')
pump_time_min = (volume_to_pump / pump_rate) * 60 # pump rate has the unit ml/h
return pump_time_min


if __name__ == '__main__':
# Instantiate the devices
reactor_client = devices['DasgipService']()
offgas_analytics_client = devices['BlueVaryService']()
external_pump_client = devices['RegloICCService']()

# Instantiate database client

# Instantiate logging
logger = initialize_logging()

# Define process parameter variables
reactor_units = [0, 2] # 0-based counting
reactor_pump_rate = 40 # The applied pump rate in the reactor, in this case max rate is used
reactor_feed_rate = 2.4 # Total feed rate in ml / h
experiment_duration = 16 * 60 * 60
increment = 0.1


# Import the discrete substrate addition times and volumes from an external file
df_feeding_intervals = pandas.read_csv("feeding_interval.csv")
print(df_feeding_intervals)
feed_intervals = df_feeding_intervals['time_interval, min']
print(feed_intervals)

# Set initial pump rate, avoids manual work
for reactor_unit in reactor_units:
# Set reactor feed pump setpoints
reactor_client.PumpCServicer_SetSetpointSelect(reactor_unit, 1)
reactor_client.PumpCServicer_SetSPM(reactor_unit, reactor_pump_rate)
logger.info(f'Setpoint is set to {reactor_pump_rate} ml/h for the units {reactor_units}')

# Start of the main loop
experiment_start_time = time.time()
start_time = time.time()
logger.info(f' Start time is {start_time}')
# Iterate over the discrete entries of feeding events in the file to simulate an intermittent feeding strategy
# with a peristaltic pump in a L-scale fermenter
for index, off_timer in feed_intervals.items():
exit_criterion: bool = False
delta_t: float = 0

# Wait for the off-timer to elapse
logger.debug(f'Waiting for off-timer ({off_timer}) to elapse')
# We wait until the next feeding event is reached

while delta_t < off_timer:
if time.time() >= (experiment_start_time + experiment_duration):
# If the defined experiment duration is reached, the exit procedure is initiated
logger.debug(f'Exiting experiment main loop at {time.time()}/ {experiment_start_time + experiment_duration}')
exit_criterion = True
break
time.sleep(increment)
delta_t = (time.time() - start_time) / 60

if exit_criterion:
# break out of the for-loop
break
# Re-initialize the start time while the pump is working
start_time = time.time()
logger.info(f' Start time is {start_time}')

# Pump the desired volume while the off-timer starts to elapse
pump_time_min = calculate_pump_duration(elapsed_time=off_timer, feed_rate=reactor_feed_rate, pump_rate=reactor_pump_rate)
logger.info(f'The scheduled pump run time in minutes is {pump_time_min}')

# Start the pump
start_feed_pump(reactor_client=reactor_client, units=reactor_units)
time.sleep(pump_time_min)
# Stop the pump
stop_feed_pump(reactor_client=reactor_client, units=reactor_units)
5 changes: 5 additions & 0 deletions examples/hello_world.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# You can use this code editor like a regular scripting environment.
# If you require specific python packages for your script, you can import them here.
# Note: Packages you want to import must be specified in the dockerfiles requirements.txt!

print("Hello World!")
5 changes: 3 additions & 2 deletions setup_test_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@
'# To call the property "StartYear" of the HelloSiLA example device use:\n'
'StartYear = yourObject.call_property("GreetingProvider","StartYear")\n\n'
'# To run the "SayHello" command use:\n'
'response = yourObject.call_command("GreetingProvider","SayHello",parameters: { "Name": })\n\n'
'response = yourObject.call_command("GreetingProvider","SayHello",parameters={"Name": \'some name\'})\n\n'
'# Note: you need to replace the "yourObject" part of the command with the client object of that device!'
},
{
Expand All @@ -458,12 +458,13 @@
'data': 'from influxdb import InfluxDBClient\n'
'from datetime import datetime\n'
'import numpy as np\n\n\n'
'import time'
'# Instantiate the database client.\n'
'influx_client = InfluxDBClient(host=\'localhost\', port=8086, username=\'root\', password=\'root\', database=\'device_manager\')\n\n'
'# Check connection\n'
'print(f\'Checking connectivity. DB server version: {influx_client.ping()}\')\n\n'
'for i in range(0, 100, 1):\n'
'# This is an example write operation\n'
'\t# This is an example write operation\n'
'\tdata_point = {\n'
'\t\t"measurement": "testMeasurement",\n'
'\t\t"tags": {\n'
Expand Down

0 comments on commit e112609

Please sign in to comment.