Skip to content

Commit

Permalink
lestarch: adding dictionary information to GUI (#80)
Browse files Browse the repository at this point in the history
* lestarch: adding dictionary information to GUI

* lestarch: sp

* lestarch: nitpick
  • Loading branch information
LeStarch authored Jun 28, 2022
1 parent 3975ae4 commit 7922fd9
Show file tree
Hide file tree
Showing 18 changed files with 144 additions and 25 deletions.
1 change: 1 addition & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ datastore
datastructures
datetime
dblclick
Dct
ddl
DDTHH
Ded
Expand Down
3 changes: 2 additions & 1 deletion src/fprime_gds/common/loaders/ch_xml_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def construct_dicts(self, path):
respectively and the values are ChTemplate objects
"""
xml_tree = self.get_xml_tree(path)
versions = xml_tree.attrib.get("framework_version", "unknown"), xml_tree.attrib.get("project_version", "unknown")

# Check if xml dict has channels section
ch_section = self.get_xml_section(self.CH_SECT, xml_tree)
Expand Down Expand Up @@ -122,4 +123,4 @@ def construct_dicts(self, path):
id_dict[ch_id] = ch_temp
name_dict[ch_temp.get_full_name()] = ch_temp

return id_dict, name_dict
return id_dict, name_dict, versions
3 changes: 2 additions & 1 deletion src/fprime_gds/common/loaders/cmd_xml_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def construct_dicts(self, path):
respectively and the values are CmdTemplate objects
"""
xml_tree = self.get_xml_tree(path)
versions = xml_tree.attrib.get("framework_version", "unknown"), xml_tree.attrib.get("project_version", "unknown")

# Check if xml dict has commands section
cmd_section = self.get_xml_section(self.CMD_SECT, xml_tree)
Expand Down Expand Up @@ -71,4 +72,4 @@ def construct_dicts(self, path):
id_dict[cmd_opcode] = cmd_temp
name_dict[cmd_temp.get_full_name()] = cmd_temp

return id_dict, name_dict
return id_dict, name_dict, versions
11 changes: 8 additions & 3 deletions src/fprime_gds/common/loaders/dict_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def __init__(self):
An initialized loader object
"""
self.saved_dicts = {}
self.versions = ("unknown", "unknown")

def get_id_dict(self, path):
"""
Expand All @@ -61,7 +62,7 @@ def get_id_dict(self, path):
if path in self.saved_dicts:
(id_dict, name_dict) = self.saved_dicts[path]
else:
(id_dict, name_dict) = self.construct_dicts(path)
(id_dict, name_dict, self.versions) = self.construct_dicts(path)
self.saved_dicts[path] = (id_dict, name_dict)

return id_dict
Expand All @@ -86,11 +87,15 @@ def get_name_dict(self, path):
if path in self.saved_dicts:
(id_dict, name_dict) = self.saved_dicts[path]
else:
(id_dict, name_dict) = self.construct_dicts(path)
(id_dict, name_dict, self.versions) = self.construct_dicts(path)
self.saved_dicts[path] = (id_dict, name_dict)

return name_dict

def get_versions(self):
""" Get version tuple """
return self.versions

def construct_dicts(self, path):
"""
Constructs and returns python dictionaries keyed on id and name.
Expand All @@ -107,4 +112,4 @@ def construct_dicts(self, path):
for both should be data_template classes. This base class only
returns empty dictionaries.
"""
return dict(), dict()
return dict(), dict(), tuple(("unknown", "unknown"))
3 changes: 2 additions & 1 deletion src/fprime_gds/common/loaders/event_xml_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def construct_dicts(self, path):
respectively and the values are ChTemplate objects
"""
xml_tree = self.get_xml_tree(path)
versions = xml_tree.attrib.get("framework_version", "unknown"), xml_tree.attrib.get("project_version", "unknown")

# Check if xml dict has events section
event_section = self.get_xml_section(self.EVENT_SECT, xml_tree)
Expand Down Expand Up @@ -83,4 +84,4 @@ def construct_dicts(self, path):
id_dict[event_id] = event_temp
name_dict[event_temp.get_full_name()] = event_temp

return id_dict, name_dict
return id_dict, name_dict, versions
14 changes: 14 additions & 0 deletions src/fprime_gds/common/pipeline/dictionaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def __init__(self):
self._event_name_dict = None
self._channel_name_dict = None
self._packet_dict = None
self._versions = None

def load_dictionaries(self, dictionary, packet_spec):
"""
Expand Down Expand Up @@ -84,14 +85,17 @@ def load_dictionaries(self, dictionary, packet_spec):
event_loader = fprime_gds.common.loaders.event_xml_loader.EventXmlLoader()
self._event_id_dict = event_loader.get_id_dict(dictionary)
self._event_name_dict = event_loader.get_name_dict(dictionary)
self._versions = event_loader.get_versions()
# Commands
command_loader = fprime_gds.common.loaders.cmd_xml_loader.CmdXmlLoader()
self._command_id_dict = command_loader.get_id_dict(dictionary)
self._command_name_dict = command_loader.get_name_dict(dictionary)
assert self._versions == command_loader.get_versions(), "Version mismatch while loading"
# Channels
channel_loader = fprime_gds.common.loaders.ch_xml_loader.ChXmlLoader()
self._channel_id_dict = channel_loader.get_id_dict(dictionary)
self._channel_name_dict = channel_loader.get_name_dict(dictionary)
assert self._versions == channel_loader.get_versions(), "Version mismatch while loading"
else:
raise Exception(f"[ERROR] Dictionary '{dictionary}' does not exist.")
# Check for packet specification
Expand Down Expand Up @@ -133,6 +137,16 @@ def channel_name(self):
"""Channel dictionary by name"""
return self._channel_name_dict

@property
def project_version(self):
"""Project version in dictionary"""
return self._versions[1]

@property
def framework_version(self):
"""Framework version in dictionary"""
return self._versions[0]

@property
def packet(self):
"""Packet dictionary"""
Expand Down
2 changes: 1 addition & 1 deletion src/fprime_gds/common/tools/seqgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def generateSequence(inputFile, outputFile, dictionary, timebase, cont=False):
# Check the user environment:
cmd_xml_dict = CmdXmlLoader()
try:
(cmd_id_dict, cmd_name_dict) = cmd_xml_dict.construct_dicts(dictionary)
(cmd_id_dict, cmd_name_dict, versions) = cmd_xml_dict.construct_dicts(dictionary)
except gseExceptions.GseControllerUndefinedFileException:
raise SeqGenException("Can't open file '" + dictionary + "'. ")

Expand Down
6 changes: 3 additions & 3 deletions src/fprime_gds/flask/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def construct_app():
api.add_resource(
fprime_gds.flask.commands.CommandDictionary,
"/dictionary/commands",
resource_class_args=[pipeline.dictionaries.command_name],
resource_class_args=[pipeline.dictionaries.command_name, pipeline.dictionaries.project_version, pipeline.dictionaries.framework_version],
)
api.add_resource(
fprime_gds.flask.commands.CommandHistory,
Expand All @@ -106,7 +106,7 @@ def construct_app():
api.add_resource(
fprime_gds.flask.events.EventDictionary,
"/dictionary/events",
resource_class_args=[pipeline.dictionaries.event_id],
resource_class_args=[pipeline.dictionaries.event_id, pipeline.dictionaries.project_version, pipeline.dictionaries.framework_version],
)
api.add_resource(
fprime_gds.flask.events.EventHistory,
Expand All @@ -116,7 +116,7 @@ def construct_app():
api.add_resource(
fprime_gds.flask.channels.ChannelDictionary,
"/dictionary/channels",
resource_class_args=[pipeline.dictionaries.channel_id],
resource_class_args=[pipeline.dictionaries.channel_id, pipeline.dictionaries.project_version, pipeline.dictionaries.framework_version],
)
api.add_resource(
fprime_gds.flask.channels.ChannelHistory,
Expand Down
16 changes: 12 additions & 4 deletions src/fprime_gds/flask/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,29 @@ class DictionaryResource(Resource):
should be flask compatible. Errors with the dictionary are a 500 server error and may not be recovered.
"""

def __init__(self, dictionary):
def __init__(self, dictionary, project_version, framework_version):
"""Constructor used to setup for dictionary
Args:
dictionary: dictionary to serve when GET is called
project_version: project version for the dictionary
framework_version: project version for the dictionary
"""
self.dictionary = dictionary
self.project_version = project_version
self.framework_version = framework_version

def get(self):
"""HTTP GET method handler for dictionary resource
Returns:
dictionary ready for conversion into JSON
"""
return self.dictionary
return {
"dictionary": self.dictionary,
"project_version": self.project_version,
"framework_version": self.framework_version
}


class HistoryResourceBase(Resource):
Expand All @@ -53,10 +61,10 @@ def __init__(self, history):
"""
self.parser = RequestParser()
self.parser.add_argument(
"session", required=True, help="Session key for fetching data."
"session", required=True, help="Session key for fetching data.", location="args"
)
self.parser.add_argument(
"limit", required=False, help="Limit to results returned (default 2000)"
"limit", required=False, help="Limit to results returned (default 2000)", location="args"
)

self.history = history
Expand Down
3 changes: 1 addition & 2 deletions src/fprime_gds/flask/static/addons/chart-display/addon.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
chart_display_template,
} from "./addon-templates.js";
import { _datastore, _dictionaries } from "../../js/datastore.js";
import { _loader } from "../../js/loader.js";
import { SiblingSet } from "./sibling.js";
import { timeToDate } from "../../js/vue-support/utils.js";
import {loadTextFileInputData, saveTextFileViaHref} from "../../js/loader.js";
Expand Down Expand Up @@ -96,7 +95,7 @@ Vue.component("chart-display", {
props: ["id", "siblings", "selected"],
data: function () {
const names_list = Object.values(
_loader.endpoints["channel-dict"].data
_dictionaries.channels
).map((value) => {
return flatten(value.type_obj, {
maxDepth: 20,
Expand Down
26 changes: 26 additions & 0 deletions src/fprime_gds/flask/static/addons/dictionary/addon-templates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export let dictionary_template = `
<div class="fp-flex-repeater">
<div class="fp-flex-repeater">
<ul class="nav nav-tabs">
<li :class="['nav-item', 'nav-link', { active: active == key }]"
v-for="key in Object.keys(this.dictionaries)" >
<a @click="change(key)">{{ capitalize(key) }}</a>
</li>
</ul>
<div class="container-fluid fp-flex-repeater" v-show="active == key" v-for="key in Object.keys(this.dictionaries)">
<h2>{{ capitalize(key) }} Dictionary</h2>
<fp-table :header-columns="['ID', 'Name', 'Description']"
:items="dictionaries[key]"
:item-to-columns="columnify"
:item-to-unique="(item) => item"
></fp-table>
</div>
</div>
</div>
`;
export let version_template = `
<small>
Dictionary Version: {{ project_version }}
Dictionary Schema: {{ framework_version }}
</small>
`;
46 changes: 46 additions & 0 deletions src/fprime_gds/flask/static/addons/dictionary/addon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {dictionary_template, version_template} from "./addon-templates.js";
import {_dictionaries} from "../../js/datastore.js";
import {sentenceCase} from "../../js/vue-support/utils.js"

function copy(text) {
navigator.clipboard.writeText(text);
}
/**
* Sequencer component used to represent the composition and editing of sequences.
*/
Vue.component("dictionary", {
template: dictionary_template,
data() {
return {
dictionaries: {
commands: Object.values(_dictionaries.commands_by_id),
events: Object.values(_dictionaries.events),
channels: Object.values(_dictionaries.channels)
},
active: "commands",
};
},
methods: {
capitalize(key) {
return sentenceCase(key);
},
change(new_key) {
this.active = new_key;
},
columnify(item) {
return [this.keyify(item), item.full_name, item.description || item.ch_desc];
},
keyify(item) {
return item.id || item.opcode;
}
}
});
Vue.component("dictionary-version", {
template: version_template,
data() {
return {
"framework_version": _dictionaries.framework_version,
"project_version": _dictionaries.project_version
}
}
})
1 change: 1 addition & 0 deletions src/fprime_gds/flask/static/addons/enabled.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ import "./image-display/addon.js"
import "./sequencer/addon.js"
import "./advanced-settings/addon.js"
import "./chart-display/addon.js"
import "./dictionary/addon.js"
4 changes: 3 additions & 1 deletion src/fprime_gds/flask/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,13 @@
<event-list v-show="currentTab == 'Events'"></event-list>
<uplink v-show="currentTab == 'Uplink'"></uplink>
<downlink v-show="currentTab == 'Downlink'"></downlink>
<logging v-show="currentTab == 'Logs'"></logging>
<chart-wrapper v-show="currentTab == 'Charts'"></chart-wrapper>
<dictionary v-show="currentTab == 'Dictionaries'"></dictionary>
<logging v-show="currentTab == 'Logs'"></logging>
<sequencer v-show="currentTab == 'Sequences'"></sequencer>
<dashboard v-show="currentTab == 'Dashboard'"></dashboard>
<advanced-settings v-show="currentTab == 'Advanced'"></advanced-settings>
<dictionary-version></dictionary-version>
</div>
<div v-if="!flags.loaded" class="container">
<div class="align-middle">
Expand Down
10 changes: 6 additions & 4 deletions src/fprime_gds/flask/static/js/datastore.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,12 @@ class DataStore {
startup() {
// Assign the dictionaries into the global object
Object.assign(_dictionaries, {
commands: _loader.endpoints["command-dict"].data,
events: _loader.endpoints["event-dict"].data,
channels: _loader.endpoints["channel-dict"].data,
commands_by_id: Object.fromEntries(Object.values(_loader.endpoints["command-dict"].data).map((value) => [value.id, value]))
commands: _loader.endpoints["command-dict"].data.dictionary,
events: _loader.endpoints["event-dict"].data.dictionary,
channels: _loader.endpoints["channel-dict"].data.dictionary,
commands_by_id: Object.fromEntries(Object.values(_loader.endpoints["command-dict"].data.dictionary).map((value) => [value.id, value])),
framework_version: _loader.endpoints["command-dict"].data.framework_version,
project_version: _loader.endpoints["command-dict"].data.project_version,
});
// Setup channels object in preparation for updates. Channel object need to be well formed, even if blank,
// because rendering of edit-views is still possible.
Expand Down
2 changes: 1 addition & 1 deletion src/fprime_gds/flask/static/js/vue-support/fptable.js
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ Vue.component("fp-table", {
if (this.itemsKey != null) {
return this.displayed;
} else if (this.items != null) {
return this.scroll(this.items);
return this.scroll(this.filter(this.items));
} else {
console.error("items and itemsKey is not defined")
}
Expand Down
7 changes: 4 additions & 3 deletions src/fprime_gds/flask/static/js/vue-support/tabetc.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import "./event.js"
import "./log.js"
import "./uplink.js"
import "./dashboard.js"
import {_datastore} from "../datastore.js";
import {_datastore, _dictionaries} from "../datastore.js";
import {_validator} from "../validate.js";

/**
Expand All @@ -41,9 +41,10 @@ Vue.component("tabbed-etc", {
["Channels", "Chn"],
["Uplink", "UpL"],
["Downlink", "DnL"],
["Logs", "Log"],
["Dictionaries", "Dct"],
["Charts", "Chr"],
["Sequences", "Seq"],
["Logs", "Log"],
["Sequences", "Seq"],
["Dashboard", "Dsh"],
["Advanced", "Adv"]
],
Expand Down
11 changes: 11 additions & 0 deletions src/fprime_gds/flask/static/js/vue-support/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@
*/
import {config} from "../config.js";

/**
* Make it sentence case where the first letter is capitalized.
* @param key: key to capitalize the first letter
* @return string
*/
export function sentenceCase(key) {
if (key.length > 0) {
return key[0].toUpperCase() + key.slice(1);
}
return key;
}
/**
* Preprocessing the matching tokens to make them easier to match match against. It takes the following steps:
* 1. Ensure all tokens are defined
Expand Down

0 comments on commit 7922fd9

Please sign in to comment.