"""
This file gets executed when a new client side session is created. The functions of :file:`app_hook.py`
will already have returned the corresponding objects and variables that are needed to run this script.
"""
# os methods for manipulating paths
import os
# json, OrderedDict to get data format for Import/Export database
import json
from collections import OrderedDict
# Bokeh basics
from bokeh.io import curdoc
from bokeh.models.widgets import Tabs
from bokeh.models import Button, Div
from bokeh.layouts import column, row
# import class for audio data handling
from sensors.Utilities.pyaudiohandler import PyAudioHandler
from sensors.Utilities.write_meta_data import WriteMetaData
# import tab classes
from sensors.GUIclasses.info import InfoTab
from sensors.GUIclasses.picam_stream import CamStreamTab
from sensors.GUIclasses.audio_stream import AudioStreamTab
from sensors.GUIclasses.triggered_save import TriggeredSaveTab
from sensors.GUIclasses.single_save import SingleSaveTab
from sensors.GUIclasses.data_viewer import DataViewerTab
from sensors.GUIclasses.export import ExportTab
import RPi.GPIO as GPIO
from concurrent.futures import ThreadPoolExecutor
[docs]def panelActive(attr, old, new):
"""Function gets called on tab change. It deactivates the ringbuffer update if its tab
is not active to save resources. Analogous behaviour for triggered respectively single save tab.
"""
# only execute if audio device found, stops ringbuffer stream if corresponding tab is not active
if config.audio_devices[1] is not None:
if all_tabs.active == 0:
if visual_ringbuffer.btn_wingbeats.label == 'Stop Stream':
visual_ringbuffer.do_update()
elif all_tabs.active == 1:
if visual_ringbuffer.btn_wingbeats.label == 'Stop Stream':
visual_ringbuffer.do_update()
elif all_tabs.active == 2:
if visual_ringbuffer.btn_wingbeats.label == 'Stop Stream':
visual_ringbuffer.do_update()
elif all_tabs.active == 3:
# if visual_trigger.btn_wingbeats.label == 'Stop Stream':
# visual_trigger.do_update()
pass
else:
if visual_ringbuffer.btn_wingbeats.label == 'Stop Stream':
visual_ringbuffer.do_update()
# if visual_trigger.btn_wingbeats.label == 'Stop Stream':
# visual_trigger.do_update()
[docs]def add_viewer_tab():
"""Function gets called on button click. It adds an extra ViewerTab and updates
the Bokeh visualization.
"""
curdoc().session_context.request.viewer_counter += 1
all_tabs.tabs.append(DataViewerTab(config).tab)
all_tabs.update(tabs=all_tabs.tabs)
[docs]def shutdown_server():
"""Function gets called on button click. Shuts down Bokeh server.
"""
print('Shutting down Bokeh server!')
GPIO.setmode(GPIO.BCM)
GPIO.setup(config.GPIO_Switch, GPIO.OUT)
GPIO.output(config.GPIO_Switch, GPIO.LOW)
exit(0)
# the following lines of code get executed if a bokeh server is instantiated,
# but will be ignored if Sphinx is creating an auto documentation, "docs" for local and "source" for RTD html creation
if os.path.basename(os.path.normpath(os.path.abspath('.'))) == 'docs' or \
os.path.basename(os.path.normpath(os.path.abspath('.'))) == 'source':
print('Sphinx automatic api documentation running!')
else:
# get config from on_session_load in app hooks
config = curdoc().session_context.server_context.config
# initialize ThreadPoolExecutor and pass to config
executor = ThreadPoolExecutor(max_workers=10)
config.executor = executor
# add curdoc to config to pass to tab objects
config.curdoc = curdoc()
# initialize data structure JSON file and add to config and pass to tab objects
if os.path.exists('sensors/templates/data_template.json'):
with open('sensors/templates/data_template.json', encoding='UTF-8') as f:
data_template_dict = json.load(f, object_pairs_hook=OrderedDict)
f.close()
data_template_dict["sensor_id"] = config.sensor_id
data_template_dict["user_id"] = config.user_id
data_template_dict["sensor_location"]["postcode"] = config.postcode
data_template_dict["sensor_location"]["name"] = config.name
data_template_dict["sensor_location"]["latitude"] = config.latitude
data_template_dict["sensor_location"]["longitude"] = config.longitude
data_template_dict["sensor_location"]["gps_masked"] = config.gps_masked
print('Loaded data format!')
else:
print('No data format template file found!\nShutting down Bokeh server!')
exit(0)
config.data = data_template_dict
# initialize species JSON file and add to config and pass to tab objects
if os.path.exists('sensors/templates/species_template.json'):
with open('sensors/templates/species_template.json', encoding='UTF-8') as f:
species_template_dict = json.load(f, object_pairs_hook=OrderedDict)
f.close()
print('Loaded species list!')
else:
print('No data format template file found!\nShutting down Bokeh server!')
exit(0)
config.species = species_template_dict
# initialize tab list
tabs_list = list()
# add info tab
tabs_list.append(InfoTab(config).tab)
# include control tabs if it's the only active session (app_hooks.py) and corresponding devices are connected
# reload is not working atm, because always 1 active session existent (destruction of old session happening
# after creation of new one)
if curdoc().session_context.request.master_session:
if config.camera is not None:
visual_camera = CamStreamTab(config)
config.visual_camera = visual_camera
tabs_list.append(visual_camera.tab)
if config.audio_devices[1] is not None:
if config.wb_triggered_save:
visual_trigger = TriggeredSaveTab(config)
else:
visual_trigger = SingleSaveTab(config)
tabs_list.append(visual_trigger.tab)
visual_ringbuffer = AudioStreamTab(config)
# tabs_list.append(visual_ringbuffer.tab)
# initialize audio thread, save audio object to session_context to kill thread after session destroyed
audio = PyAudioHandler(visual_trigger, visual_ringbuffer)
audio.start()
curdoc().session_context.audio = audio
# add export tab
tabs_list.append(ExportTab(config).tab)
# add viewer tab
tabs_list.append(DataViewerTab(config).tab)
# create tab object from tab list, only check for active tabs in master session
all_tabs = Tabs(tabs=tabs_list)
if curdoc().session_context.request.master_session:
all_tabs.on_change('active', panelActive)
# create exit button
btn_exit = Button(label='Shutdown server', button_type="danger", width=115)
btn_exit.on_click(shutdown_server)
# create extra ViewerTab at button click
btn_add_tab = Button(label='Add Data Viewer', button_type='primary', width=115)
btn_add_tab.on_click(add_viewer_tab)
# add osi logo and final layout of all elements in current document
logo_osi = """<div><img src="sensors/static/logos/osi.svg" width="32"></div>"""
logo_kinsecta = """<div><img src="sensors/static/logos/kinsecta.svg" width="32" ></div>"""
curdoc().add_root(column(row(Div(text=logo_kinsecta), Div(text=logo_osi), btn_exit, btn_add_tab), all_tabs))
# browser tab name
curdoc().title = 'KInsecta Multisensors - Bokeh'