Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix EVR page freeze issue #6

Merged
merged 15 commits into from
Jul 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/spelling/excludes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ ignore$
\.xls$
\.xlsx$
\.xsd$
\.ico$
1 change: 1 addition & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ fbuild
fd
FEEDNAME
FFCC
fff
fieldset
figcaption
filemode
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
docs/doxy/**
*.log

Expand Down
8 changes: 8 additions & 0 deletions src/fprime_gds/flask/static/css/fpstyle.css
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,11 @@ table td:last-child {
background: black;
border-radius: 5px;
}

/**
* Style related to Events page
**/
.fp-auto-increment {
background-color: #28a745 !important;
color: #fff !important;
}
Binary file added src/fprime_gds/flask/static/favicon.ico
Binary file not shown.
31 changes: 27 additions & 4 deletions src/fprime_gds/flask/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
<template id="dashboard-template">
<div class="fp-flex-repeater">
<div class="fp-flex-header">
<div>
<div class="mt-2">
<label for="config-file">Choose a Layout Configuration File:</label>
<input type="file"
name="config-file"
Expand Down Expand Up @@ -237,6 +237,24 @@ <h5 class="mb-1">{{calculateCommandTime + " " + command.template.mnemonic }}</h5
<div class="fp-flex-header">
<h2>Events</h2>
</div>
<div class="row justify-content-end no-gutters">
<div class="col-sm-4">
<div class="input-group mb-3">
<div class="input-group-prepend">
<button class="btn btn-secondary" type="button" v-on:click="offsetToFirst">First</button>
<button class="btn btn-secondary" type="button" v-on:click="offsetToPrev" v-bind:class="{ 'disabled': !scrollStatus }">&lt;</button>
<span class="input-group-text" v-bind:class="{ 'fp-auto-increment': isAutoUpdate }">
{{ eventsStartOffset }} - {{ eventsEndOffset }}
</span>
</div>
<input type="text" class="form-control" v-model="totalEventsSize" disabled>
<div class="input-group-append">
<button class="btn btn-secondary" type="button" v-on:click="offsetToNext" v-bind:class="{ 'disabled': !scrollStatus }">&gt;</button>
<button class="btn btn-secondary" type="button" v-on:click="offsetToLast">Last</button>
</div>
</div>
</div>
</div>
<fp-table :header-columns="['Event Time', 'Event Id', 'Event Name', 'Event Severity', 'Event Description']"
:items="componentEvents"
:item-to-columns="columnify"
Expand All @@ -248,6 +266,11 @@ <h2>Events</h2>
:filter-text="filterText"
:initial-views="itemsShown"
:compact="compact"
:totalEventsSize="updateTotalEventsSize"
:eventsStartOffset="eventsStartOffset"
:eventsEndOffset="eventsEndOffset"
:isAutoUpdate="isAutoUpdate"
:scrollStatus="scrollStatus"
></fp-table>
</div>
</template>
Expand Down Expand Up @@ -397,18 +420,18 @@ <h2>File Downlink</h2>
5. File downlink progress and download (uses file-row template)
-->
<template id="fp-table-template">
<div class="fp-flex-repeater">
<div class="fp-flex-repeater mt-2">
<div class="row fp-flex-header no-gutters justify-content-end" v-if="!compact">
<div class="mx-1">Filters:</div>
<div class="ml-1 col-4">
<div class="ml-1 mb-2 col-4">
<v-select id="fp-table-filter" :clearable="true" :searchable="true" :taggable="true"
:no-drop="true" :options="[]" :multiple="true"
v-model="matching">
</v-select>
</div>
</div>
<div class="fp-scroll-container">
<div class="fp-scrollable">
<div class="fp-scrollable" id="fp-scrollable-id">
<table class="sortable table table-bordered table-hover">
<thead>
<tr v-if="!compact">
Expand Down
212 changes: 205 additions & 7 deletions src/fprime_gds/flask/static/js/vue-support/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,16 @@ Vue.component("event-list", {
return {
// NOTE: Events/command lists shared across all component instances
"events": _datastore.events,
"eventsOffset": 0,
"totalEventsSize": _datastore.events.length,
"eventsStartOffset": 0,
"eventsEndOffset": 100,
"eventOffsetSize": 100,
"scrollOffsetSize": 5,
"isAutoUpdate": false,
"scrollableElm": null,
"scrollStatus": false,
"currTime": 0,
"prevTime": 0,
"commands": _datastore.commands
};
},
Expand Down Expand Up @@ -117,11 +126,13 @@ Vue.component("event-list", {
return "evt-" + item.id + "-" + item.time.seconds + "-"+ item.time.microseconds;
},
/**
* A function to clear this events pane to remove events that have already been seen. Note: this action is
* irrecoverable.
* A function to clear this events pane by moving the offset to the end
* of the list. User call see the previous events again if scrolling back
*/
clearEvents() {
this.eventsOffset = this.events.length;
this.eventsStartOffset = this.events.length;
this.eventsEndOffset = this.events.length + this.eventOffsetSize;
this.isAutoUpdate = false;
},
/**
* Returns if the given item should be hidden in the data table; by
Expand All @@ -133,6 +144,168 @@ Vue.component("event-list", {
*/
isItemHidden(item) {
return listExistsAndItemNameNotInList(this.itemsShown, item);
},
/**
* Check if the scroll bar is at the bottom or at the top of the
* scrollable div. If at the bottom of the page load the next group
* of events into table. If at the top load the previous group.
*/
onScroll(e) {
let elmH = this.scrollableElm.scrollHeight;
let elmT = Math.abs(this.scrollableElm.scrollTop);
let elmC = this.scrollableElm.clientHeight;
let isAtBottom = ((elmH - elmT) === elmC) && (elmT !== 0);

if (!this.isScrollable()) {
// Disabling auto update user scrolls
if (this.hasUserScrolled(e)) {
this.isAutoUpdate = false;
}
return;
}

// Turn off auto scrolling
this.isAutoUpdate = false;

if (isAtBottom) {
// Scrollbar reached to the bottom
this.updateNextOffsetRange(this.scrollOffsetSize);
} else if (this.scrollableElm.scrollTop === 0) {
// Scrollbar reached to the top
this.updatePrevOffsetRange(this.scrollOffsetSize);
}
},
/**
* Jump to the top of the event list.
*/
offsetToFirst() {
this.scrollableElm.scrollTop = 0;
this.isAutoUpdate = false;
if (!this.isScrollable()) {
return;
}
this.eventsStartOffset = 0;
this.eventsEndOffset = this.eventOffsetSize;
},
/**
* Jump to the bottom of the event list.
*/
offsetToLast() {
this.scrollableElm.scrollTop = this.scrollableElm.scrollHeight - this.scrollableElm.clientHeight;
this.isAutoUpdate = true;
if (!this.isScrollable()) {
return;
}
this.eventsStartOffset = this.events.length - this.eventOffsetSize;
this.eventsEndOffset = this.events.length;
},
/**
* Load previous group of events
*/
offsetToPrev() {
if (!this.isScrollable()) {
return;
}
this.isAutoUpdate = false;
this.updatePrevOffsetRange(this.scrollOffsetSize);
},
/**
* Load next group of events
*/
offsetToNext() {
if (!this.isScrollable()) {
return;
}
this.isAutoUpdate = false;
this.updateNextOffsetRange(this.scrollOffsetSize);
},
/**
* If auto update is enabled load the new events and remove old events
* in the given range.
*/
updateAutoOffsetRange() {
if ((this.isAutoUpdate) &&
(this.eventsEndOffset < this.events.length) &&
(this.events.length - this.eventOffsetSize) > 0) {
this.eventsStartOffset = this.events.length - this.eventOffsetSize;
this.eventsEndOffset = this.events.length;
}
},
/**
* Utility function to keep the scroll bar at the bottom when auto scroll
* is enabled.
*/
updateScrollPos() {
if (this.isAutoUpdate) {
this.scrollableElm.scrollTop = this.getScrollBottomLimit();
}
},
/**
* Utility function to load previous group of events
* @param {number} offset: specifies how much to move backward in the list
*/
updatePrevOffsetRange(offset) {
if ((this.eventsStartOffset - offset) > 0) {
this.eventsStartOffset -= offset;
this.eventsEndOffset -= offset;
// Keep scrollbar down if there are more items to load
this.scrollableElm.scrollTop = 20;
// Turn off auto scrolling
this.isAutoUpdate = false;
} else {
// Will not subtract more since we are at the start of the list
this.eventsStartOffset = 0;
this.eventsEndOffset = this.eventOffsetSize;
// Move scrollbar to the top
this.scrollableElm.scrollTop = 0;
// Turn off auto scrolling
this.isAutoUpdate = false;
}
},
/**
* Utility function to load next group of events
* @param {number} offset: specifies how much to move forward in the list
*/
updateNextOffsetRange(offset) {
if ((this.eventsEndOffset + offset) >= this.events.length) {
// Will not add more since we are at the end of the list
this.eventsEndOffset = this.events.length;
this.eventsStartOffset = this.eventsEndOffset - this.eventOffsetSize;
this.scrollableElm.scrollTop = this.getScrollBottomLimit();
// Turn on auto scrolling since we are at the end of the list
this.isAutoUpdate = true;
} else if ((this.eventsEndOffset+offset) < this.events.length) {
this.eventsStartOffset += offset;
this.eventsEndOffset += offset;
let scrollbarTop = this.getScrollBottomLimit() - 20;
this.scrollableElm.scrollTop = scrollbarTop > 0 ? scrollbarTop : 0;
// Turn off auto scrolling
this.isAutoUpdate = false;
}
},
/**
* Utility function to check if enough events to start scrolling
*/
isScrollable() {
return (this.events.length - this.eventOffsetSize > 0);
},
/**
* Utility function to check for scrollbar bottom limit
*/
getScrollBottomLimit() {
return this.scrollableElm.scrollHeight - this.scrollableElm.clientHeight;
},
/**
* Check the timestamp of scroll event and if less than a certain
* millisecond interval it is a user triggered scroll
*/
hasUserScrolled(e) {
if (e === undefined) {
return false;
}
this.prevTime = this.currTime;
this.currTime = e.timeStamp;
return (this.currTime - this.prevTime) < 800;
}
},
computed: {
Expand All @@ -143,7 +316,32 @@ Vue.component("event-list", {
* @return {Array} The list of event items this instance can show
*/
componentEvents() {
return this.events.slice(this.eventsOffset);
}
}
this.updateAutoOffsetRange();
this.updateScrollPos();
this.scrollStatus = this.isScrollable();
return this.events.slice(this.eventsStartOffset, this.eventsEndOffset);
},
/**
* Update the total number of events in the list
*/
updateTotalEventsSize() {
this.totalEventsSize = this.events.length;
},
},
/**
* Add scroll event listener during mounting of element
*/
mounted: function() {
this.$nextTick(function(e) {
this.scrollableElm = this.$el.querySelector("#fp-scrollable-id");
this.scrollableElm.addEventListener('scroll', this.onScroll, true);
this.onScroll(e); // needed for initial loading on page
});
},
/**
* Remove scroll event listener
*/
beforeDestroy: function() {
this.scrollableElm.removeEventListener('scroll', this.onScroll);
},
});