Select Git revision
annot3-RNA-01-extract_complete_ORFs.sh
poolview_sql.py 127.08 KiB
import io
import os
import importlib
import json
import struct
import threading
import subprocess
import time
import sys
import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
import DBus_Basic
import ccs_function_lib as cfl
from typing import NamedTuple
import confignator
import gi
import matplotlib
matplotlib.use('Gtk3Cairo')
gi.require_version('Gtk', '3.0')
gi.require_version('Notify', '0.7')
from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, Notify, Pango # NOQA
from event_storm_squasher import delayed
# from matplotlib.figure import Figure
# from matplotlib.backends.backend_gtk3cairo import FigureCanvasGTK3Cairo as FigureCanvas
# from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
# from matplotlib.backends.backend_gtk3 import NavigationToolbar2GTK3 as NavigationToolbar
# from sqlalchemy.sql.expression import func, distinct
from sqlalchemy.orm import load_only
from database.tm_db import DbTelemetryPool, DbTelemetry, RMapTelemetry, FEEDataTelemetry, scoped_session_maker
ActivePoolInfo = cfl.ActivePoolInfo
cfg = confignator.get_config(check_interpolation=False)
project = 'packet_config_{}'.format(cfg.get('ccs-database', 'project'))
packet_config = importlib.import_module(project)
TM_HEADER_LEN, TC_HEADER_LEN, PEC_LEN = [packet_config.TM_HEADER_LEN, packet_config.TC_HEADER_LEN, packet_config.PEC_LEN]
Telemetry = {'PUS': DbTelemetry, 'RMAP': RMapTelemetry, 'FEE': FEEDataTelemetry}
class TMPoolView(Gtk.Window):
# (label, data column alignment)
column_labels = {'PUS': [('#', 1), ('TM/TC', 1), ("APID", 1), ("SEQ", 1), ("len-7", 1), ("ST", 1), ("SST", 1),
("Dest ID", 1), ("Time", 1), ("Data", 0)],
'RMAP': [('#', 1), ('TYPE', 1), ('R/W', 1), ('VERIFY', 1), ('REPLY', 1), ('INCR', 1),
('KEY/STAT', 1), ('TA ID', 1), ('ADDRESS', 1), ('DATALEN', 1), ('RAW', 0)],
'FEE': [('#', 1), ('TYPE', 1), ('FRAME CNT', 1), ('SEQ', 1), ('RAW', 0)]}
tm_columns = {'PUS': {'#': [DbTelemetry.idx, 0, None], 'TM/TC': [DbTelemetry.is_tm, 0, None],
"APID": [DbTelemetry.apid, 0, None], "SEQ": [DbTelemetry.seq, 0, None],
"len-7": [DbTelemetry.len_7, 0, None], "ST": [DbTelemetry.stc, 0, None],
"SST": [DbTelemetry.sst, 0, None], "Dest ID": [DbTelemetry.destID, 0, None],
"Time": [DbTelemetry.timestamp, 0, None], "Data": [DbTelemetry.data, 0, None]},
'RMAP': {'#': [RMapTelemetry.idx, 0, None], 'TYPE': [RMapTelemetry.cmd, 0, None],
'R/W': [RMapTelemetry.write, 0, None], "VERIFY": [RMapTelemetry.verify, 0, None],
"REPLY": [RMapTelemetry.reply, 0, None], 'INCR': [RMapTelemetry.increment, 0, None],
"KEY": [RMapTelemetry.keystat, 0, None], "TA ID": [RMapTelemetry.taid, 0, None],
"ADDRESS": [RMapTelemetry.addr, 0, None], "DATALEN": [RMapTelemetry.datalen, 0, None],
"RAW": [RMapTelemetry.raw, 0, None]},
'FEE': {'#': [FEEDataTelemetry.idx, 0, None], 'TYPE': [FEEDataTelemetry.type, 0, None],
"FRAME CNT": [FEEDataTelemetry.framecnt, 0, None],
"SEQ": [FEEDataTelemetry.seqcnt, 0, None],
"RAW": [FEEDataTelemetry.raw, 0, None]}}
sort_order_dict = {0: Gtk.SortType.ASCENDING, 1: Gtk.SortType.ASCENDING, 2: Gtk.SortType.DESCENDING}
filter_rules = {}
rule_box = None
tmtc = {0: 'TM', 1: 'TC'}
w_r = {0: 'R', 1: 'W'}
cmd_repl = {0: 'rep', 1: 'cmd'}
sort_order = Gtk.SortType.ASCENDING
pckt_queue = None
queues = {}
row_colour = ''
colour_filters = {}
# active_pool_info = None # type: Union[None, ActivePoolInfo]
decoding_type = 'PUS'
live_signal = {True: '[REC]', False: None}
currently_selected = set()
shift_range = [1, 1]
active_row = None
selected_row = 1
cursor_path = 0
pool_refresh_rate = 1 / 10.
last_decoded_row = None
row_buffer_n = 100
main_instance = None
first_run = True
only_scroll = False
CELLPAD_MAGIC = float(cfg['ccs-misc']['viewer_cell_pad'])
def __init__(self, cfg=cfg, pool_name=None, cfilters='default', standalone=False):
Gtk.Window.__init__(self, title="Pool View", default_height=800, default_width=1100)
self.refresh_treeview_active = False
self.cnt = 0
# self.active_pool_info = ActivePoolInfo(None, None, None, None)
self.active_pool_info = ActivePoolInfo('', 0, '', False)
self.set_border_width(2)
self.set_resizable(True)
self.set_default_size(1150, 1280)
# self.set_position(Gtk.WindowPosition.CENTER)
self.set_gravity(Gdk.Gravity.NORTH_WEST)
Notify.init('PoolViewer')
# self.cfg = confignator.get_config()
self.cfg = cfg
self.autoscroll = 1
self.autoselect = 1
self._selected_mon_par_set = None
self.paned = Gtk.Paned(orientation=Gtk.Orientation.HORIZONTAL, wide_handle=True, position=400)
self.statusbar = Gtk.Statusbar()
self.statusbar.set_halign(Gtk.Align.END)
grid = Gtk.VBox()
grid.pack_start(self.paned, 1, 1, 0)
grid.pack_start(self.statusbar, 0, 0, 0)
self.add(grid)
self.grid = Gtk.Grid()
self.grid.set_column_homogeneous(True)
self.grid.set_row_homogeneous(False)
self.paned.add2(self.grid)
self.set_events(self.get_events() | Gdk.EventMask.SCROLL_MASK)
self.treebox = self.create_treeview()
self.grid.attach(self.treebox, 0, 3, 1, 12)
self.create_pool_managebar()
self.grid.attach(self.pool_managebar, 0, 0, 1, 1)
self.filterbar = self.create_filterbar()
self.grid.attach(self.filterbar, 0, 1, 1, 1)
self._add_rulebox()
dataview = self.create_tm_data_viewer()
self.paned.add1(dataview)
self.set_keybinds()
self.key_held_pressed = False
sets = self.get_settings()
sets.set_property('gtk-error-bell', False)
if self.cfg.has_section('ccs-pool_colour_filters') and (cfilters is not None):
for cfilter in json.loads(self.cfg['ccs-pool_colour_filters'][cfilters]):
self.add_colour_filter(cfilter)
self.rgba_black = Gdk.RGBA()
self.rgba_black.parse('black')
self.session_factory_idb = scoped_session_maker('idb')
self.session_factory_storage = scoped_session_maker('storage')
# Set up the logging module
self.logger = cfl.start_logging('Poolviewer')
if pool_name is not None:
self.set_pool(pool_name)
self.stored_packet = []
self.connect("delete-event", self.quit_func)
self.show_all()
def checking(self):
self.adj.set_value(60)
return
def quit_func(self, *args):
# Check if Poolmanager is running otherwise just close viewer
if not cfl.is_open('poolmanager', cfl.communication['poolmanager']):
self.close_terminal_connection()
self.update_all_connections_quit()
if Gtk.main_level():
Gtk.main_quit()
return False
pmgr = cfl.dbus_connection('poolmanager', cfl.communication['poolmanager'])
# Check if Gui and only close poolviewer if it is
if cfl.Variables(pmgr, 'gui_running'):
self.close_terminal_connection()
self.update_all_connections_quit()
if Gtk.main_level():
Gtk.main_quit()
return False
# Ask if Poolmanager should be cloosed too
ask = UnsavedBufferDialog(parent=self, msg='Response NO will keep the Poolmanager running in the Background')
response = ask.run()
if response == Gtk.ResponseType.NO:
Notify.Notification.new('Poolmanager is still running without a GUI').show()
elif response == Gtk.ResponseType.CANCEL:
ask.destroy()
return True
else:
self.close_terminal_connection()
self.update_all_connections_quit()
ask.destroy()
pmgr.Functions('quit_func_pv', ignore_reply=True)
Gtk.main_quit()
return False
self.close_terminal_connection()
self.update_all_connections_quit()
ask.destroy()
Gtk.main_quit()
return False
def close_terminal_connection(self):
# Try to tell terminal in the editor that the variable is not longer availabe
for service in dbus.SessionBus().list_names():
if service.startswith(self.cfg['ccs-dbus_names']['editor']):
editor = cfl.dbus_connection(service[0:-1].split('.')[1], service[-1])
if self.main_instance == editor.Variables('main_instance'):
nr = self.my_bus_name[-1]
if nr == str(1):
nr = ''
editor.Functions('_to_console_via_socket', 'del(pv'+str(nr)+')')
return
def update_all_connections_quit(self):
"""
Tells all running applications that it is not longer availabe and suggests another main communicator if one is
available
:return:
"""
our_con = [] # All connections to running applications without communicions form the same applications as this
my_con = [] # All connections to same applications as this
for service in dbus.SessionBus().list_names():
if service.split('.')[1] in self.cfg['ccs-dbus_names']: # Check if connection belongs to CCS
if service == self.my_bus_name: #If own allplication do nothing
continue
conn = cfl.dbus_connection(service.split('.')[1], service[-1])
if conn.Variables('main_instance') == self.main_instance: #Check if running in same project
if service.startswith(self.my_bus_name[:-1]): #Check if it is same application type
my_con.append(service)
else:
our_con.append(service)
instance = my_con[0][-1] if my_con else 0 # Select new main application if possible, is randomly selected
our_con = our_con + my_con # Add the instances of same application to change the main communication as well
for service in our_con: # Change the main communication for all applications+
conn = cfl.dbus_connection(service.split('.')[1], service[-1])
comm = conn.Functions('get_communication')
# Check if this application is the main applications otherwise do nothing
if str(comm[self.my_bus_name.split('.')[1]]) == self.my_bus_name[-1]:
conn.Functions('change_communication', self.my_bus_name.split('.')[1], instance, False)
return
def send_cfg(self):
return self.cfg
def set_pool_from_pmgr(self):
try:
poolmgr = cfl.dbus_connection('poolmanager', cfl.communication['poolmanager'])
if not poolmgr:
return
pools = cfl.Functions(poolmgr, 'loaded_pools_export_func')
if not pools:
self.logger.warning('No loaded pools found, nothing to view.')
return
for pool in pools:
if not pool[0].count('/'):
self.set_pool(pool[0])
except Exception as err:
self.logger.exception(err)
def set_pool(self, pool_name, pmgr_pools=None, instance=None):
if pool_name is None:
return
if not pmgr_pools:
try:
poolmgr = cfl.dbus_connection('poolmanager', cfl.communication['poolmanager'])
if not poolmgr:
raise TypeError('No poolmanager instance found.')
try:
self.Active_Pool_Info_append(cfl.Dictionaries(poolmgr, 'loaded_pools', pool_name))
except (dbus.DBusException, KeyError):
raise ValueError('No loaded pool {} found.'.format(pool_name))
cfl.Functions(poolmgr, 'loaded_pools_func', self.active_pool_info.pool_name, self.active_pool_info)
except Exception as err:
self.logger.warning(err)
if '/' in pool_name:
pool_path = str(os.path.realpath(pool_name))
# pool_name = pool_name.split('/')[-1]
else:
pool_path = pool_name
# change_error
attribute = [pool_path, int(round(time.time())), str(pool_name), False]
self.Active_Pool_Info_append(attribute)
else:
for pool in pmgr_pools:
if pool_name == pool[2]:
self.Active_Pool_Info_append(list(pool))
# self.Active_Pool_Info_append(pmgr_pools[pool_name])
self._set_pool_list_and_display(instance=instance)
if self.active_pool_info.live:
self.refresh_treeview(pool_name)
# print('THIS STEP IS NOT NEEDED ANYMORE')
# if self.pool is None:
# self.pool = pool
#
# thread = threading.Thread(target = self.update_packets)
# thread.daemon = True
# thread.start()
# def set_queue(self, pool_name, pckt_queue):
#
# self.queues.update({pool_name: pckt_queue})
#
# model = self.pool_selector.get_model()
#
# iter = model.append([pool_name])
#
# if model.iter_n_children() == 1:
# self.pool_selector.set_active_iter(iter)
# self.pool_liststore.clear()
# self.pckt_queue = pckt_queue
# self.pool_name = pool_name
# def update_packets_worker(self):
# pckt_queue = self.pckt_queue
# if pckt_queue is None or self.pool is None or pckt_queue.empty():
# return True
# print("Worker resumes...")
# unpack_pus = self.pool.unpack_pus
# getpq = pckt_queue.get
# appendls = self.pool_liststore.append
# cuctime = self.pool.cuc_time_str
# print("Worker resumes 2...")
# # self.treeview.set_model(None)
# self.treeview.set_model(self.pool_liststore)
# self.treeview.freeze_child_notify()
# pckt_count = 0
# while not pckt_queue.empty():
# (seq, pckt) = getpq()
# tm = unpack_pus(pckt)
# appendls([seq] + [self.tmtc[tm[1]]] + [tm[3]] + tm[5:7] + tm[10:13] + [cuctime(tm)] + [tm[-2]])
# pckt_count+=1
# if (pckt_count % 128) == 127:
# # self.treeview.set_model(self.pool_liststore)
# self.treeview.thaw_child_notify()
# print("Added:", pckt_count, "...")
# return True
#
# self.treeview.thaw_child_notify()
# print("Completed - Added:", pckt_count, "\n")
# # self.change_cursor(self.scrolled_treelist.get_window(),'default')
# return True
def change_communication(self, application, instance=1, check=True):
# If it is checked that both run in the same project it is not necessary to do it again
if check:
conn = cfl.dbus_connection(application, instance)
# Both are not in the same project do not change
if not cfl.Variables(conn, 'main_instance') == self.main_instance:
self.logger.warning('Application {} is not in the same project as {}: Can not communicate'.format(
self.my_bus_name, self.cfg['ccs-dbus_names'][application] + str(instance)))
return
cfl.communication[application] = int(instance)
return
def get_communication(self):
return cfl.communication
def connect_to_all(self, My_Bus_Name, Count):
self.my_bus_name = My_Bus_Name
#print(My_Bus_Name)
# Look if other applications are running in the same project group
our_con = []
#Look for all connections starting with com, therefore only one loop over all connections is necessary
for service in dbus.SessionBus().list_names():
if service.startswith('com'):
our_con.append(service)
# Check if a com connection has the same name as given in cfg file
for app in our_con:
if app.split('.')[1] in self.cfg['ccs-dbus_names']:
# If name is the name of the program skip
if app == self.my_bus_name:
continue
# Otherwise save the main connections in cfl.communication
conn_name = app.split('.')[1]
conn = cfl.dbus_connection(conn_name, app[-1])
if conn.Variables('main_instance') == self.main_instance:
cfl.communication = conn.Functions('get_communication')
conn_com = conn.Functions('get_communication')
if conn_com[self.my_bus_name.split('.')[1]] == 0:
conn.Functions('change_communication', self.my_bus_name.split('.')[1], self.my_bus_name [-1], False)
if not cfl.communication[self.my_bus_name.split('.')[1]]:
cfl.communication[self.my_bus_name.split('.')[1]] = int(self.my_bus_name[-1])
# Connect to the Terminal
if Count == 1:
for service in dbus.SessionBus().list_names():
if service.startswith(self.cfg['ccs-dbus_names']['editor']):
editor = cfl.dbus_connection('editor', service[-1])
editor.Functions('_to_console_via_socket', "pv = dbus.SessionBus().get_object('" + str(My_Bus_Name)
+ "', '/MessageListener')")
else:
for service in dbus.SessionBus().list_names():
if service.startswith(self.cfg['ccs-dbus_names']['editor']):
editor = cfl.dbus_connection('editor', service[-1])
editor.Functions('_to_console_via_socket', "pv" +str(Count)+ " = dbus.SessionBus().get_object('" +
str(My_Bus_Name) + "', '/MessageListener')")
# Check if pool from poolmanagers should be loaded, default is True
if '--noload' not in sys.argv:
self.set_pool_from_pmgr()
def add_colour_filter(self, colour_filter):
seq = len(self.colour_filters.keys())
rgba = Gdk.RGBA()
rgba.parse(colour_filter['colour'])
colour_filter['colour'] = rgba
self.colour_filters.update({seq: colour_filter})
def update_colour_filter(self, index, colour_filter):
self.colour_filters.update({index: colour_filter})
def del_colour_filter(self, filter_index):
self.colour_filters.pop(filter_index)
def get_colour_filters(self):
return self.colour_filters
def text_colour(self, column, cell, tree_model, iter, data=None):
# labels = list(map(list, zip(*self.column_labels)))[0]
""" this is a bit stupid and inefficient, but it does the job """
if column == self.treeview.get_column(0):
labels = [x[0] for x in self.column_labels[self.decoding_type]]
self.row_colour = self.rgba_black
for filter in self.colour_filters:
d = {}
cf = self.colour_filters[filter].copy()
cf.pop('colour')
for key in cf.keys():
d.update({key: tree_model[iter][labels.index(key)]})
if d == cf:
self.row_colour = self.colour_filters[filter]['colour']
# cell.set_property('foreground', self.row_colour)
cell.set_property('foreground-rgba', self.row_colour)
def text_colour2(self, column, cell, tree_model, iter, data=None):
if column == self.treeview.get_column(0):
labels = [x[0] for x in self.column_labels[self.decoding_type]]
d = {key: tree_model[iter][labels.index(key)] for key in labels[1:-2]} # skip idx, time and data fields
self.row_colour = self.rgba_black
for f in self.colour_filters:
cf = self.colour_filters[f].copy()
colour = cf.pop('colour')
if cf.items() <= d.items():
self.row_colour = colour
break
else:
continue
cell.set_property('foreground-rgba', self.row_colour)
def create_treeview(self):
self.pool_liststore = self.create_liststore()
self.treeview = Gtk.TreeView()
self.treeview.set_model(self.pool_liststore)
self.treeview.set_rubber_banding(False)
self.treeview.set_activate_on_single_click(True)
# self.treeview.set_fixed_height_mode(True)
self.create_treeview_columns()
self.scrolled_treelist = Gtk.ScrolledWindow()
self.scrolled_treelist.set_policy(Gtk.PolicyType.ALWAYS, Gtk.PolicyType.ALWAYS)
self.scrolled_treelist.set_overlay_scrolling(False)
self.scrolled_treelist.set_min_content_height(235)
self.scrolled_treelist.set_vexpand(True)
self.scrolled_treelist.add(self.treeview)
self.scrolled_treelist.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK)
self.scrolled_treelist.add_events(Gdk.EventMask.SCROLL_MASK)
self.connect('configure-event', self.set_number_of_treeview_rows)
# self.connect('check-resize', self.set_number_of_treeview_rows)
self.connect('window-state-event', self.resize_treeview)
self.treeview.connect('size-allocate', self.treeview_update)
# self.treeview.connect('size-allocate', self.set_tm_data_view)
self.treeview.connect('key-press-event', self.key_pressed)
# self.treeview.connect('key-press-event', self.set_tm_data_view)
# self.treeview.connect('key-press-event', self.set_currently_selected)
self.treeview.connect('button-release-event', self.set_currently_selected)
self.treeview.connect('button-press-event', self._set_current_row)
# something like that
# self.scrolled_treelist.connect('edge-reached', self.edge_reached)
# self.scrolled_treelist.connect('edge-overshot', self.edge_reached)
self.scrolled_treelist.connect('scroll-event', self.scroll_event)
# self.scrolled_treelist.connect('scroll-event', self.reselect_rows)
# self.scrolled_treelist.connect('button-release-event', self.scroll_event)
self.scrolled_treelist.connect('scroll-child', self.scroll_child)
# self.scrolled_treelist.get_vscrollbar().connect('value-changed', self.scroll_bar)
self.scrolled_treelist.get_vscrollbar().set_visible(False)
self.selection = self.treeview.get_selection()
self.selection.set_mode(Gtk.SelectionMode.SINGLE)
# self.selection.connect('changed', self.tree_selection_changed)
self.selection.connect('changed', self.set_tm_data_view)
# self.selection.connect('changed', self.unselect_bottom)
scrollbar = Gtk.VScrollbar()
self.adj = scrollbar.get_adjustment()
# get size of tmpool
if self.active_pool_info.pool_name not in (None, ''):
self.adj.set_upper(self.count_current_pool_rows())
height = self.treeview.get_allocated_height()
column = self.treeview.get_column(0)
cell_pad = column.get_cells()[0].get_padding()[1]
cell = column.cell_get_size()[-1] + cell_pad * self.CELLPAD_MAGIC
nlines = height // cell
self.adj.set_page_size(nlines)
scrollbar.connect('value_changed', self._on_scrollbar_changed, self.adj, False)
scrollbar.connect('button-press-event', self.scroll_bar)
# scrollbar.connect('value_changed', self.reselect_rows)
# Set up Drag and Drop
self.treeview.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [], Gdk.DragAction.COPY)
self.treeview.drag_source_set_target_list(None)
self.treeview.drag_source_add_text_targets()
self.treeview.connect("drag-data-get", self.on_drag_data_get)
hbox = Gtk.HBox()
hbox.pack_start(self.scrolled_treelist, 1, 1, 0)
hbox.pack_start(scrollbar, 0, 0, 0)
return hbox
def create_treeview_columns(self):
for i, (column_title, align) in enumerate(self.column_labels[self.decoding_type]):
render = Gtk.CellRendererText(xalign=align)
if column_title in ('Data', 'RAW'):
render.set_property('font', 'Monospace')
column = Gtk.TreeViewColumn(column_title, render, text=i)
column.set_cell_data_func(render, self.text_colour2)
# column.set_sort_column_id(i)
column.set_clickable(True)
column.set_resizable(True)
column.connect('clicked', self.column_clicked)
self.treeview.append_column(column)
def _set_current_row(self, widget=None, event=None):
x, y = event.x, event.y
path = widget.get_path_at_pos(x, y)
if path is not None:
self.active_row = widget.get_model()[path[0]][0]
self.set_shift_range(self.active_row)
self.autoselect = 0
self.set_tm_data_view()
def set_shift_range(self, row):
self.shift_range[0] = self.shift_range[1]
self.shift_range[1] = row
# @delayed(10)
def set_number_of_treeview_rows(self, widget=None, allocation=None):
# alloc = widget.get_allocation()
height = self.treeview.get_allocated_height()
# height = self.treeview.get_visible_rect().height
# cell = 25
column = self.treeview.get_column(0)
cell_pad = column.get_cells()[0].get_padding()[1]
cell = column.cell_get_size()[-1] + cell_pad * self.CELLPAD_MAGIC
nlines = height // cell
self.adj.set_page_size(nlines - 1)
# self._scroll_treeview()
self.reselect_rows()
def resize_treeview(self, widget, event):
if Gdk.WindowState.MAXIMIZED == event.new_window_state:
self.set_number_of_treeview_rows()
# @delayed(10)
def _on_scrollbar_changed(self, widget=None, adj=None, force=True):
if self.autoscroll and not force:
return
if self.only_scroll: # Stops second scroll event after scrollbar zeiger was reset,if scrolled by scroll wheel
return
self.offset = int(self.adj.get_value())
self.limit = int(self.adj.get_page_size())
self.feed_lines_to_view(self.fetch_lines_from_db(offset=self.offset, limit=self.limit, force_import=True))
self.reselect_rows()
def count_current_pool_rows(self, pool_info=None):
if pool_info is not None:
self.Active_Pool_Info_append(pool_info)
if self.active_pool_info is None:
return 0
new_session = self.session_factory_storage
rows = new_session.query(
Telemetry[self.decoding_type]
).join(
DbTelemetryPool,
Telemetry[self.decoding_type].pool_id == DbTelemetryPool.iid
).filter(
DbTelemetryPool.pool_name == self.active_pool_info.filename
)
if rows.first() is None:
self.logger.warning('Could not find rows for pool {}'.format(self.active_pool_info.filename))
cnt = 0
else:
cnt = rows.order_by(Telemetry[self.decoding_type].idx.desc()).first().idx
new_session.close()
return cnt
def get_current_pool_rows(self, dbsession=None):
if self.active_pool_info is None:
return 0
new_session = self.session_factory_storage
rows = new_session.query(
Telemetry[self.decoding_type]
).join(
DbTelemetryPool,
Telemetry[self.decoding_type].pool_id == DbTelemetryPool.iid
).filter(
DbTelemetryPool.pool_name == self.active_pool_info.filename
)
new_session.close()
return rows
def on_drag_data_get(self, treeview, drag_context, selection_data, info, time, *args):
treeselection = treeview.get_selection()
model, my_iter = treeselection.get_selected()
if model is not None and my_iter is not None:
new_session = self.session_factory_storage
row = new_session.query(
Telemetry[self.decoding_type]
).join(
DbTelemetryPool,
Telemetry[self.decoding_type].pool_id == DbTelemetryPool.iid
).filter(
DbTelemetryPool.pool_name == self.active_pool_info.filename
).filter(
Telemetry[self.decoding_type].idx == model[my_iter][0]
)
selection_data.set_text(str(row.first().raw), -1)
new_session.close()
def fetch_lines_from_db(self, offset=0, limit=None, sort=None, order='asc', buffer=10, rows=None, scrolled=False,
force_import=False):
"""
Reads the packages from the Database and shows it according to the position of the scrollbar
@param offset: Index of first line - 1
@param limit: How many packages should be displayed
@param sort: If the packages are sorted in any way
@param order: In which order the packages should be displayed
@param buffer: How many packages should be loaded but are not shown
@param rows: Show these rows if given
@param scrolled: True if view is scrolled
@param force_import: Import all rows again from the Database
@return: -
"""
if self.active_pool_info is None:
return
limit = self.adj.get_page_size() if not limit else limit # Check if a limit is given
if rows is None:
# Combine the Storage Tables and filter only the packages for the given pool
new_session = self.session_factory_storage
rows = new_session.query(
Telemetry[self.decoding_type]
).join(
DbTelemetryPool,
Telemetry[self.decoding_type].pool_id == DbTelemetryPool.iid
).filter(
DbTelemetryPool.pool_name == self.active_pool_info.filename
)
new_session.close()
sorted = False
# Check if the rows should be shown in any specific order
for col in self.tm_columns[self.decoding_type]:
if self.tm_columns[self.decoding_type][col][1] == 1:
rows = rows.order_by(self.tm_columns[self.decoding_type][col][0], Telemetry[self.decoding_type].idx)
sorted = True
elif self.tm_columns[self.decoding_type][col][1] == 2:
rows = rows.order_by(self.tm_columns[self.decoding_type][col][0].desc(), Telemetry[self.decoding_type].idx.desc())
sorted = True
# Check if a filter has been applied
if self.filter_rules_active:
rows = self._filter_rows(rows)
if sorted:
rows = rows.options(load_only()).yield_per(1000).offset(offset).limit(limit)
else:
rows = rows.filter(Telemetry[self.decoding_type].idx > offset).limit(limit)
return rows
def _filter_rows(self, rows):
def f_rule(x):
if x[1] == '==':
return x[0] == x[2]
elif x[1] == '!=':
return x[0] != x[2]
elif x[1] == '<':
return x[0] < x[2]
elif x[1] == '>':
return x[0] > x[2]
# for fid in self.filter_rules:
# ff = self.filter_rules[fid]
# if ff[1] == '==':
# rows = rows.filter(ff[0] == ff[2])
# elif ff[1] == '!=':
# rows = rows.filter(ff[0] != ff[2])
# elif ff[1] == '<':
# rows = rows.filter(ff[0] < ff[2])
# elif ff[1] == '>':
# rows = rows.filter(ff[0] > ff[2])
first = 1
for fil in self.filter_rules.values():
if first:
rule = f_rule(fil)
first = 0
elif fil[3] == 'AND':
rule = rule & f_rule(fil)
elif fil[3] == 'OR':
rule = rule | f_rule(fil)
# filter_chain = [f_rule(self.filter_rules[ff]) for ff in self.filter_rules]
if not first:
rows = rows.filter(rule)
return rows
def feed_lines_to_view(self, rows):
"""
Updates the shown packages from the buffer
@param shown_diff: how many lines has been scrolled
@return: -
"""
self.pool_liststore.clear()
# new_rows = [[tm.idx, self.tmtc[tm.is_tm], tm.apid, tm.seq, tm.len_7, tm.stc, tm.sst, tm.destID, str(tm.timestamp),
# tm.data.hex()] for tm in rows]
# self.treeview.freeze_child_notify()
for tm in rows:
self.pool_liststore.append([tm.idx, self.tmtc[tm.is_tm], tm.apid, tm.seq, tm.len_7, tm.stc, tm.sst,
tm.destID, str(tm.timestamp), tm.data.hex()])
# self.treeview.thaw_child_notify()
def format_loaded_rows(self, dbrows):
'''
This function converts every packet into a readable form
@param dbrows: The rows from SQL query
@return: rows in readable form
'''
tm_rows = []
if self.decoding_type == 'PUS':
for tm in dbrows:
tm_row = [tm.idx, self.tmtc[tm.is_tm], tm.apid, tm.seq, tm.len_7, tm.stc, tm.sst, tm.destID,
str(tm.timestamp), tm.data.hex()]
tm_rows.append(tm_row)
elif self.decoding_type == 'RMAP':
for tm in dbrows:
tm_row = [tm.idx, self.cmd_repl[tm.cmd], self.w_r[tm.write], int(tm.verify), int(tm.reply),
int(tm.increment), '0x{:02X}'.format(tm.keystat), tm.taid, self._addr_fmt_hex_str(tm.addr),
tm.datalen, tm.raw.hex()]
tm_rows.append(tm_row)
elif self.decoding_type == 'FEE':
for tm in dbrows:
tm_row = [tm.idx, tm.type, tm.framecnt, tm.seqcnt, tm.raw.hex()]
tm_rows.append(tm_row)
else:
self.logger.error('Unsupported packet format!')
return tm_rows
@staticmethod
def _addr_fmt_hex_str(x):
if x is not None:
return '0x{:08X}'.format(x)
else:
return ''
def tree_selection_changed(self, selection):
mode = selection.get_mode()
model, treepaths = selection.get_selected_rows()
if len(treepaths) > 0:
path = treepaths[-1]
else:
if mode != Gtk.SelectionMode.SINGLE:
return
model, treeiter = selection.get_selected()
if treeiter is None:
return
path = model.get_path(treeiter)
self.logger.debug('SEL', time.time(), path, self.autoscroll, self.autoselect)
# I will probably be murdered in my sleep for doing this, but it works!
entry = int(str(path))
children = self.pool_liststore.iter_n_children() - 1
if entry == children:
self.autoselect = 1
else:
self.autoselect = 0
self.set_tm_data_view()
def column_clicked(self, widget):
# widget.get_sort_column_id()
self._toggle_sort_order(widget)
self._scroll_treeview(force_db_import=True)
def _toggle_sort_order(self, column):
colname = column.get_title()
newstate = self.tm_columns[self.decoding_type][colname][1] = (self.tm_columns[self.decoding_type][colname][1] + 1) % 3
column.set_sort_indicator(newstate)
column.set_sort_order(self.sort_order_dict[newstate])
def get_last_item(self, model, parent=None):
n = model.iter_n_children(parent)
return n and model.iter_nth_child(parent, n - 1)
def treeview_update(self, widget=None, event=None, data=None, row=1):
if self.autoscroll:
# adj = widget.get_vadjustment()
# if self.sort_order == Gtk.SortType.DESCENDING:
# adj.set_value(adj.get_upper() - adj.get_page_size())
if self.autoselect:
# selection = self.treeview.get_selection()
# self.selection.set_mode(Gtk.SelectionMode.SINGLE)
last = self.get_last_item(self.pool_liststore)
if last != 0:
self.selection.select_iter(last)
row = self.pool_liststore[self.pool_liststore.get_path(last)][0]
if self.selected_row != row:
self.set_tm_data_view()
self.selected_row = row
# self.selection.set_mode(Gtk.SelectionMode.MULTIPLE)
def edge_reached(self, widget, event, data=None):
if event.value_name == 'GTK_POS_BOTTOM':
self.autoscroll = 1
self.autoselect = 1
def scroll_bar(self, widget=None, event=None):
# a little crude, but we want to catch scrollbar-drag events too
self.autoscroll = 0
def scroll_child(self, widget, event, data=None):
self.logger.debug('Seems like I do not work')
return
def scroll_event(self, widget, event, data=None):
# print(event.direction.value_name, event.delta_x, event.delta_y)
self.only_scroll = True
if event.direction.value_name == 'GDK_SCROLL_SMOOTH':
scroll_lines = 3 * max(-1, event.delta_y)
if scroll_lines < 0:
self.autoscroll = 0
elif scroll_lines > 6:
self.autoscroll = 1
# needed for remote desktop
elif event.direction.value_name == 'GDK_SCROLL_UP':
scroll_lines = -3
self.autoscroll = 0
elif event.direction.value_name == 'GDK_SCROLL_DOWN':
scroll_lines = 3
else:
return
self._scroll_treeview(scroll_lines)
self.reselect_rows()
# Only_scroll is necessary to not launch a second event after the scrollbar is reset to new value
self.only_scroll = False
# print(self.offset, self.limit)
# disable autoscroll on scrollwheel up
# if event.get_scroll_deltas()[2] == -1:
# self.autoscroll = 0
def _scroll_treeview(self, scroll_lines=0, sort=None, order='asc', rows=None, force_db_import=False):
upper_limit = self.adj.get_upper() - self.adj.get_page_size()
self.offset = int(min(upper_limit, self.adj.get_value() + scroll_lines))
self.offset = max(0, self.offset)
self.limit = int(self.adj.get_page_size())
self.feed_lines_to_view(
self.fetch_lines_from_db(self.offset, self.limit, sort=sort, order=order, rows=rows))
self.adj.set_value(self.offset)
def key_pressed(self, widget=None, event=None):
def unselect_rows(clear=False):
# selection = widget.get_selection()
# self.selection.disconnect_by_func(self.tree_selection_changed)
if clear:
self.currently_selected = set()
else:
model, paths = self.selection.get_selected_rows()
self.currently_selected = {model[path][0] for path in paths}
self.reselect_rows()
# self.selection.connect('changed', self.tree_selection_changed)
try:
cursor_path = self.treeview.get_cursor()[0]
in_row = cursor_path.get_indices()[0]
# cursor_path.free()
self.cursor_path = cursor_path
except AttributeError:
in_row = 1
# cursor_path = self.cursor_path
# in_row = cursor_path.get_indices()[0]
# if in_row not in (0, self.limit - 1):
# return
if event.keyval == Gdk.KEY_Up and in_row == 0:
# cursor_path = self.treeview.get_cursor()[0]
self._scroll_treeview(-1)
self.treeview.set_cursor(cursor_path)
self.autoscroll = False
unselect_rows()
elif event.keyval == Gdk.KEY_Up:
self.autoselect = False
self.autoscroll = False
elif event.keyval == Gdk.KEY_Down and in_row == self.limit - 1:
# cursor_path = self.treeview.get_cursor()[0]
self._scroll_treeview(1)
self.treeview.set_cursor(cursor_path)
unselect_rows()
elif event.keyval == Gdk.KEY_Home:
self._scroll_treeview(-self.offset)
# self.treeview.set_cursor(cursor_path)
self.autoscroll = False
unselect_rows()
elif event.keyval == Gdk.KEY_End:
# cursor_path = self.treeview.get_cursor()[0]
# self._scroll_treeview(self.adj.get_upper() - self.offset - self.limit)
# self.treeview.set_cursor(cursor_path)
self.autoscroll = True
self.autoselect = True
self.scroll_to_bottom()
unselect_rows(clear=True)
elif event.keyval == Gdk.KEY_Page_Up:
# cursor_path = self.treeview.get_cursor()[0]
self._scroll_treeview(-self.limit)
if cursor_path is not None:
self.treeview.set_cursor(cursor_path)
self.autoscroll = False
unselect_rows()
elif event.keyval == Gdk.KEY_Page_Down:
# cursor_path = self.treeview.get_cursor()[0]
self._scroll_treeview(self.limit)
if cursor_path is not None:
self.treeview.set_cursor(cursor_path)
self.autoscroll = False
unselect_rows()
# else:
# print(event.keyval)
# Gdk.KEY_Page_Up,Gdk.KEY_Page_Down,Gdk.KEY_Home,Gdk.KEY_End
def set_currently_selected(self, widget=None, event=None):
state = event.get_state()
# print(state, state & Gdk.ModifierType.CONTROL_MASK == Gdk.ModifierType.CONTROL_MASK)
# if state & Gdk.ModifierType.CONTROL_MASK == Gdk.ModifierType.CONTROL_MASK:
# # selection = widget.get_selection()
# # model, paths = selection.get_selected_rows()
# idx = self.shift_range[1]
# if idx in self.currently_selected:
# self.currently_selected.remove(idx)
# else:
# self.currently_selected.add(idx)
# # for path in paths:
# # self.currently_selected.add(model[path][0])
# elif state & Gdk.ModifierType.SHIFT_MASK == Gdk.ModifierType.SHIFT_MASK:
# # selection = widget.get_selection()
# # model, paths = selection.get_selected_rows()
# # for path in paths:
# # self.currently_selected.add(model[path][0])
# if len(self.currently_selected) > 1:
# for idx in range(min(self.shift_range), max(self.shift_range) + 1):
# self.currently_selected.add(idx)
# else:
# self.currently_selected = set(range(min(self.shift_range), max(self.shift_range) + 1))
# else:
# # selection = widget.get_selection()
# model, paths = self.selection.get_selected_rows()
# self.currently_selected = {model[path][0] for path in paths}
model, paths = self.selection.get_selected_rows()
self.currently_selected = {model[path][0] for path in paths}
self.reselect_rows()
def select_all_rows(self, widget=None, *args):
nrows = self.count_current_pool_rows()
self.currently_selected = set(range(1, nrows + 1))
self.reselect_rows()
def reselect_rows(self, widget=None, event=None):
model = self.pool_liststore # self.treeview.get_model()
# self.treeview.get_selection().unselect_all()
for row in model:
if row[0] in self.currently_selected:
try:
self.selection.select_path(model.get_path(model.get_iter(row[0] - self.offset - 1)))
except ValueError:
self.logger.info('valerr')
except TypeError:
self.logger.info('typerr')
def unselect_bottom(self, widget=None):
if widget.count_selected_rows() > 1:
bottom_path = widget.get_selected_rows()[-1][-1]
widget.unselect_path(bottom_path)
def create_liststore(self):
if self.decoding_type == 'PUS':
return Gtk.ListStore('guint', str, 'guint', 'guint', 'guint', 'guint', 'guint', 'guint', str, str)
elif self.decoding_type == 'RMAP':
return Gtk.ListStore('guint', str, str, 'guint', 'guint', 'guint', str, 'guint', str, 'guint', str)
elif self.decoding_type == 'FEE':
return Gtk.ListStore('guint', 'guint', 'guint', 'guint', str)
else:
self.logger.error('Decoding Type is an unknown value')
def set_keybinds(self):
accel = Gtk.AccelGroup()
accel.connect(Gdk.keyval_from_name('w'), Gdk.ModifierType.CONTROL_MASK,
0, self.quit_func)
accel.connect(Gdk.keyval_from_name('q'), Gdk.ModifierType.CONTROL_MASK,
0, self.quit_func)
# accel.connect(Gdk.keyval_from_name('a'), Gdk.ModifierType.CONTROL_MASK,
# 0, self.select_all_rows)
self.add_accel_group(accel)
def create_pool_managebar(self):
self.pool_managebar = Gtk.HBox()
self.pool_selector = Gtk.ComboBoxText(tooltip_text='Pool path')
pool_names = Gtk.ListStore(str, str, str, str)
# if self.pool != None:
# [self.pool_names.append([name]) for name in self.pool.datapool.keys()]
self.pool_selector.set_model(pool_names)
cell = self.pool_selector.get_cells()[0]
cell.set_property('ellipsize', Pango.EllipsizeMode.MIDDLE)
type_cell = Gtk.CellRendererText(foreground='gray', style=Pango.Style.ITALIC)
self.pool_selector.pack_start(type_cell, 0)
self.pool_selector.add_attribute(type_cell, 'text', 2)
state_cell = Gtk.CellRendererText(foreground='red')
self.pool_selector.pack_start(state_cell, 0)
self.pool_selector.add_attribute(state_cell, 'text', 1)
self.pool_selector.connect('changed', self.select_pool)
icon_path = os.path.join(self.cfg.get('paths', 'ccs'), 'pixmap/func.png')
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_path, 32, 32)
plot_butt = Gtk.Button(image=Gtk.Image.new_from_pixbuf(pixbuf), tooltip_text='Parameter Plotter')
# plot_butt.connect('button-press-event', self.show_context_menu, self.context_menu())
plot_butt.connect('clicked', self.plot_parameters)
icon_path = os.path.join(self.cfg.get('paths', 'ccs'), 'pixmap/monitor.png')
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_path, 32, 32)
self.mon_butt = Gtk.Button(image=Gtk.Image.new_from_pixbuf(pixbuf), tooltip_text='Parameter Monitor')
self.mon_butt.connect('clicked', self.monitor_parameters)
self.mon_butt.connect('button-press-event', self.show_context_menu, self.context_menu())
dump_butt = Gtk.Button.new_from_icon_name('gtk-save', Gtk.IconSize.LARGE_TOOLBAR)
dump_butt.set_tooltip_text('Save pool')
dump_butt.connect('clicked', self.save_pool)
load_butt = Gtk.Button.new_from_icon_name('gtk-open', Gtk.IconSize.LARGE_TOOLBAR)
load_butt.set_tooltip_text('Load pool')
load_butt.connect('clicked', self.load_pool)
extract_butt = Gtk.Button.new_from_icon_name('gtk-paste', Gtk.IconSize.LARGE_TOOLBAR)
extract_butt.set_tooltip_text('Extract packets')
extract_butt.connect('clicked', self.collect_packet_data)
# live buttons
self.rec_butt = Gtk.Button(image=Gtk.Image.new_from_icon_name('gtk-media-record', Gtk.IconSize.LARGE_TOOLBAR),
tooltip_text='Manage recording to LIVE pool')
self.rec_butt.connect('clicked', self.start_recording)
self.stop_butt = Gtk.Button(image=Gtk.Image.new_from_icon_name('gtk-media-stop', Gtk.IconSize.LARGE_TOOLBAR),
tooltip_text='Stop recording to currently selected LIVE pool')
self.stop_butt.set_sensitive(False)
self.stop_butt.connect('clicked', self.stop_recording)
clear_butt = Gtk.Button.new_from_icon_name('edit-clear', Gtk.IconSize.LARGE_TOOLBAR)
clear_butt.set_tooltip_text('Clear current pool')
clear_butt.connect('clicked', self.clear_pool)
self.univie_box = self.create_univie_box()
bigd = Gtk.Button.new_from_icon_name('gtk-justify-fill', Gtk.IconSize.LARGE_TOOLBAR)
bigd.set_tooltip_text('Open Large Data Viewer')
bigd.connect('clicked', self.show_bigdata)
self.pool_managebar.pack_start(self.pool_selector, 1, 1, 0)
self.pool_managebar.pack_start(plot_butt, 0, 0, 0)
self.pool_managebar.pack_start(self.mon_butt, 0, 0, 0)
self.pool_managebar.pack_end(self.univie_box, 0, 0, 0)
self.pool_managebar.pack_end(clear_butt, 0, 0, 0)
self.pool_managebar.pack_end(bigd, 0, 0, 0)
self.pool_managebar.pack_end(self.stop_butt, 0, 0, 0)
self.pool_managebar.pack_end(self.rec_butt, 0, 0, 0)
self.pool_managebar.pack_end(extract_butt, 0, 0, 0)
self.pool_managebar.pack_end(dump_butt, 0, 0, 0)
self.pool_managebar.pack_end(load_butt, 0, 0, 0)
def create_filterbar(self):
filterbar = Gtk.HBox()
box = Gtk.HBox()
column_model = Gtk.ListStore(str)
for name in self.tm_columns[self.decoding_type]:
column_model.append([name])
column_name = Gtk.ComboBoxText()
column_name.set_model(column_model)
column_name.set_tooltip_text('Select column')
operator_model = Gtk.ListStore(str)
for op in ['==', '!=', '<', '>']:
operator_model.append([op])
operator = Gtk.ComboBoxText()
operator.set_model(operator_model)
operator.set_active(0)
operator.set_tooltip_text('Select relational operator')
filter_value = Gtk.Entry()
filter_value.set_placeholder_text('Filter value')
filter_value.set_tooltip_text('Filter value')
filter_value.set_width_chars(14)
filter_value.connect('activate', self._add_to_rules, filter_value, column_name, operator, 'AND')
path_ccs = self.cfg.get('paths', 'ccs')
add_to_rule_button_and = Gtk.Button()
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(os.path.join(path_ccs, 'pixmap/intersection_icon.svg'), 10, 10)
add_to_rule_button_and.set_image(Gtk.Image.new_from_pixbuf(pixbuf))
add_to_rule_button_and.set_tooltip_text('AND')
add_to_rule_button_and.connect('clicked', self._add_to_rules, filter_value, column_name, operator, 'AND')
add_to_rule_button_or = Gtk.Button()
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(os.path.join(path_ccs, 'pixmap/union_icon.svg'), 10, 10)
add_to_rule_button_or.set_image(Gtk.Image.new_from_pixbuf(pixbuf))
add_to_rule_button_or.set_tooltip_text('OR')
add_to_rule_button_or.connect('clicked', self._add_to_rules, filter_value, column_name, operator, 'OR')
box.pack_start(column_name, 1, 1, 0)
box.pack_start(operator, 1, 1, 0)
box.pack_start(filter_value, 1, 1, 0)
box.pack_start(add_to_rule_button_and, 1, 1, 0)
box.pack_start(add_to_rule_button_or, 1, 1, 0)
filterbar.pack_start(box, 0, 0, 0)
# for col in self.column_labels:
# if col[0] == 'Data':
# continue
# filter = Gtk.Entry()
# filter.set_placeholder_text(col[0])
# filter.set_tooltip_text(col[0])
# filter.set_width_chars(5)
# filter.connect('activate', self._update_filters)
# filterbar.pack_start(filter, 0, 0, 0)
# filterbar.pack_start(Gtk.Separator.new(Gtk.Orientation.VERTICAL), 1, 0, 0)
goto_timestamp = Gtk.Entry()
goto_timestamp.set_placeholder_text('GOTO timestamp')
goto_timestamp.set_tooltip_text('GOTO timestamp')
goto_timestamp.set_width_chars(14)
goto_timestamp.connect('activate', self._goto_timestamp)
filterbar.pack_end(goto_timestamp, 0, 0, 0)
goto_idx = Gtk.Entry()
goto_idx.set_placeholder_text('GOTO idx')
goto_idx.set_tooltip_text('GOTO idx')
goto_idx.set_width_chars(10)
goto_idx.connect('activate', self._goto_idx)
filterbar.pack_end(goto_idx, 0, 0, 0)
return filterbar
def _add_to_rules(self, widget=None, filter_value_box=None, column_name=None, operator=None, and_or=None):
if and_or == 'AND':
aosym = '\u2229'
elif and_or == 'OR':
aosym = '\u222A'
column = column_name.get_active_text()
value = filter_value_box.get_text()
operator = operator.get_active_text()
if not column or not value or not operator:
return
# if self.rule_box is None:
# self._add_rulebox()
rule = Gtk.HBox()
if len(self.rule_box) == 1:
name = Gtk.Label('{}{}{}'.format(column, operator, value))
else:
name = Gtk.Label('{} {}{}{}'.format(aosym, column, operator, value))
rule.pack_start(name, 1, 1, 0)
close_butt = Gtk.Button()
close_butt.set_image(Gtk.Image.new_from_icon_name(Gtk.STOCK_CLOSE, Gtk.IconSize.MENU))
close_butt.set_relief(Gtk.ReliefStyle.NONE)
close_butt.connect('clicked', self._remove_rule)
rule.pack_end(close_butt, 0, 0, 0)
self.filter_rules[hash(rule)] = (self.tm_columns[self.decoding_type][column][0], operator, value, and_or)
self.rule_box.pack_start(rule, 0, 0, 0)
self.show_all()
self._scroll_treeview(force_db_import=True)
def _add_rulebox(self):
# if self.rule_box is not None:
# self.rule_box.remove()
# self.rule_box = None
scrolled_window = Gtk.ScrolledWindow()
scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER)
rule_box = Gtk.HBox()
rule_box.set_spacing(3)
rule_box.pack_start(Gtk.Label(label='Filters: '), 0, 0, 0)
scrolled_window.add(rule_box)
#scrolled_window.add(resize_view_check_button)
# remove_rule_button = Gtk.Button()
# remove_rule_button.set_image(Gtk.Image.new_from_icon_name('list-remove', Gtk.IconSize.BUTTON))
# remove_rule_button.connect('clicked', self._remove_rule)
# rule_box.pack_end(remove_rule_button, 1, 1, 0)
rule_active_button = Gtk.Switch()
# rule_active_button.set_image(Gtk.Image.new_from_icon_name('list-remove', Gtk.IconSize.BUTTON))
rule_active_button.set_tooltip_text('Toggle filter rules')
rule_active_button.connect('state-set', self._toggle_rule)
self.filter_rules_active = rule_active_button.get_active()
self.filter_spinner = Gtk.Spinner()
self.rule_box = rule_box
box = Gtk.HBox()
box.set_spacing(3)
box.pack_start(scrolled_window, 1, 1, 5)
box.pack_start(self.filter_spinner, 1, 1, 1)
box.pack_end(rule_active_button, 0, 0, 0)
self.grid.attach(box, 0, 2, 1, 1)
#self.filter_spinner.start()
# self.show_all()
def _resize_scrollbar_toggled(self, widget):
#self._on_scrollbar_changed()
pass
# def resize_scrollbar(self):
#
# if self.first_run or self.resize_thread.is_alive():
# self.first_run = False
# self.resize_thread = threading.Thread(target=self.small_refresh_function)
# return
#
# self.resize_thread = threading.Thread(target=self.scrollbar_size_worker)
# self.resize_thread.setDaemon(True)
# self.resize_thread.start()
#
# def scrollbar_size_worker(self):
# #print(1)
# new_session = self.session_factory_storage
# rows = new_session.query(
# Telemetry[self.decoding_type]
# ).join(
# DbTelemetryPool,
# Telemetry[self.decoding_type].pool_id == DbTelemetryPool.iid
# ).filter(
# DbTelemetryPool.pool_name == self.active_pool_info.filename
# )
# #new_session.close()
# cnt = rows.count()
# #print(cnt)
# rows = self._filter_rows(rows)
# cnt = rows.count()
# #count_q = que.statement.with_only_columns([func.count()]).order_by(None)
# #cnt = new_session.execute(count_q).scalar()
# #cnt = new_session.query(que).count()
# #print(2)
# #print(cnt)
# GLib.idle_add(self.adj.set_upper, cnt,)
# #print(3)
# new_session.close()
def _toggle_rule(self, widget=None, data=None):
self.filter_rules_active = widget.get_active()
self._scroll_treeview(force_db_import=True)
def _remove_rule(self, widget=None):
rule = widget.get_parent()
self.filter_rules.pop(hash(rule))
rule.get_parent().remove(rule)
self.show_all()
self._scroll_treeview()
def _update_filters(self, widget):
value = widget.get_text()
value = None if value == '' else value
self.tm_columns[self.decoding_type][widget.get_placeholder_text()][2] = value
self._scroll_treeview()
def _goto_idx(self, widget):
try:
widget.set_sensitive(False)
goto = int(widget.get_text()) - 1
except ValueError:
return
finally:
widget.set_sensitive(True)
upper_limit = self.adj.get_upper() - self.adj.get_page_size()
self.offset = abs(int(min(upper_limit, goto)))
self.limit = int(self.adj.get_page_size())
self.feed_lines_to_view(self.fetch_lines_from_db(self.offset, self.limit, sort=None, order='asc', force_import=True))
self.autoscroll = False
self.adj.set_value(self.offset)
def _goto_timestamp(self, widget):
if self.decoding_type != 'PUS':
self.logger.info('No Timestamp in RMAP and FEE data')
return
try:
goto = widget.get_text()
except ValueError:
return
upper_limit = self.adj.get_upper() - self.adj.get_page_size()
rows = self.get_current_pool_rows()
try:
widget.set_sensitive(False)
idx = rows.filter(Telemetry[self.decoding_type].timestamp.like(goto + "%"))[0].idx - 1
except IndexError:
return
finally:
widget.set_sensitive(True)
self.offset = abs(int(min(upper_limit, idx)))
self.limit = int(self.adj.get_page_size())
self.feed_lines_to_view(self.fetch_lines_from_db(self.offset, self.limit, sort=None, order='asc', force_import=True))
self.autoscroll = False
self.adj.set_value(self.offset)
def context_menu(self):
menu = Gtk.Menu()
par_sets = cfg['ccs-monitor_parameter_sets']
for parset in par_sets:
item = Gtk.MenuItem(label=str(parset))
item.connect('activate', self.mon_menu, parset)
menu.append(item)
return menu
def show_context_menu(self, widget, event, menu):
if event.button != 3:
return
menu.show_all()
menu.popup_at_pointer()
def mon_menu(self, widget=None, parset=None):
if parset is not None:
self._selected_mon_par_set = parset
self.mon_butt.set_tooltip_text('Parameter Monitor [{}]'.format(parset))
def check_structure_type(self):
# If pool is changed but not created:
model = self.pool_selector.get_model()
if self.pool_selector.get_active_iter():
current_selected_type = model.get_value(self.pool_selector.get_active_iter(), 2) # Get the shown decoding type
current_selected_pool = self.pool_selector.get_active_text() # get the shown pool
else:
current_selected_type = False
current_selected_pool = False
if self.active_pool_info.pool_name == current_selected_pool:
self.decoding_type = current_selected_type
return
'''
count = 0
while count < len(model):
value = model.get_value(model.get_iter(count), 0) # Get the value
if value.split(' - ')[0] == self.active_pool_info.pool_name:
self.pool_selector.set_active_iter(model.get_iter(count))
changed = True
break
count += 1
# if no other poo'''
# If pool is created
# Check in the DB which datatype should be use
new_session = self.session_factory_storage
pool = new_session.query(
DbTelemetryPool.protocol
).filter(
DbTelemetryPool.pool_name == self.active_pool_info.filename
)
pool = pool.all()
# new_session.commit()
# time.sleep(0.5) # with no wait query might return empty. WHY?
# Still sometimes empty, if not pool abfrage should stopp this behaviour
if not pool:
self.decoding_type = 'PUS'
elif pool[0][0] in ['PUS', 'PLMSIM']: # If PUS decode PUS
self.decoding_type = 'PUS'
else: # If a new pool is created always show RMAP
self.decoding_type = 'RMAP'
new_session.close()
return
# def get_pool_names(self, widget):
# if self.pool is None:
# return
#
# self.pool_names = Gtk.ListStore(str)
# [self.pool_names.append([name]) for name in self.pool.datapool.keys()]
# self.pool_selector.set_model(self.pool_names)
# This function fills the Active pool info variable with data form pus_datapool
def Active_Pool_Info_append(self, pool_info=None):
if pool_info is not None:
self.active_pool_info = ActivePoolInfo(str(pool_info[0]), int(pool_info[1]),
str(pool_info[2]), bool(pool_info[3]))
self.check_structure_type()
self._set_pool_selector_tooltip()
return self.active_pool_info
def _set_pool_selector_tooltip(self):
self.pool_selector.set_tooltip_text(self.active_pool_info.filename)
def _check_pool_is_loaded(self, filename):
model = self.pool_selector.get_model()
for pool in model:
if pool[3] == filename:
self.logger.info('Pool {} already loaded'.format(filename))
return pool
return False
def update_columns(self):
columns = self.treeview.get_columns()
for column in columns:
self.treeview.remove_column(column)
self.create_treeview_columns()
self.pool_liststore = self.create_liststore()
self.treeview.set_model(self.pool_liststore)
# self.scrolled_treelist.add(self.treeview)
self.show_all()
def select_pool(self, selector, new_pool=None):
active_info = selector.get_model()[selector.get_active_iter()]
if new_pool is None:
pool_name = active_info[3]
else:
pool_name = new_pool
new_session = self.session_factory_storage()
try:
live = False
dbpool = new_session.query(DbTelemetryPool).filter(DbTelemetryPool.pool_name == pool_name).first()
if active_info[0] == active_info[3]: # check if pool is loaded from a file
live = True
if dbpool is not None:
self.Active_Pool_Info_append(pool_info=[pool_name, dbpool.modification_time, os.path.basename(pool_name), live])
else:
self.Active_Pool_Info_append(pool_info=None)
except Exception as err:
self.logger.warning(err)
return
finally:
new_session.close()
self.update_columns()
# self._set_pool_list_and_display()
if not self.active_pool_info.live:
self.stop_butt.set_sensitive(False)
else:
self.stop_butt.set_sensitive(True)
self.refresh_treeview(pool_name)
self.adj.set_upper(self.count_current_pool_rows())
self.adj.set_value(0)
self._on_scrollbar_changed(adj=self.adj)
# queue = self.queues[pool_name]
#
# if queue is not None:
# self.pool_liststore.clear()
# self.pckt_queue = queue
# self.pool_name = pool_name
# if self.pool is not None:
# self.model_unset = True
# self.pool.reset_queue_seq(pool_name, queue)
# # self.change_cursor(self.scrolled_treelist.get_window(),'progress')
def clear_pool(self, widget):
poolmgr = cfl.dbus_connection('poolmanager', cfl.communication['poolmanager'])
# don't clear static pools
if self.active_pool_info.filename.count('/'):
return
widget.set_sensitive(False)
pool_name = self.get_active_pool_name()
if pool_name is None:
return
poolmgr.Functions('_clear_pool', pool_name)
# self.active_pool_info = poolmgr.Dictionaries('loaded_pools', pool_name)
self.Active_Pool_Info_append(poolmgr.Dictionaries('loaded_pools', pool_name))
self.offset = 0
self.autoscroll = True
self.autoselect = True
self.adj.set_upper(self.count_current_pool_rows())
self._on_scrollbar_changed()
widget.set_sensitive(True)
def create_univie_box(self):
"""
Creates the Univie Button which can be found in every application, Used to Start all parts of the CCS and
manage communication
:return:
"""
univie_box = Gtk.HBox()
univie_button = Gtk.ToolButton()
# button_run_nextline.set_icon_name("media-playback-start-symbolic")
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
self.cfg.get('paths', 'ccs') + '/pixmap/Icon_Space_blau_en.png', 48, 48)
icon = Gtk.Image.new_from_pixbuf(pixbuf)
univie_button.set_icon_widget(icon)
univie_button.set_tooltip_text('Applications and About')
univie_button.connect("clicked", self.on_univie_button)
univie_box.add(univie_button)
# Popover creates the popup menu over the button and lets one use multiple buttons for the same one
self.popover = Gtk.Popover()
# Add the different Starting Options
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5, margin=4)
for name in self.cfg['ccs-dbus_names']:
if name in ['plotter', 'monitor']:
continue
start_button = Gtk.Button.new_with_label("Start " + name.capitalize())
start_button.connect("clicked", cfl.on_open_univie_clicked)
vbox.pack_start(start_button, False, True, 0)
# Add the TST option
conn_button = Gtk.Button.new_with_label('Test Specification Tool')
conn_button.connect("clicked", cfl.start_tst)
vbox.pack_start(conn_button, True, True, 0)
# Add the manage connections option
conn_button = Gtk.Button.new_with_label('Communication')
conn_button.connect("clicked", self.on_communication_dialog)
vbox.pack_start(conn_button, True, True, 0)
# Add the configuration manager option
conn_button = Gtk.Button.new_with_label('Preferences')
conn_button.connect("clicked", cfl.start_config_editor)
vbox.pack_start(conn_button, True, True, 0)
# Add the option to see the Credits
about_button = Gtk.Button.new_with_label('About')
about_button.connect("clicked", self._on_select_about_dialog)
vbox.pack_start(about_button, False, True, 10)
self.popover.add(vbox)
self.popover.set_position(Gtk.PositionType.BOTTOM)
self.popover.set_relative_to(univie_button)
return univie_box
def on_univie_button(self, action):
"""
Adds the Popover menu to the UNIVIE Button
:param action: Simply the button
:return:
"""
self.popover.show_all()
self.popover.popup()
def on_communication_dialog(self, button):
cfl.change_communication_func(main_instance=self.main_instance, parentwin=self)
def _on_select_about_dialog(self, action):
cfl.about_dialog(self)
return
def get_active_pool_name(self):
return self.pool_selector.get_active_text()
def update_pool_view(self, pool_name, pmgr_load_pool=None, instance=1):
"""
Used to change the view to given pool, or create a new entry if it does not exist
Mostly used by Poolmanger GUI 'Display' Button
:param pool_name: Name of the pool to change to or to create
:return:
"""
# If Active (Shown) Pool is the one dont do anything
if pool_name == self.get_active_pool_name():
return
changed = False
#self.select_pool(False, new_pool=pool_name)
model = self.pool_selector.get_model()
# It will check all entries in the Pool selector and change to the one if possible
count = 0
while count < len(model):
value = model.get_value(model.get_iter(count), 0) # Get the value
if value == pool_name: # If the wanted connection is found change to it
self.pool_selector.set_active_iter(model.get_iter(count))
changed = True
break
count += 1
# if no other pool could be found create a new one
if not changed:
if not pmgr_load_pool:
self.set_pool(pool_name, instance=instance)
else:
self.set_pool(pool_name, pmgr_load_pool, instance=instance)
# Instance has to be used only here, explanation is found in pus_datapool where this function is called
if instance:
poolmgr = cfl.dbus_connection('poolmanager', instance)
cfl.Functions(poolmgr, 'loaded_pools_func', self.active_pool_info.pool_name, self.active_pool_info)
return
def create_tm_data_viewer(self):
box = Gtk.VBox()
self.decoder_box = Gtk.VBox()
decoder_bar = self.create_decoder_bar()
self.decoder_box.pack_start(decoder_bar, 1, 1, 1)
box.pack_start(self.decoder_box, 0, 0, 0)
self.rawswitch = Gtk.CheckButton.new_with_label('Decode Source Data')
self.rawswitch.connect('toggled', self.set_tm_data_view, None, True)
self.rawswitch.connect('toggled', self._update_user_tm_decoders)
switchbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
switchbox.pack_start(self.rawswitch, 0, 0, 0)
self.calibrated_switch = Gtk.CheckButton.new_with_label('Calibrated')
self.calibrated_switch.set_sensitive(False)
self.calibrated_switch.connect('toggled', self.set_tm_data_view, None, True)
self.rawswitch.connect('toggled', self._enable_calibrated_switch)
switchbox.pack_start(self.calibrated_switch, 0, 0, 0)
ctrlbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
ctrlbox.pack_start(switchbox, 0, 0, 0)
self.hexview = Gtk.Label()
self.hexview.set_selectable(True)
ctrlbox.pack_end(self.hexview, 1, 1, 0)
box.pack_start(ctrlbox, 0, 0, 3)
scrolled_header_view = Gtk.ScrolledWindow()
scrolled_header_view.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER)
# self.tm_data_viewer.set_hexpand(True)
self.tm_header_view = Gtk.TextView(editable=False, cursor_visible=False)
scrolled_header_view.add(self.tm_header_view)
box.pack_start(scrolled_header_view, 0, 0, 0)
scrolled_tm_view = Gtk.ScrolledWindow()
self.tm_data_view = self.create_tm_data_viewer_list(decode=False, create=True)
data_selection = self.tm_data_view.get_selection()
data_selection.connect('changed', self.set_hexview)
scrolled_tm_view.add(self.tm_data_view)
box.pack_start(scrolled_tm_view, 1, 1, 0)
return box
def _enable_calibrated_switch(self, *args):
if self.rawswitch.get_active():
self.calibrated_switch.set_sensitive(True)
self.calibrated_switch.set_active(True)
else:
self.calibrated_switch.set_sensitive(False)
self.calibrated_switch.set_active(False)
def create_tm_data_viewer_list(self, decode=False, create=False):
tm_data_model = Gtk.ListStore(str, str, str, str)
if create:
listview = Gtk.TreeView()
listview.set_model(tm_data_model)
# Set up Drag and Drop
listview.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [], Gdk.DragAction.COPY)
listview.drag_source_set_target_list(None)
listview.drag_source_add_text_targets()
listview.connect("drag-data-get", self.on_drag_tmdata_get)
else:
listview = self.tm_data_view
for c in listview.get_columns():
listview.remove_column(c)
listview.set_model(tm_data_model)
if decode:
for i, column_title in enumerate(['Parameter', 'Value', 'Unit']):
render = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(column_title, render, text=i)
# column.set_cell_data_func(render, self.text_colour2)
column.set_sort_column_id(i)
column.set_clickable(True)
column.set_resizable(True)
# column.connect('clicked', self.column_clicked)
listview.append_column(column)
render = Gtk.CellRendererText()
column = Gtk.TreeViewColumn('tooltip', render, text=3)
column.set_visible(False)
listview.append_column(column)
listview.set_tooltip_column(3)
else:
for i, column_title in enumerate(['BYTEPOS', 'HEX', 'DEC', 'ASCII']):
render = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(column_title, render, text=i)
# column.set_cell_data_func(render, self.text_colour2)
column.set_resizable(True)
listview.append_column(column)
if create:
return listview
def on_drag_tmdata_get(self, treeview, drag_context, selection_data, info, time, *args):
treeselection = treeview.get_selection()
model, my_iter = treeselection.get_selected()
selection_data.set_text('{} = {}'.format(*model[my_iter][:2]), -1)
def create_decoder_bar(self):
box = Gtk.VBox()
box1 = Gtk.HBox()
package_button = Gtk.Button(label='Create TM Structure')
package_button.set_image(Gtk.Image.new_from_icon_name('list-add', Gtk.IconSize.MENU))
package_button.set_tooltip_text('Create user defined TM packet structure')
package_button.set_always_show_image(True)
parameter_button = Gtk.Button(label='Create Parameter')
parameter_button.set_image(Gtk.Image.new_from_icon_name('list-add', Gtk.IconSize.MENU))
parameter_button.set_tooltip_text('Create user defined parameter')
parameter_button.set_always_show_image(True)
decode_udef_check = Gtk.CheckButton.new_with_label('UDEF')
decode_udef_check.set_tooltip_text('Force decoding with custom packet structures')
package_button.connect('clicked', self.add_new_user_package)
parameter_button.connect('clicked', self.add_decode_parameter)
decode_udef_check.connect('toggled', self.set_decoding_order)
box1.pack_start(package_button, 0, 0, 0)
box1.pack_start(parameter_button, 0, 0, 0)
box1.pack_start(decode_udef_check, 0, 0, 2)
box2 = Gtk.HBox()
decoder_name = Gtk.ComboBoxText.new_with_entry()
decoder_name.set_tooltip_text('Label')
decoder_name_entry = decoder_name.get_child()
decoder_name_entry.set_placeholder_text('Label')
decoder_name_entry.set_width_chars(10)
decoder_name.set_model(self.create_decoder_model())
bytepos = Gtk.Entry()
bytepos.set_placeholder_text('Offset') # +{}'.format(TM_HEADER_LEN))
bytepos.set_tooltip_text('Offset') # +{}'.format(TM_HEADER_LEN))
bytepos.set_width_chars(7)
bytelength = Gtk.Entry()
bytelength.set_placeholder_text('Length')
bytelength.set_tooltip_text('Length')
bytelength.set_width_chars(7)
add_button = Gtk.Button(label=' Decoder')
add_button.set_image(Gtk.Image.new_from_icon_name('list-add', Gtk.IconSize.MENU))
add_button.set_tooltip_text('Add byte decoder')
add_button.set_always_show_image(True)
decoder_name.connect('changed', self.fill_decoder_mask, bytepos, bytelength)
add_button.connect('clicked', self.add_decoder, decoder_name, bytepos, bytelength)
box2.pack_start(decoder_name, 0, 1, 0)
box2.pack_start(bytepos, 0, 0, 0)
box2.pack_start(bytelength, 0, 0, 0)
box2.pack_start(add_button, 0, 0, 0)
box.pack_start(box1, 0, 0, 0)
box.pack_start(box2, 0, 0, 0)
self.decoder_dict = {}
self.UDEF = False
return box
def add_decode_parameter(self, widget):
cfl.add_decode_parameter(parentwin=self)
def add_new_user_package(self, widget):
cfl.add_tm_decoder(parentwin=self)
def set_decoding_order(self, widget):
if widget.get_active():
self.UDEF = True
else:
self.UDEF = False
self.set_tm_data_view()
def create_decoder_model(self):
model = Gtk.ListStore(str)
# if self.cfg.has_section('user_decoders'):
# for decoder in self.cfg['user_decoders'].keys():
# model.append([decoder])
for decoder in self.cfg['ccs-user_decoders'].keys():
len = self.cfg['ccs-user_decoders'][decoder]
if 'bytelen' in len:
model.append([decoder])
return model
def fill_decoder_mask(self, widget, bytepos=None, bytelen=None):
decoder = widget.get_active_text()
# if not self.cfg.has_option('user_decoders',decoder):
# return
if self.cfg.has_option('ccs-user_decoders', decoder):
data = json.loads(self.cfg['ccs-user_decoders'][decoder])
bytepos.set_text(str(data['bytepos']))
bytelen.set_text(str(data['bytelen']))
def add_decoder(self, widget, decoder_name, byteoffset, bytelength):
try:
label, bytepos, bytelen = decoder_name.get_active_text(), int(byteoffset.get_text()), int(bytelength.get_text())
except Exception as err:
self.logger.info(err)
return
if label in (None, ''):
return
self.decoder_dict[label] = {'bytepos': bytepos, 'bytelen': bytelen}
try:
self.cfg.save_option_to_file('ccs-user_decoders', label, json.dumps(self.decoder_dict[label]))
except AttributeError:
self.logger.info('Could not save decoder to cfg')
decoder_name.set_model(self.create_decoder_model())
box = Gtk.HBox()
name = Gtk.Label(label=decoder_name.get_active_text())
name.set_tooltip_text('bytes {}-{}'.format(bytepos, bytepos + bytelen - 1))
hexa = Gtk.Label()
uint = Gtk.Label()
bina = Gtk.Label()
box.pack_start(name, 1, 1, 0)
close_butt = Gtk.Button()
close_butt.set_image(Gtk.Image.new_from_icon_name(Gtk.STOCK_CLOSE, Gtk.IconSize.MENU))
close_butt.set_relief(Gtk.ReliefStyle.NONE)
box.pack_end(close_butt, 0, 0, 0)
box.pack_end(bina, 1, 0, 1)
box.pack_end(uint, 1, 0, 1)
box.pack_end(hexa, 1, 0, 1)
decoder_box = widget.get_parent().get_parent()
decoder_box.pack_start(box, 0, 0, 1)
close_butt.connect('clicked', self.remove_decoder, box, decoder_box)
self.show_all()
return box
def remove_decoder(self, widget, decoder, decoder_box):
decoder_box.remove(decoder)
self.show_all()
def set_decoder_view(self, tm):
decoders = self.decoder_box.get_children()[0].get_children()[2:]
for decoder in decoders:
try:
name, hexa, uint, bina, _ = decoder.get_children()
bytepos = self.decoder_dict[name.get_label()]['bytepos']
bytelen = self.decoder_dict[name.get_label()]['bytelen']
byts = tm[bytepos:bytepos + bytelen]
hexa.set_label(byts.hex().upper())
uint.set_label(str(int.from_bytes(byts, 'big')))
bina.set_label(bin(int.from_bytes(byts, 'big'))[2:])
except Exception as err:
self.logger.info(err)
hexa.set_label('###')
uint.set_label('###')
bina.set_label('###')
def set_hexview(self, widget=None, data=None):
if self.rawswitch.get_active():
model, treepath = widget.get_selected_rows()
if treepath:
value = model[treepath[0]][3]
self.hexview.set_text(value)
def _update_user_tm_decoders(self, widget):
if widget.get_active():
importlib.reload(cfl)
@delayed(10)
def set_tm_data_view(self, selection=None, event=None, change_columns=False, floatfmt='.7G'):
if not self.active_pool_info:
self.logger.warning('No active pool')
return
if self.decoding_type != 'PUS' and self.rawswitch.get_active():
self.logger.info('Cannot decode parameters for RMAP or FEE data packets')
buf = Gtk.TextBuffer(text='Parameter view not available for non-PUS packets')
self.tm_header_view.set_buffer(buf)
self.tm_data_view.get_model().clear()
return
if change_columns:
self.tm_data_view.freeze_child_notify()
self.create_tm_data_viewer_list(decode=self.rawswitch.get_active(), create=False)
self.tm_data_view.thaw_child_notify()
# print(time.time(), self.autoselect)
# self.adj.set_upper(self.count_current_pool_rows())
# textview = self.tm_data_viewer.get_child()
datamodel = self.tm_data_view.get_model()
if not isinstance(selection, Gtk.TreeSelection):
toggle = True
selection = self.treeview.get_selection()
else:
toggle = False
# nrows = selection.count_selected_rows()
nrows = len(self.currently_selected)
if nrows > 1:
self.tm_header_view.set_buffer(Gtk.TextBuffer(text='{} packets selected'.format(nrows)))
datamodel.clear()
return
if selection is None:
return
model, treepath = selection.get_selected_rows()
if len(treepath) == 0:
return
else:
rowidx = model[treepath][0]
if rowidx == self.last_decoded_row and not toggle:
return
else:
self.last_decoded_row = rowidx
tm_index = model[treepath[0]][0]
new_session = self.session_factory_storage
raw = new_session.query(
Telemetry[self.decoding_type].raw
).join(
DbTelemetryPool,
DbTelemetryPool.iid == Telemetry[self.decoding_type].pool_id
).filter(
DbTelemetryPool.pool_name == self.active_pool_info.filename,
Telemetry[self.decoding_type].idx == tm_index
).first()
if not raw:
new_session.close()
return
tm = raw[0]
new_session.close()
self.set_decoder_view(tm)
if self.rawswitch.get_active():
self.tm_header_view.set_monospace(False)
datamodel.clear()
nocalibration = not self.calibrated_switch.get_active()
try:
if self.UDEF:
data = cfl.Tmformatted(tm, textmode=False, udef=True, nocal=nocalibration, floatfmt=floatfmt)
buf = Gtk.TextBuffer(text=cfl.Tm_header_formatted(tm) + '\n{}\n'.format(data[1]))
self._feed_tm_data_view_model(datamodel, data[0])
else:
data = cfl.Tmformatted(tm, textmode=False, nocal=nocalibration, floatfmt=floatfmt)
buf = Gtk.TextBuffer(text=cfl.Tm_header_formatted(tm) + '\n{}\n'.format(data[1]))
self._feed_tm_data_view_model(datamodel, data[0])
except Exception as error:
buf = Gtk.TextBuffer(text='Error in decoding packet data:\n{}\n'.format(error))
else:
self.tm_header_view.set_monospace(False)
if self.decoding_type != 'PUS':
tmio = io.BytesIO(tm)
headers, _, _ = cfl.extract_spw(tmio)
header = headers[0].raw
headlen = len(header)
head = cfl.spw_header_formatted(headers[0])
else:
head = cfl.Tm_header_formatted(tm, detailed=True)
headlen = TC_HEADER_LEN if (tm[0] >> 4 & 1) else TM_HEADER_LEN
tmsource = tm[headlen:]
byteview = [[str(n + headlen), '{:02X}'.format(i), str(i), ascii(chr(i)).strip("'")] for n, i in enumerate(tmsource[:])]
self._feed_tm_data_view_model(datamodel, byteview)
buf = Gtk.TextBuffer(text=head + '\n')
self.tm_header_view.set_buffer(buf)
def _feed_tm_data_view_model(self, model, data):
try:
if not isinstance(data[0], list):
data = [data]
except IndexError:
model.clear()
return
self.tm_data_view.freeze_child_notify()
model.clear()
for row in data:
if row:
model.append(row)
self.tm_data_view.thaw_child_notify()
def save_pool(self, widget):
dialog = SavePoolDialog(parent=self, decoding_type=self.decoding_type)
response = dialog.run()
if response == Gtk.ResponseType.OK:
filename = dialog.get_filename()
modes = dialog.formatbox.get_children()
if modes[0].get_active():
mode = 'binary'
elif modes[1].get_active():
mode = 'hex'
elif modes[2].get_active():
mode = 'text'
merge = dialog.merge_tables.get_active()
if dialog.selectiononly.get_active():
# selection = self.treeview.get_selection()
# model, paths = selection.get_selected_rows()
# indices = [model[path][0] for path in paths]
indices = self.currently_selected
# tmlist = self.ccs.get_packet_selection(indices, self.get_active_pool_name())
tmlist = self.get_packets_from_indices(indices=indices, filtered=dialog.save_filtered.get_active())
else:
# tmlist = self.pool.datapool[self.get_active_pool_name()]['pckts'].values()
indices = None
tmlist = self.get_packets_from_indices(filtered=dialog.save_filtered.get_active(), merged_tables=merge)
crc = dialog.crccheck.get_active()
cfl.Tmdump(filename, tmlist, mode=mode, st_filter=None, check_crc=crc)
self.logger.info(
'{} packets from {} saved as {} in {} mode (CRC {})'.format(len(list(tmlist)),
self.get_active_pool_name(),
filename,
mode.upper(), crc))
if dialog.store_in_db.get_active():
self.save_pool_in_db(filename, int(os.path.getmtime(filename)), indices)
dialog.destroy()
def save_pool_in_db(self, filename, timestamp, indices=None):
# this function only works for PUS packets at the moment
if self.decoding_type != 'PUS':
self.logger.error('Save to DB not supported for {} protocol.'.format(self.decoding_type))
return
new_session = self.session_factory_storage
self.logger.info('Deleting any existing DB entries associated with {}'.format(filename))
new_session.execute(
'DELETE tm FROM tm INNER JOIN tm_pool ON tm.pool_id=tm_pool.iid WHERE tm_pool.pool_name="{}"'.format(
filename))
new_session.execute('DELETE tm_pool FROM tm_pool WHERE tm_pool.pool_name="{}"'.format(filename))
new_session.commit()
self.logger.debug('...deleted')
self.logger.info('Storing current pool {} in DB for {}'.format(self.active_pool_info.pool_name, filename))
newPoolRow = DbTelemetryPool(pool_name=filename, protocol='PUS', modification_time=timestamp)
new_session.add(newPoolRow)
new_session.flush()
rows = new_session.query(
Telemetry[self.decoding_type]
).join(
DbTelemetryPool,
DbTelemetryPool.iid == Telemetry[self.decoding_type].pool_id
).filter(
DbTelemetryPool.pool_name == self.active_pool_info.filename)
if indices is not None:
rows = rows.filter(Telemetry[self.decoding_type].idx.in_(indices))
for idx, row in enumerate(rows, 1):
new_session.add(Telemetry[self.decoding_type](pool_id=newPoolRow.iid,
idx=idx,
is_tm=row.is_tm,
apid=row.apid,
seq=row.seq,
len_7=row.len_7,
stc=row.stc,
sst=row.sst,
destID=row.destID,
timestamp=row.timestamp,
data=row.data,
raw=row.raw))
# new_session.flush()
new_session.commit()
new_session.close()
'''
# Poolmgr can call the LoadInfo Window via dbus, needed for the load_pool function
def LoadInfo(self):
loadinfo = LoadInfo(parent=self)
return loadinfo
'''
def load_saved_pool(self, filename=None, protocol='PUS'):
if filename is not None:
self.load_pool(widget=None, filename=filename, protocol=protocol)
else:
self.logger.error('Please give a Filename')
return 'Please give a Filename'
return
# Whole function is now done in Poolmgr
# def load_pool2(self, widget=None, filename=None, brute=False, force_db_import=False, protocol='PUS'):
# if cfl.is_open('poolmanager', cfl.communication['poolmanager']):
# poolmgr = cfl.dbus_connection('poolmanager', cfl.communication['poolmanager'])
# else:
# cfl.start_pmgr(gui=False)
# #path_pm = os.path.join(confignator.get_option('paths', 'ccs'), 'pus_datapool.py')
# #subprocess.Popen(['python3', path_pm, '--nogui'])
# self.logger.info('Poolmanager was started in the background')
#
# # Here we have a little bit of a tricky situation since when we start the Poolmanager it wants to tell the
# # Manager to which number it can talk to but it can only do this when PoolViewer is not busy...
# # Therefore it is first found out which number the new Poolmanager will get and it will be called by that
# our_con = []
# # Look for all connections starting with com.poolmanager.communication,
# # therefore only one loop over all connections is necessary
# for service in dbus.SessionBus().list_names():
# if service.startswith(self.cfg['ccs-dbus_names']['poolmanager']):
# our_con.append(service)
#
# new_pmgr_nbr = 0
# if len(our_con) != 0: # If an active PoolManager is found they have to belong to another prject
# for k in range(1, 10): # Loop over all possible numbers
# for j in our_con: # Check every number with every PoolManager
# if str(k) == str(j[-1]): # If the number is not found set variable found to True
# found = True
# else: # If number is found set variable found to False
# found = False
# break
#
# if found: # If number could not be found save the number and try connecting
# new_pmgr_nbr = k
# break
#
# else:
# new_pmgr_nbr = 1
#
# if new_pmgr_nbr == 0:
# self.logger.warning('The maximum amount of Poolviewers has been reached')
# return
#
# # Wait a maximum of 10 seconds to connect to the poolmanager
# i = 0
# while i < 100:
# if cfl.is_open('poolmanager', new_pmgr_nbr):
# poolmgr = cfl.dbus_connection('poolmanager', new_pmgr_nbr)
# break
# else:
# i += 1
# time.sleep(0.1)
#
# if filename is not None and filename:
# pool_name = filename.split('/')[-1]
# try:
# new_pool = cfl.Functions(poolmgr, 'load_pool_poolviewer', pool_name, filename, brute, force_db_import,
# self.count_current_pool_rows(), self.my_bus_name[-1], protocol)
#
# except:
# self.logger.warning('Pool could not be loaded, File: ' + str(filename) + 'does probably not exist')
# # print('Pool could not be loaded, File' +str(filename)+ 'does probably not exist')
# return
# else:
# dialog = Gtk.FileChooserDialog(title="Load File to pool", parent=self, action=Gtk.FileChooserAction.OPEN)
# dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
#
# area = dialog.get_content_area()
# hbox, force_button, brute_extract, type_buttons = self.pool_loader_dialog_buttons()
#
# area.add(hbox)
# area.show_all()
#
# dialog.set_transient_for(self)
#
# response = dialog.run()
#
# if response == Gtk.ResponseType.OK:
# filename = dialog.get_filename()
# pool_name = filename.split('/')[-1]
# isbrute = brute_extract.get_active()
# force_db_import = force_button.get_active()
# for button in type_buttons:
# if button.get_active():
# package_type = button.get_label()
# else:
# dialog.destroy()
# return
#
# if package_type:
# if package_type not in ['PUS', 'PLMSIM']:
# package_type == 'SPW'
#
# # package_type defines which type was selected by the user, if any was selected
# new_pool = cfl.Functions(poolmgr, 'load_pool_poolviewer', pool_name, filename, isbrute, force_db_import,
# self.count_current_pool_rows(), self.my_bus_name[-1], package_type)
#
# dialog.destroy()
#
# if new_pool:
# self._set_pool_list_and_display(new_pool)
def load_pool(self, widget=None, filename=None, brute=False, force_db_import=False, protocol='PUS', no_duplicates=True):
if filename is None:
dialog = Gtk.FileChooserDialog(title="Load File to pool", parent=self, action=Gtk.FileChooserAction.OPEN)
dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
area = dialog.get_content_area()
hbox, force_button, brute_extract, type_buttons = self.pool_loader_dialog_buttons()
area.add(hbox)
area.show_all()
dialog.set_transient_for(self)
response = dialog.run()
if response == Gtk.ResponseType.OK:
filename = dialog.get_filename()
brute = brute_extract.get_active()
force_db_import = force_button.get_active()
for button in type_buttons:
if button.get_active():
protocol = button.get_label()
else:
dialog.destroy()
return
if protocol:
if protocol not in ['PUS', 'PLMSIM']:
protocol = 'SPW'
dialog.destroy()
if no_duplicates and (not force_db_import):
loadeditem = self._check_pool_is_loaded(filename)
if loadeditem:
self.pool_selector.set_active_iter(loadeditem.iter)
return
new_pool, loader_thread = cfl.DbTools.sql_insert_binary_dump(filename, brute=brute, force_db_import=force_db_import,
protocol=protocol, pecmode='warn', parent=self)
self._set_pool_list_and_display(new_pool)
if loader_thread is not None:
checkloader_thread = threading.Thread(target=self._db_loading_checker, args=[new_pool, loader_thread])
checkloader_thread.daemon = True
checkloader_thread.start()
def _db_loading_checker(self, pool_info, thread):
try:
model = self.pool_selector.get_model()
for pool in model:
if pool[3] == pool_info.filename:
pool[1] = '[LOAD]'
while thread.is_alive():
time.sleep(1)
model = self.pool_selector.get_model()
for pool in model:
if pool[3] == pool_info.filename:
pool[1] = None
break
# check via filename if loaded pool is currently viewed
if model[self.pool_selector.get_active_iter()][3] == pool_info.filename:
# self.select_pool(self.pool_selector, pool_info.filename)
self.adj.set_upper(self.count_current_pool_rows(pool_info=pool_info))
self.adj.set_value(0)
self._on_scrollbar_changed(adj=self.adj)
except Exception as err:
self.logger.error(err)
model = self.pool_selector.get_model()
for pool in model:
if pool[3] == pool_info.filename:
pool[1] = 'ERR'
def pool_loader_dialog_buttons(self):
'''
Small Function to set up the buttons for the Pool Loading Window
@return: A Gtk.HBox
'''
hbox = Gtk.HBox()
hbox.set_border_width(10)
brute_extract = Gtk.CheckButton.new_with_label('Search valid packets')
brute_extract.set_tooltip_text('Keep searching for valid packets if invalid ones are encountered')
force_button = Gtk.CheckButton.new_with_label('Force DB Import')
force_button.set_tooltip_text(
'Do a fresh import of the packets in the dump, even if they are already in the DB')
hbox.pack_end(brute_extract, 0, 0, 0)
hbox.pack_end(force_button, 0, 0, 0)
import_pool_win_buttons = []
i = 1
# for packet_type in self.column_labels:
for packet_type in ['PUS', 'SPW']:
if i == 1:
button1 = Gtk.RadioButton(label=str(packet_type))
button1.set_tooltip_text("Imported file has {} protocol".format(packet_type))
button1.set_sensitive(True)
import_pool_win_buttons.append(button1)
hbox.pack_end(button1, 0, 0, 0)
else:
button = Gtk.RadioButton.new_from_widget(button1)
button.set_label(str(packet_type))
button.set_sensitive(True)
button.set_tooltip_text("Imported file has {} protocol".format(packet_type))
import_pool_win_buttons.append(button)
hbox.pack_end(button, 0, 0, 0)
i += 1
# force_button.connect("toggled", self._on_force_button_changed, import_pool_win_buttons)
# hbox.pack_end(force_button, 0, 0, 0)
return hbox, force_button, brute_extract, import_pool_win_buttons
def _on_force_button_changed(self, widget, buttons):
if widget.get_active():
for button in buttons:
button.set_sensitive(True)
else:
for button in buttons:
button.set_sensitive(False)
# Glib.idle_add only does only do something when there is time, sometimes this is blocked until the main loop does
# another iteration, this nonesense function start this and lets Glib.idle add do the funciton
# Also used to set up the thread variable in resize scrollbar
def small_refresh_function(self):
return
# Only to use Glib idle add and ignore_reply keyword at the same time
def _set_list_and_display_Glib_idle_add(self, active_pool_info_mgr=None, instance=None):
if active_pool_info_mgr is not None:
GLib.idle_add(self._set_pool_list_and_display(active_pool_info_mgr, instance))
else:
GLib.idle_add(self._set_pool_list_and_display(instance=instance))
return
def _set_pool_list_and_display(self, pool_info=None, instance=None):
if pool_info is not None:
self.Active_Pool_Info_append(pool_info)
self.update_columns()
self.adj.set_upper(self.count_current_pool_rows())
self.offset = 0
self.limit = self.adj.get_page_size()
self._on_scrollbar_changed(adj=self.adj, force=True)
# Check the decoding type to show a pool
if self.decoding_type == 'PUS':
model = self.pool_selector.get_model()
iter = model.append([self.active_pool_info.pool_name, self.live_signal[self.active_pool_info.live], self.decoding_type, self.active_pool_info.filename])
self.pool_selector.set_active_iter(iter)
else:
# If not PUS open all other possible types but show RMAP
for packet_type in Telemetry:
if packet_type == 'RMAP':
model = self.pool_selector.get_model()
iter = model.append([self.active_pool_info.pool_name, self.live_signal[self.active_pool_info.live], packet_type, self.active_pool_info.filename])
self.pool_selector.set_active_iter(iter) # Always show the RMAP pool when created
else:
model = self.pool_selector.get_model()
iter = model.append([self.active_pool_info.pool_name, self.live_signal[self.active_pool_info.live], packet_type, self.active_pool_info.filename])
if self.active_pool_info.live:
self.stop_butt.set_sensitive(True)
else:
self.stop_butt.set_sensitive(False)
refresh_rate = 1 # in Hz
GLib.timeout_add(1000 / refresh_rate, self.show_data_rate, refresh_rate, instance, priority=GLib.PRIORITY_DEFAULT)
return True
def collect_packet_data(self, widget):
# selection = self.treeview.get_selection()
# model, paths = selection.get_selected_rows()
if not self.active_pool_info:
self.logger.error('No pool to extract packets from')
return
###############
# If this is ever changed to all packet standards and not only PUS, be aware that further down the database is asced of ST and SST... only possible for PUS
###############
indices = self.currently_selected
dialog = ExtractionDialog(parent=self, pkttype=self.decoding_type)
response = dialog.run()
if response == Gtk.ResponseType.OK:
try:
st, sst = dialog.st.get_text(), dialog.sst.get_text()
onlysource = dialog.sourcebox.get_active()
new_session = self.session_factory_storage
rows = new_session.query(
Telemetry[self.decoding_type]
).join(
DbTelemetryPool,
Telemetry[self.decoding_type].pool_id == DbTelemetryPool.iid
).filter(
DbTelemetryPool.pool_name == self.active_pool_info.filename
).filter(
Telemetry[self.decoding_type].idx.in_(indices)
)
except AttributeError as error:
self.logger.error(error)
dialog.destroy()
return
if st != '':
rows = rows.filter(DbTelemetry.stc == int(st))
if sst != '':
rows = rows.filter(DbTelemetry.sst == int(sst))
packets = [row.raw for row in rows]
new_session.close()
if onlysource:
packetdata = [tm[TM_HEADER_LEN:-PEC_LEN] for tm in packets]
else:
packetdata = packets
self.selected_packet(packetdata)
dialog.destroy()
def selected_packet(self, packet=None):
if packet is not None:
self.stored_packet = packet
return packet
else:
return str(self.stored_packet)
def get_packets_from_indices(self, indices=None, filtered=False, merged_tables=False):
if indices is None:
indices = []
new_session = self.session_factory_storage
if not merged_tables:
rows = new_session.query(
Telemetry[self.decoding_type]
).join(
DbTelemetryPool,
Telemetry[self.decoding_type].pool_id == DbTelemetryPool.iid
).filter(
DbTelemetryPool.pool_name == self.active_pool_info.filename
)
if len(indices) != 0:
rows = rows.filter(
Telemetry[self.decoding_type].idx.in_(indices)
)
if filtered and self.filter_rules_active:
rows = self._filter_rows(rows)
ret = [row.raw for row in rows]
else:
ret = self.get_raw_from_merged_tables(self.active_pool_info.filename)
new_session.close()
return ret
def get_raw_from_merged_tables(self, pool_name, filtered=False):
# db = self.session_factory_storage
# q1 = db.query(RMapTelemetry.idx,RMapTelemetry.raw).join(DbTelemetryPool,RMapTelemetry.pool_id==DbTelemetryPool.iid).filter(DbTelemetryPool.pool_name==pool_name)
# q2 = db.query(FEEDataTelemetry.idx,FEEDataTelemetry.raw).join(DbTelemetryPool,FEEDataTelemetry.pool_id==DbTelemetryPool.iid).filter(DbTelemetryPool.pool_name==pool_name)
# rows = q1.union_all(q2).order_by(FEEDataTelemetry.idx)
# if filtered and self.filter_rules_active:
# rows = self._filter_rows(rows)
# return (p.raw for p in q.yield_per(1000))
que = 'SELECT idx,raw FROM rmap_tm LEFT JOIN tm_pool ON pool_id=tm_pool.iid WHERE pool_name="{}"\
UNION SELECT idx,raw FROM feedata_tm LEFT JOIN tm_pool ON pool_id=tm_pool.iid WHERE pool_name="{}"\
ORDER BY idx'.format(pool_name, pool_name)
# alternative fetch with stream
# conn = self.session_factory_storage.connection()
# res = conn.execution_options(stream_results=True).execute(que)
# self.session_factory_storage.close()
res = self.session_factory_storage.execute(que)
return [row[1] for row in res]
def plot_parameters(self, widget=None, parameters=None, start_live=False):
if not self.active_pool_info.filename:
self.logger.warning('No pool selected')
return
cfl.start_plotter(self.active_pool_info.filename)
def monitor_parameters(self, widget=None, **kwargs):
if not self.active_pool_info.filename:
self.logger.warning('No pool selected')
return
cfl.start_monitor(self.active_pool_info.filename, parameter_set=self._selected_mon_par_set)
def start_recording(self, widget=None):
if cfl.is_open('poolmanager', cfl.communication['poolmanager']):
poolmgr = cfl.dbus_connection('poolmanager', cfl.communication['poolmanager'])
else:
path_pm = os.path.join(self.cfg.get('paths', 'ccs'), 'pus_datapool.py')
subprocess.Popen(['python3', path_pm])
return
if poolmgr.Variables('gui_running'):
poolmgr.Functions('raise_window')
return
# Ignore_reply is no problem here since only the gui is started
poolmgr.Functions('start_gui', ignore_reply = True)
return
def stop_recording(self, widget=None, pool_name=None):
if not self.active_pool_info.live:
return False
if cfl.is_open('poolmanager', cfl.communication['poolmanager']):
poolmgr = cfl.dbus_connection('poolmanager', cfl.communication['poolmanager'])
else:
poolmgr = None
if pool_name is None:
pool_name = self.active_pool_info.pool_name
# Not necessary done in Poolmanager
# Dictionarie in Dictionaries are not very well supported over dbus connection
# Get dictonary connections, key pool_name, key 'recording' set to False, True to change
# poolmgr.Dictionaries('connections', pool_name, 'recording', False, True)
if self.active_pool_info.pool_name == pool_name:
self.active_pool_info = ActivePoolInfo(pool_name, self.active_pool_info.modification_time, pool_name, False)
if poolmgr:
poolmgr.Functions('loaded_pools_func', pool_name, self.active_pool_info)
else:
# Check if the pool name does exist
try:
pinfo = poolmgr.Dictionaries('loaded_pools', pool_name)
except (dbus.DBusException, KeyError):
self.logger.error("Can't stop recording: pool name not in loaded pools")
return
pinfo_modification_time = int(pinfo[2])
#poolmgr.Functions('loaded_pools_func', pool_name, ActivePoolInfo(pool_name, pinfo_modification_time, pool_name, False), ignore_reply=True)
if poolmgr:
poolmgr.Functions('loaded_pools_func', pool_name,
ActivePoolInfo(pool_name, pinfo_modification_time, pool_name, False))
# Update the Poolmanager GUI
#poolmgr.Functions('disconnect', pool_name, ignore_reply = True)
if poolmgr:
poolmgr.Functions('disconnect', pool_name)
itr = self.pool_selector.get_active_iter()
mod = self.pool_selector.get_model()
if mod is not None:
mod[itr][1] = self.live_signal[self.active_pool_info.live]
self.stop_butt.set_sensitive(False)
return
def stop_recording_info(self, pool_name=None):
"""
Connection is closed by the Poolmanager, Informes the Pool Viewer to stop updating or
Poolmanager is closed and therefore all Pools become static pools
Functions is normally called by the poolmanager when it is closing or disconnecting any connections
"""
# if no pool name was specified, Change all connections to static
if not pool_name:
mod = self.pool_selector.get_model()
self.active_pool_info = ActivePoolInfo(self.active_pool_info.filename,
self.active_pool_info.modification_time,
self.active_pool_info.pool_name, False)
#self.active_pool_info.live = False
for row in mod:
mod[row.iter][1] = self.live_signal[self.active_pool_info.live]
self.stop_butt.set_sensitive(False)
# If active pool is live change it to static
elif self.active_pool_info.pool_name == pool_name:
self.active_pool_info = ActivePoolInfo(self.active_pool_info.filename, self.active_pool_info.modification_time, self.active_pool_info.pool_name, False)
iter = self.pool_selector.get_active_iter()
mod = self.pool_selector.get_model()
if mod is not None:
mod[iter][1] = self.live_signal[self.active_pool_info.live]
self.stop_butt.set_sensitive(False)
# Specific Pool is no longer LIVe
else:
mod = self.pool_selector.get_model()
for row in mod:
if mod[row.iter][0] == pool_name:
mod[row.iter][1] = self.live_signal[self.active_pool_info.live]
self.stop_butt.set_sensitive(False)
return
def refresh_treeview(self, pool_name):
if self.refresh_treeview_active:
return
self.refresh_treeview_active = True
self.n_pool_rows = 0
GLib.timeout_add(self.pool_refresh_rate * 1000, self.refresh_treeview_worker2, pool_name) # priority=GLib.PRIORITY_HIGH)
# def refresh_treeview_worker(self, pool_name):
# poolmgr = cfl.dbus_connection('poolmanager', cfl.communication ['poolmanager'])
# # while not self.pool.recordingThread.stopRecording:
# # Get value of dict connections, with key self.active... and key recording, True to get
# pool_connection_recording = cfl.Dictionaries(poolmgr, 'connections', self.active_pool_info.pool_name, 'recording', True)
# type = self.decoding_type
# #while self.pool.connections[self.active_pool_info.pool_name]['recording']:
# while pool_connection_recording:
# GLib.idle_add(self.scroll_to_bottom)
# time.sleep(self.pool_refresh_rate)
# if pool_name != self.active_pool_info.pool_name or type != self.decoding_type:
# dbsession.close()
# return
# self.stop_recording()
def refresh_treeview_worker2(self, pool_name):
if pool_name != self.active_pool_info.pool_name:
self.refresh_treeview_active = False
return False
if self.active_pool_info.live:
rows = self.get_current_pool_rows()
if rows.first() is None:
cnt = 0
else:
cnt = rows.order_by(Telemetry[self.decoding_type].idx.desc()).first().idx
if cnt != self.n_pool_rows:
self.scroll_to_bottom(n_pool_rows=cnt, rows=rows)
self.n_pool_rows = cnt
return True
else:
return True
else:
self.refresh_treeview_active = False
return False
def dbtest(self, pool_name, sleep=0.1):
dbcon = self.session_factory_storage
while self.testing:
rows = dbcon.query(
Telemetry[self.decoding_type]
).join(
DbTelemetryPool,
Telemetry[self.decoding_type].pool_id == DbTelemetryPool.iid
).filter(
DbTelemetryPool.pool_name == pool_name
)
# cnt = rows.count()
cnt = rows.order_by(Telemetry[self.decoding_type].idx.desc()).first().idx
# print(cnt)
rows = rows.filter(Telemetry[self.decoding_type].idx > (cnt - 100)).offset(100 - 25).limit(25).all()
# rr=[row for row in rows]
self.logger.info('fetched', rows[-1].idx, cnt, 'at', time.time())
dbcon.close()
time.sleep(sleep)
self.logger.warning('TEST ABORTED')
def starttest(self, pool_name, sleep=0.1):
t = threading.Thread(target=self.dbtest, args=[pool_name, sleep])
t.daemon = True
t.start()
def scroll_to_bottom(self, n_pool_rows=None, rows=None):
if self.active_pool_info.live:
if n_pool_rows is None:
cnt = self.count_current_pool_rows()
else:
cnt = n_pool_rows
self.adj.set_upper(cnt)
if self.autoscroll:
self._scroll_treeview(self.adj.get_upper(), rows=rows)
self.reselect_rows()
else:
self._scroll_treeview(self.adj.get_upper(), rows=rows)
def change_cursor(self, window, name='default'):
window.set_cursor(Gdk.Cursor.new_from_name(Gdk.Display.get_default(), name))
def show_data_rate(self, refresh_rate=1, instance=1):
if not self.active_pool_info.live:
return False
if not instance:
instance = 1
try:
pmgr = cfl.dbus_connection('poolmanager', instance)
trashbytes, tc_data_rate, data_rate, tc_rx_bytes = pmgr.Functions('calc_data_rate', self.active_pool_info.filename, refresh_rate)
self.statusbar.push(0, 'Trash: {:d} B | TC: {:7.3f} KiB/s | TM: {:7.3f} KiB/s'.format(
trashbytes, tc_data_rate/1024, data_rate/1024))
self.statusbar.set_tooltip_text('TCRX: {:d} B | TC: {:7.3f} kbps | TM: {:7.3f} kbps'.format(tc_rx_bytes, tc_data_rate/1000*8, data_rate/1000*8))
except Exception as err:
self.logger.debug(err)
return True
def show_bigdata(self, *args):
self.bigd = BigDataViewer(self)
class ExtractionDialog(Gtk.MessageDialog):
def __init__(self, parent=None, pkttype='PUS'):
super(ExtractionDialog, self).__init__(title="Extract packets", parent=parent, flags=0)
self.add_buttons(Gtk.STOCK_OK, Gtk.ResponseType.OK, Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
self.set_transient_for(parent)
box = self.get_content_area()
self.set_markup('Select Service Type and Subtype to be extracted (PUS)')
hbox = Gtk.HBox()
self.st = Gtk.Entry()
self.sst = Gtk.Entry()
self.st.set_placeholder_text('Service Type')
self.sst.set_placeholder_text('Service Subtype')
hbox.pack_start(self.st, 0, 0, 0)
hbox.pack_start(self.sst, 0, 0, 0)
hbox.set_homogeneous(True)
self.sourcebox = Gtk.CheckButton.new_with_label('Source data only')
box.pack_end(self.sourcebox, 0, 0, 0)
box.pack_end(hbox, 0, 0, 5)
if pkttype != 'PUS':
self.st.set_sensitive(False)
self.sst.set_sensitive(False)
self.sourcebox.set_sensitive(False)
self.show_all()
class SavePoolDialog(Gtk.FileChooserDialog):
def __init__(self, parent=None, decoding_type='PUS'):
super(SavePoolDialog, self).__init__(title="Save packets", parent=parent, action=Gtk.FileChooserAction.SAVE)
self.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK)
# self.set_transient_for(parent)
area = self.get_content_area()
hbox = Gtk.HBox()
hbox.set_border_width(10)
self.formatbox = Gtk.HBox()
binbut = Gtk.RadioButton.new_with_label_from_widget(None, 'binary')
hexbut = Gtk.RadioButton.new_with_label_from_widget(binbut, 'hex')
csvbut = Gtk.RadioButton.new_with_label_from_widget(binbut, 'csv (decoded)')
self.formatbox.pack_start(binbut, 0, 0, 3)
self.formatbox.pack_start(hexbut, 0, 0, 3)
self.formatbox.pack_start(csvbut, 0, 0, 3)
self.selectiononly = Gtk.CheckButton.new_with_label('Save only selected packets')
self.crccheck = Gtk.CheckButton.new_with_label('CRC')
self.crccheck.set_tooltip_text('Save only packets that pass CRC')
self.store_in_db = Gtk.CheckButton.new_with_label('Store in DB')
self.store_in_db.set_tooltip_text('Permanently store pool in DB - THIS MAY TAKE A WHILE FOR LARGE DATASETS!')
if decoding_type != 'PUS':
self.store_in_db.set_sensitive(False)
self.save_filtered = Gtk.CheckButton.new_with_label('Apply packet filter')
self.save_filtered.set_tooltip_text('Save only packets according to the currently active poolview filter')
self.merge_tables = Gtk.CheckButton.new_with_label('Merge tables')
self.merge_tables.set_tooltip_text('Merge and save all packet types from the same pool/connection')
hbox.pack_start(self.formatbox, 0, 0, 0)
hbox.pack_start(self.selectiononly, 0, 0, 0)
hbox.pack_start(self.crccheck, 0, 0, 0)
hbox.pack_start(self.store_in_db, 0, 0, 0)
hbox.pack_start(self.save_filtered, 0, 0, 0)
hbox.pack_start(self.merge_tables, 0, 0, 0)
hbox.set_homogeneous(True)
area.add(hbox) # ,0,0,10)
self.show_all()
# TODO
class LoadPoolDialog(Gtk.FileChooserDialog):
def __init__(self, parent=None):
super(LoadPoolDialog, self).__init__(title="Save packets", parent=parent, action=Gtk.FileChooserAction.SAVE,
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
dialog = Gtk.FileChooserDialog(title="Load File to pool", parent=self, action=Gtk.FileChooserAction.OPEN)
dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
area = dialog.get_content_area()
hbox, force_button, brute_extract, type_buttons = self.pool_loader_dialog_buttons()
area.add(hbox)
area.show_all()
self.show_all()
'''
class LoadInfo(Gtk.Window):
def __init__(self, parent=None, title=None):
Gtk.Window.__init__(self)
if title is None:
self.set_title('Loading data to pool...')
else:
self.set_title(title)
grid = Gtk.VBox()
logo = Gtk.Image.new_from_file('pixmap/cheops-logo-with-additional3.png')
self.spinner = Gtk.Spinner()
self.spinner.set_size_request(32, 32)
self.log = Gtk.Label()
self.ok_button = Gtk.Button.new_with_label('OK')
self.ok_button.connect('clicked', self.destroy_window, self)
grid.pack_start(logo, 1, 1, 0)
grid.pack_start(self.spinner, 1, 1, 0)
grid.pack_start(self.log, 1, 1, 0)
grid.pack_start(self.ok_button, 1, 1, 0)
grid.set_spacing(2)
self.add(grid)
self.show_all()
def destroy_window(self, widget, window):
window.destroy()
'''
class RecordingDialog(Gtk.MessageDialog):
def __init__(self, parent=None):
Gtk.Dialog.__init__(self, "Record to pool", parent, 0,
buttons=(Gtk.STOCK_OK, Gtk.ResponseType.OK, Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL))
# self.set_transient_for(parent)
ok_butt = self.get_action_area().get_children()[0]
ok_butt.set_always_show_image(True)
ok_butt.set_image(Gtk.Image.new_from_icon_name('gtk-media-record', Gtk.IconSize.BUTTON))
ok_butt.set_label('Connect')
ok_butt.set_sensitive(True)
box = self.get_content_area()
self.set_markup('Start recording from socket:')
vbox = Gtk.VBox()
vbox.set_spacing(2)
self.host = Gtk.Entry(text="127.0.0.1")
self.host.connect('changed', self.check_entry, ok_butt)
self.port = Gtk.Entry(text="5570")
self.port.connect('changed', self.check_entry, ok_butt)
self.pool_name = Gtk.Entry(text="LIVE")
self.pool_name.connect('changed', self.check_entry, ok_butt)
self.host.set_placeholder_text('Host')
self.port.set_placeholder_text('Port')
self.pool_name.set_placeholder_text('Pool name')
vbox.pack_start(self.host, 0, 0, 0)
vbox.pack_start(self.port, 0, 0, 0)
vbox.pack_start(self.pool_name, 0, 0, 0)
vbox.set_homogeneous(True)
box.pack_end(vbox, 0, 0, 0)
self.set_focus(self.get_action_area().get_children()[1])
self.show_all()
def check_entry(self, widget, button):
fields = self.get_content_area().get_children()[1].get_children()
if not all([len(field.get_text()) for field in fields]):
button.set_sensitive(False)
return
try:
int(self.port.get_text())
button.set_sensitive(True)
except ValueError:
button.set_sensitive(False)
class BigDataViewer(Gtk.Window):
def __init__(self, pv):
super(BigDataViewer, self).__init__()
self.pv = pv
self.interval = 0.05
self.connect('delete-event', self.stop_thread)
self.hscale = 1
self.maxhscale = 8
self.minhscale = 0.125
self.init_ui()
self.start_cycle()
def init_ui(self):
self.darea = Gtk.DrawingArea()
self.darea.connect("draw", self.on_draw)
self.darea.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
self.darea.add_events(Gdk.EventMask.SCROLL_MASK)
self.darea.connect("button-press-event", self.on_button_press)
self.darea.connect("scroll-event", self.set_hscale)
self.add(self.darea)
self.set_title("Big Data")
self.resize(500, 800)
self.set_position(Gtk.WindowPosition.CENTER)
self.show_all()
def on_draw(self, widget, cr):
poolmgr = cfl.dbus_connection('poolmanager', cfl.communication['poolmanager'])
if not poolmgr:
return
check = poolmgr.Functions('_return_colour_list', 'try')
if check is False:
return
n = int(self.darea.get_allocated_height() / self.hscale)
w = self.darea.get_allocated_width()
length = poolmgr.Functions('_return_colour_list', 'length')
for i in range(min(n, length)):
#for i in range(min(n, len(self.pv.pool.colour_list))):
#rgb, pcktlen = self.pv.pool.colour_list[-i - 1]
rgb, pcktlen = poolmgr.Functions('_return_colour_list', i)
cr.rectangle(0, self.hscale * (n - i), (pcktlen + 7) / 1024 * w, self.hscale)
cr.set_source_rgb(*rgb)
cr.fill()
# cr.translate(220, 180)
# cr.scale(1, 0.7)
# cr.fill()
def on_button_press(self, w, e):
if e.type == Gdk.EventType.BUTTON_PRESS and e.button == 1:
self.start_cycle()
# self.palette = [np.random.random(3) for i in range(1000)]
# self.darea.queue_draw()
if e.type == Gdk.EventType.BUTTON_PRESS and e.button == 3:
self.darea.queue_draw()
self.cycle_on = False
def start_cycle(self):
self.cycle_on = True
if 'BigDataViewer' in [x.name for x in threading.enumerate()]:
# self.interval /= 2.
return
t = threading.Thread(target=self.cycle_worker)
t.setName('BigDataViewer')
t.setDaemon(True)
t.start()
def cycle_worker(self):
while self.cycle_on:
GLib.idle_add(self.darea.queue_draw)
time.sleep(self.interval)
def stop_thread(self, widget=None, data=None):
self.cycle_on = False
def set_hscale(self, widget, event):
if event.direction.value_name == 'GDK_SCROLL_SMOOTH':
scale = 2 ** (-event.delta_y)
# needed for remote desktop
elif event.direction.value_name == 'GDK_SCROLL_UP':
scale = 2
elif event.direction.value_name == 'GDK_SCROLL_DOWN':
scale = 0.5
else:
return
self.hscale = max(min(self.hscale * scale, self.maxhscale), self.minhscale)
class UnsavedBufferDialog(Gtk.MessageDialog):
def __init__(self, parent=None, msg=None):
Gtk.MessageDialog.__init__(self, title="Close Poolmanager?", parent=parent, flags=0,
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_NO, Gtk.ResponseType.NO,
Gtk.STOCK_YES, Gtk.ResponseType.YES,))
head, message = self.get_message_area().get_children()
if msg is None:
head.set_text('Response NO will keep the Poolmanager running in the Background')
else:
head.set_text(msg)
self.show_all()
def run(pool_name):
bus_name = cfg.get('ccs-dbus_names', 'poolviewer')
DBusGMainLoop(set_as_default=True)
pv = TMPoolView()
DBus_Basic.MessageListener(pv, bus_name, *sys.argv)
if pool_name is not None:
pv.set_pool(pool_name)
Gtk.main()
if __name__ == "__main__":
poolname = None
if len(sys.argv) > 1:
for arg in sys.argv[1:]:
if not arg.startswith('-'):
poolname = arg
run(pool_name=poolname)