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

Issue #105_kkallas - Rework of Backend/Frontend telemetry processing #170

Merged
merged 34 commits into from
May 26, 2020
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ebd3d67
Issue #105 - Send packet as JSON to frontend
aywaldron Mar 22, 2019
9f48361
Issue #105 - Update highcharts plot function for JSON formatted packet
aywaldron Mar 22, 2019
1f930d4
Issue #105 - Store last packet on backend; send only changes to front…
aywaldron Mar 22, 2019
c5887fb
Add delta to current packet state before emitting event (#105)
aywaldron Sep 18, 2019
df28e93
Send new dntoeu values from backend to frontend (#105)
aywaldron Oct 2, 2019
8841588
Request full packet from backend on page refresh
aywaldron Oct 3, 2019
96d223d
Update backend endpoint for returning latest packet states (#105)
aywaldron Oct 8, 2019
7740371
Get latest packet state from backend on frontend initialization (#105)
aywaldron Oct 8, 2019
ab010e2
Encode datetimes as ISO formatted strings for sending to frontend (#105)
aywaldron Oct 9, 2019
62b97e4
Implement counter method for checking packet delta order on frontend …
aywaldron Oct 9, 2019
58d6801
Add max 32 bit int as max packet counter (#105)
aywaldron Oct 14, 2019
e277c42
Deep copy packet state for inserting into ait.packets so fields updat…
aywaldron Oct 18, 2019
52a614f
Make counters session dependent (#105)
aywaldron Oct 18, 2019
7fddbf8
Modularize getting packet definition and name (#105)
aywaldron Oct 18, 2019
3b7ce2d
Fix bug in adding delta to packet state on frontend (#105)
aywaldron Oct 18, 2019
9175608
Store deltas in queue per session (#105)
aywaldron Oct 18, 2019
05e4171
Add tracking of dntoeus on backend (#105)
aywaldron Oct 22, 2019
2bde078
Add tracking of dntoeu states on frontend (#105)
aywaldron Oct 22, 2019
11855c9
Remove packet class from frontend (#105)
aywaldron Oct 22, 2019
a074583
Use new function for getting pkt defn in openmct endpoint (#105)
aywaldron Oct 22, 2019
1e7be37
Remove unecessary changes (debug prints, etc.) (#105)
aywaldron Dec 19, 2019
c0e0515
Add packet defn data structure to avoid loop on every call (#105)
aywaldron Dec 19, 2019
aa28077
Remove initial packet conversion to JSON to save time (#105)
aywaldron Dec 19, 2019
a437eec
Update dict.iteritems to dict.items (#169)
May 14, 2020
7aa9c54
Add dntoeu plot option
May 14, 2020
21c8c91
Add getPacket utility method
May 14, 2020
c2007a1
Remove spaces
May 14, 2020
610d47a
Use getPacket utility method
May 14, 2020
7f7b35c
Update dntoeu plot option (#105)
Futabay May 19, 2020
b89e233
Add digitsAfterDecimal option to legend (#105)
Futabay May 19, 2020
658ccea
Update getPacket utility method (#105)
Futabay May 19, 2020
4a5bf0e
Apply getPacket utility method (#105)
Futabay May 19, 2020
dd52c2a
Check if field exists in dntoeu (#105)
Futabay May 19, 2020
d8c568f
Update uttlity method (#105)
Futabay May 26, 2020
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
137 changes: 126 additions & 11 deletions ait/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ def __init__ (self, store=None, maxlen=100):
self.events = api.GeventDeque(maxlen=maxlen)
self.messages = api.GeventDeque(maxlen=maxlen)
self.telemetry = api.GeventDeque(maxlen=maxlen)
self.deltas = api.GeventDeque(maxlen=maxlen)
self.tlm_counters = { }
self._maxlen = maxlen
self._store = store
self._numConnections = 0
Expand All @@ -68,6 +70,38 @@ def id (self):
"""A unique identifier for this Session."""
return str( id(self) )

def updateCounter(self, pkt_name):
if pkt_name not in self.tlm_counters:
self.tlm_counters[pkt_name] = 0
else:
count = self.tlm_counters[pkt_name]
count = count + 1 if count < 2**31 - 1 else 0
self.tlm_counters[pkt_name] = count

return self.tlm_counters[pkt_name]


packet_defns = {}
def getPacketDefn(uid):
"""
Returns packet defn from tlm dict matching uid.
Logs warning and returns None if no defn matching uid is found.
"""
global packet_defns

if uid in packet_defns:
return packet_defns[uid]

else:
tlmdict = ait.core.tlm.getDefaultDict()
for k, v in tlmdict.items():
if v.uid == uid:
packet_defns[uid] = v
return v

log.warn('No packet defn matching UID {}'.format(uid))
return None


class SessionStore (dict):
"""SessionStore
Expand All @@ -84,7 +118,17 @@ def addTelemetry (self, uid, packet):
"""Adds a telemetry packet to all Sessions in the store."""
item = (uid, packet)
SessionStore.History.telemetry.append(item)

pkt_defn = getPacketDefn(uid)
pkt_name = pkt_defn.name
delta, dntoeus = get_packet_delta(pkt_defn, packet)
delta = replace_datetimes(delta)

for session in self.values():
counter = session.updateCounter(pkt_name)
item = (pkt_name, delta, dntoeus, counter)
session.deltas.append(item)
item = (uid, packet, session.tlm_counters[pkt_name])
session.telemetry.append(item)

def addMessage (self, msg):
Expand Down Expand Up @@ -131,7 +175,7 @@ class Playback(object):
be sent to the frontend during this.
playback.enabled: True if historical data playback is enabled. This will be False
if a database connection cannot be made or if data playback is disabled for
some other reason.
some other reason.
"""

def __init__(self):
Expand Down Expand Up @@ -272,7 +316,7 @@ def process(self, input_data, topic=None):
self.process_log_msg(input_data)
processed = True

if not processed:
if not processed:
raise ValueError('Topic of received message not recognized as telem or log stream.')

def process_telem_msg(self, msg):
Expand Down Expand Up @@ -640,6 +684,7 @@ def handle():
__setResponseToEventStream()
yield 'data: %s\n\n' % json.dumps(msg)


@App.route('/tlm/realtime/openmct')
def handle():
"""Return telemetry packets in realtime to client"""
Expand All @@ -655,13 +700,7 @@ def handle():
while not wsock.closed:
try:
uid, data = session.telemetry.popleft(timeout=30)
pkt_defn = None
for k, v in tlmdict.iteritems():
if v.uid == uid:
pkt_defn = v
break
else:
continue
pkt_defn = getPacketDefn(uid)

wsock.send(json.dumps({
'packet': pkt_defn.name,
Expand All @@ -684,6 +723,63 @@ def handle():
pass


packet_states = { }
def get_packet_delta(pkt_defn, packet):
"""
Keeps track of last packets recieved of all types recieved
and returns only fields that have changed since last packet.

Params:
pkt_defn: Packet definition
packet: Binary packet data
Returns:
delta: JSON of packet fields that have changed
"""
ait_pkt = ait.core.tlm.Packet(pkt_defn, data=packet)

# first packet of this type
if pkt_defn.name not in packet_states:
packet_states[pkt_defn.name] = {}

# get raw fields
raw_fields = {f.name: getattr(ait_pkt.raw, f.name) for f in pkt_defn.fields}
packet_states[pkt_defn.name]['raw'] = raw_fields
delta = raw_fields

# get converted fields
packet_states[pkt_defn.name]['dntoeu'] = {}
dntoeus = {f.name: getattr(ait_pkt, f.name) for f in pkt_defn.fields if f.dntoeu is not None}

# previous packets of this type received
else:
delta, dntoeus = {}, {}

for field in pkt_defn.fields:
new_value = getattr(ait_pkt.raw, field.name)
last_value = packet_states[pkt_defn.name]['raw'][field.name]

if new_value != last_value:
delta[field.name] = new_value
packet_states[pkt_defn.name]['raw'][field.name] = new_value

if field.dntoeu is not None:
dntoeu_val = getattr(ait_pkt, field.name)
dntoeus[field.name] = dntoeu_val
packet_states[pkt_defn.name]['dntoeu'][field.name] = dntoeu_val

return delta, dntoeus


def replace_datetimes(delta):
"""Replace datetime objects with ISO formatted
strings for JSON serialization"""
for key, val in delta.items():
if type(val) is datetime:
delta[key] = val.isoformat()

return delta


@App.route('/tlm/realtime')
def handle():
"""Return telemetry packets in realtime to client"""
Expand All @@ -698,8 +794,15 @@ def handle():
try:
while not wsock.closed:
try:
uid, data = session.telemetry.popleft(timeout=30)
wsock.send(pad + struct.pack('>I', uid) + data)
name, delta, dntoeus, counter = session.deltas.popleft(timeout=30)

wsock.send(json.dumps({
'packet': name,
'data': delta,
'dntoeus': dntoeus,
'counter': counter
}))

except IndexError:
# If no telemetry has been received by the GUI
# server after timeout seconds, "probe" the client
Expand All @@ -716,6 +819,18 @@ def handle():
pass


@App.route('/tlm/latest', method='GET')
def handle():
"""Return latest telemetry packet to client"""
for pkt_type, state in packet_states.items():
packet_states[pkt_type]['raw'] = replace_datetimes(state['raw'])

with Sessions.current() as session:
counters = session.tlm_counters
return json.dumps({'states': packet_states,
'counters': counters})


@App.route('/tlm/query', method='POST')
def handle():
""""""
Expand Down
14 changes: 7 additions & 7 deletions ait/gui/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ <h1>Welcome to the AMMOS Instrument Toolkit Telemetry Monitoring Interface!</h1>
The AIT UI is created from a number of <strong>components</strong>. These components are written in JavaScript and provided by the project to cover basic functionality needed in the user interface. Users can customize their interface by making minimal HTML or configuration file changes. If a user needs more control they can create their own components and include them in the interface.
</p>
<p>
The majority of the content that you're seeing is displayed in <strong>ait-tabset</strong> components. The tabset component helps keep our display split into logical blocks and displays additional status information to the user.
The majority of the content that you're seeing is displayed in <strong>ait-tabset</strong> components. The tabset component helps keep our display split into logical blocks and displays additional status information to the user.

<pre>
&lt;ait-tabset&gt;
Expand Down Expand Up @@ -202,10 +202,10 @@ <h4 class="telem-group-title">1553 HS Voltages</h4>
"height": 300
}
</ait-plot-config>
<ait-plot-series packet="1553_HS_Packet" name="Voltage_A"></ait-plot-series>
<ait-plot-series packet="1553_HS_Packet" name="Voltage_B"></ait-plot-series>
<ait-plot-series packet="1553_HS_Packet" name="Voltage_C"></ait-plot-series>
<ait-plot-series packet="1553_HS_Packet" name="Voltage_D"></ait-plot-series>
<ait-plot-series packet="1553_HS_Packet" name="Voltage_A" raw=true></ait-plot-series>
<ait-plot-series packet="1553_HS_Packet" name="Voltage_B" raw=true></ait-plot-series>
<ait-plot-series packet="1553_HS_Packet" name="Voltage_C" raw=true></ait-plot-series>
<ait-plot-series packet="1553_HS_Packet" name="Voltage_D" raw=true></ait-plot-series>
</ait-plot>
</div>
<div class="col-sm-6">
Expand All @@ -232,7 +232,7 @@ <h4 class="telem-group-title">1553 HS Currents</h4>
</ait-tab>
</ait-tabset>
</div>


<div class='ctrlcontainer'>
<ait-tabset class='nav-tabs'>
Expand All @@ -259,7 +259,7 @@ <h4 class="telem-group-title">1553 HS Currents</h4>
<ait-command-history></ait-command-history>
</ait-tab>
<ait-tab title='Sequences'>
<ait-sequence></ait-sequence>
<ait-sequence></ait-sequence>
</ait-tab>
<ait-tab title="Script Control">
<div class="row">
Expand Down
10 changes: 8 additions & 2 deletions ait/gui/static/js/ait/gui/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@ const Field =
* retrieving the packet value.
*/
getValue (packet, raw=false) {
return packet && packet.__get__(this._fname, raw)
if ( !raw ) {
if ( packet && this._fname in packet['dntoeu']) {
return packet && packet['dntoeu'][this._fname]
}
}

return packet && packet['raw'][this._fname]
},


Expand Down Expand Up @@ -285,7 +291,7 @@ const Field =
},

onbeforeupdate (vnode, old) {
return this.hasChanged()
return this.hasChanged()
},

view (vnode) {
Expand Down
Loading