Skip to content

Commit febdcf2

Browse files
authored
Merge pull request #195 from OpenC3/stale_indicator
Stale indicator
2 parents a856125 + b767d43 commit febdcf2

File tree

10 files changed

+150
-27
lines changed

10 files changed

+150
-27
lines changed

openc3-init/plugins/packages/openc3-demo/targets/INST/screens/adcs.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
SCREEN AUTO AUTO 1.0 FIXED
1+
SCREEN AUTO AUTO 1.0
22

33
VERTICAL
44
TITLE "<%= target_name %> Instrument ADCS Information" Courier 20 NORMAL true
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
SCREEN AUTO AUTO 0.5
2+
STALE_TIME 5 # Number of seconds to wait before marking data stale
23

34
VERTICAL
4-
55
TITLE "Status"
66

77
LABELVALUE SYSTEM META OPENC3_VERSION
88
LABELVALUE INST HEALTH_STATUS COLLECTS
99
LABELVALUE INST2 HEALTH_STATUS COLLECTS
10+
LABELVALUE EXAMPLE STATUS VALUE
11+
LABELVALUE EXAMPLE STATUS STRING
1012
END
11-
12-

openc3-init/plugins/packages/openc3-tool-common/src/components/widgets/VWidget.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export default {
4040
return {
4141
curValue: null,
4242
prevValue: null,
43-
grayLevel: 30,
43+
grayLevel: 80,
4444
grayRate: 5,
4545
valueId: null,
4646
colorBlind: false,

openc3-init/plugins/packages/openc3-tool-common/src/services/openc3-api.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,10 @@ export class OpenC3Api {
243243
return this.exec('get_all_telemetry_names', [target_name])
244244
}
245245

246-
async get_tlm_packet(target_name, packet_name, value_type) {
246+
async get_tlm_packet(target_name, packet_name, value_type, stale_time = 30) {
247247
const data = await this.exec('get_tlm_packet', [target_name, packet_name], {
248248
type: value_type,
249+
stale_time: stale_time,
249250
})
250251
// Make sure data isn't null or undefined. Note this is the only valid use of == or !=
251252
if (data != null) {
@@ -269,8 +270,10 @@ export class OpenC3Api {
269270
return this.exec('get_tlm_buffer', [target_name, packet_name])
270271
}
271272

272-
async get_tlm_values(items) {
273-
const data = await this.exec('get_tlm_values', [items])
273+
async get_tlm_values(items, stale_time = 30) {
274+
const data = await this.exec('get_tlm_values', [items], {
275+
stale_time: stale_time,
276+
})
274277
var len = data[0].length
275278
var converted = null
276279
for (var i = 0; i < len; i++) {

openc3-init/plugins/packages/openc3-tool-packetviewer/src/tools/PacketViewer/PacketViewer.vue

+20-1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,19 @@
9090
label="Refresh Interval (ms)"
9191
:value="refreshInterval"
9292
@change="refreshInterval = $event"
93+
data-test="refresh-interval"
94+
/>
95+
</div>
96+
<div class="pa-3">
97+
<v-text-field
98+
min="1"
99+
max="10000"
100+
step="1"
101+
type="number"
102+
label="Time at which to mark data Stale (s)"
103+
:value="staleLimit"
104+
@change="staleLimit = parseInt($event)"
105+
data-test="stale-limit"
93106
/>
94107
</div>
95108
</v-card-text>
@@ -195,6 +208,7 @@ export default {
195208
packetName: '',
196209
valueType: 'WITH_UNITS',
197210
refreshInterval: 1000,
211+
staleLimit: 30,
198212
rows: [],
199213
menuItems: [],
200214
api: null,
@@ -269,7 +283,12 @@ export default {
269283

270284
this.updater = setInterval(() => {
271285
this.api
272-
.get_tlm_packet(this.targetName, this.packetName, this.valueType)
286+
.get_tlm_packet(
287+
this.targetName,
288+
this.packetName,
289+
this.valueType,
290+
this.staleLimit
291+
)
273292
.then((data) => {
274293
// Make sure data isn't null or undefined. Note this is the only valid use of == or !=
275294
if (data != null) {

openc3-init/plugins/packages/openc3-tool-tlmviewer/src/tools/TlmViewer/Openc3Screen.vue

+11-8
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ export default {
205205
dynamicWidgets: [],
206206
width: null,
207207
height: null,
208-
fixed: null,
208+
staleTime: 30,
209209
globalSettings: [],
210210
globalSubsettings: [],
211211
substitute: false,
@@ -305,23 +305,26 @@ export default {
305305
this.configParser.verify_num_parameters(
306306
3,
307307
4,
308-
`${keyword} <Width or AUTO> <Height or AUTO> <Polling Period> <FIXED>`
308+
`${keyword} <Width or AUTO> <Height or AUTO> <Polling Period>`
309309
)
310310
this.width = parseInt(parameters[0])
311311
this.height = parseInt(parameters[1])
312312
this.pollingPeriod = parseFloat(parameters[2])
313-
if (parameters.length === 4) {
314-
this.fixed = true
315-
} else {
316-
this.fixed = false
317-
}
318313
break
319314
case 'END':
320315
this.configParser.verify_num_parameters(0, 0, `${keyword}`)
321316
this.layoutStack.pop()
322317
this.currentLayout =
323318
this.layoutStack[this.layoutStack.length - 1]
324319
break
320+
case 'STALE_TIME':
321+
this.configParser.verify_num_parameters(
322+
1,
323+
1,
324+
`${keyword} <Time (s)>`
325+
)
326+
this.staleTime = parseInt(parameters[0])
327+
break
325328
case 'SETTING':
326329
case 'SUBSETTING':
327330
const widget =
@@ -384,7 +387,7 @@ export default {
384387
this.errors.length === 0
385388
) {
386389
this.api
387-
.get_tlm_values(this.$store.state.tlmViewerItems)
390+
.get_tlm_values(this.$store.state.tlmViewerItems, this.staleTime)
388391
.then((data) => {
389392
this.$store.commit('tlmViewerUpdateValues', data)
390393
})

openc3/data/config/screen.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ END:
2121
description: All layout widgets must be closed to properly identify where they
2222
stop. For example, a VERTICALBOX keyword must be matched with an END keyword
2323
to indicate where the VERTICALBOX ends.
24+
STALE_TIME:
25+
summary: Values are marked stale if the packet time is more than Stale Time seconds in the past
26+
parameters:
27+
- name: value
28+
required: true
29+
description: Items from packets with RECEIVED_TIMESECONDS greater than this value in the past will be marked stale.
30+
The default is 30s. Recommend a minimum of 2s to avoid false positives due to race conditions.
31+
values: \d+
2432
GLOBAL_SETTING:
2533
summary: Applies a widget setting to all widgets of a certain type
2634
parameters:

openc3/lib/openc3/api/tlm_api.rb

+9-5
Original file line numberDiff line numberDiff line change
@@ -202,16 +202,19 @@ def get_tlm_buffer(target_name, packet_name, scope: $openc3_scope, token: $openc
202202
#
203203
# @param target_name [String] Name of the target
204204
# @param packet_name [String] Name of the packet
205+
# @param stale_time [Integer] Time in seconds from Time.now that packet will be marked stale
205206
# @param type [Symbol] Types returned, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
206-
# @return (see OpenC3::Packet#read_all_with_limits_states)
207-
def get_tlm_packet(target_name, packet_name, type: :CONVERTED, scope: $openc3_scope, token: $openc3_token)
207+
# @return [Array<String, Object, Symbol|nil>] Returns an Array consisting
208+
# of [item name, item value, item limits state] where the item limits
209+
# state can be one of {OpenC3::Limits::LIMITS_STATES}
210+
def get_tlm_packet(target_name, packet_name, stale_time: 30, type: :CONVERTED, scope: $openc3_scope, token: $openc3_token)
208211
authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
209212
packet = TargetModel.packet(target_name, packet_name, scope: scope)
210213
t = _validate_tlm_type(type)
211214
raise ArgumentError, "Unknown type '#{type}' for #{target_name} #{packet_name}" if t.nil?
212215
items = packet['items'].map { | item | item['name'] }
213216
cvt_items = items.map { | item | "#{target_name}__#{packet_name}__#{item}__#{type}" }
214-
current_values = CvtModel.get_tlm_values(cvt_items, scope: scope)
217+
current_values = CvtModel.get_tlm_values(cvt_items, stale_time: stale_time, scope: scope)
215218
items.zip(current_values).map { | item , values | [item, values[0], values[1]]}
216219
end
217220

@@ -221,10 +224,11 @@ def get_tlm_packet(target_name, packet_name, type: :CONVERTED, scope: $openc3_sc
221224
#
222225
# @since 5.0.0
223226
# @param items [Array<String>] Array of items consisting of 'tgt__pkt__item__type'
227+
# @param stale_time [Integer] Time in seconds from Time.now that data will be marked stale
224228
# @return [Array<Object, Symbol>]
225229
# Array consisting of the item value and limits state
226230
# given as symbols such as :RED, :YELLOW, :STALE
227-
def get_tlm_values(items, scope: $openc3_scope, token: $openc3_token)
231+
def get_tlm_values(items, stale_time: 30, scope: $openc3_scope, token: $openc3_token)
228232
if !items.is_a?(Array) || !items[0].is_a?(String)
229233
raise ArgumentError, "items must be array of strings: ['TGT__PKT__ITEM__TYPE', ...]"
230234
end
@@ -237,7 +241,7 @@ def get_tlm_values(items, scope: $openc3_scope, token: $openc3_token)
237241
end
238242
authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
239243
end
240-
CvtModel.get_tlm_values(items, scope: scope)
244+
CvtModel.get_tlm_values(items, stale_time: stale_time, scope: scope)
241245
end
242246

243247
# Returns an array of all the telemetry packet hashes

openc3/lib/openc3/models/cvt_model.rb

+11-5
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,10 @@ def self.get_item(target_name, packet_name, item_name, type:, scope:)
8787
# Return all item values and limit state from the CVT
8888
#
8989
# @param items [Array<String>] Items to return. Must be formatted as TGT__PKT__ITEM__TYPE
90+
# @param stale_time [Integer] Time in seconds from Time.now that value will be marked stale
9091
# @return [Array] Array of values
91-
def self.get_tlm_values(items, scope: $openc3_scope)
92+
def self.get_tlm_values(items, stale_time: 30, scope: $openc3_scope)
93+
now = Time.now.to_f
9294
results = []
9395
lookups = []
9496
packet_lookup = {}
@@ -109,10 +111,14 @@ def self.get_tlm_values(items, scope: $openc3_scope)
109111
end
110112
# If we were able to find a value, try to get the limits state
111113
if item_result[0]
112-
# The last key is simply the name (RAW) so we can append __L
113-
# If there is no limits then it returns nil which is acceptable
114-
item_result[1] = hash["#{packet_values[-1]}__L"]
115-
item_result[1] = item_result[1].intern if item_result[1] # Convert to symbol
114+
if now - hash['RECEIVED_TIMESECONDS'] > stale_time
115+
item_result[1] = :STALE
116+
else
117+
# The last key is simply the name (RAW) so we can append __L
118+
# If there is no limits then it returns nil which is acceptable
119+
item_result[1] = hash["#{packet_values[-1]}__L"]
120+
item_result[1] = item_result[1].intern if item_result[1] # Convert to symbol
121+
end
116122
else
117123
raise "Item '#{target_name} #{packet_name} #{packet_values[-1]}' does not exist" unless hash.key?(packet_values[-1])
118124
item_result[1] = nil

openc3/spec/api/tlm_api_spec.rb

+80
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,46 @@ def test_tlm_unknown(method)
545545
expect(vals[14][1]).to eql "-100.000 C"
546546
expect(vals[14][2]).to eql :RED_LOW
547547
end
548+
549+
it "marks data as stale" do
550+
packet = System.telemetry.packet('INST', 'HEALTH_STATUS')
551+
packet.received_time = Time.now.sys - 100
552+
packet.stored = false
553+
packet.check_limits
554+
TelemetryDecomTopic.write_packet(packet, scope: "DEFAULT")
555+
sleep(0.01) # Allow the write to happen
556+
557+
# Use the default stale_time of 30s
558+
vals = @api.get_tlm_packet("INST", "HEALTH_STATUS")
559+
# Spot check a few
560+
expect(vals[11][0]).to eql "TEMP1"
561+
expect(vals[11][1]).to eql(-100.0)
562+
expect(vals[11][2]).to eql :STALE
563+
expect(vals[12][0]).to eql "TEMP2"
564+
expect(vals[12][1]).to eql(-100.0)
565+
expect(vals[12][2]).to eql :STALE
566+
expect(vals[13][0]).to eql "TEMP3"
567+
expect(vals[13][1]).to eql(-100.0)
568+
expect(vals[13][2]).to eql :STALE
569+
expect(vals[14][0]).to eql "TEMP4"
570+
expect(vals[14][1]).to eql(-100.0)
571+
expect(vals[14][2]).to eql :STALE
572+
573+
vals = @api.get_tlm_packet("INST", "HEALTH_STATUS", stale_time: 101)
574+
# Verify it goes back to the limits setting and not STALE
575+
expect(vals[11][0]).to eql "TEMP1"
576+
expect(vals[11][1]).to eql(-100.0)
577+
expect(vals[11][2]).to eql :RED_LOW
578+
expect(vals[12][0]).to eql "TEMP2"
579+
expect(vals[12][1]).to eql(-100.0)
580+
expect(vals[12][2]).to eql :RED_LOW
581+
expect(vals[13][0]).to eql "TEMP3"
582+
expect(vals[13][1]).to eql(-100.0)
583+
expect(vals[13][2]).to eql :RED_LOW
584+
expect(vals[14][0]).to eql "TEMP4"
585+
expect(vals[14][1]).to eql(-100.0)
586+
expect(vals[14][2]).to eql :RED_LOW
587+
end
548588
end
549589

550590
describe "get_tlm_values" do
@@ -642,6 +682,46 @@ def test_tlm_unknown(method)
642682
expect(vals[2][1]).to be_nil
643683
expect(vals[3][1]).to be_nil
644684
end
685+
686+
it "marks data as stale" do
687+
packet = System.telemetry.packet('INST', 'HEALTH_STATUS')
688+
packet.received_time = Time.now.sys - 100
689+
packet.stored = false
690+
packet.check_limits
691+
TelemetryDecomTopic.write_packet(packet, scope: "DEFAULT")
692+
sleep(0.01) # Allow the write to happen
693+
694+
items = []
695+
items << 'INST__HEALTH_STATUS__TEMP1__CONVERTED'
696+
items << 'INST__LATEST__TEMP2__CONVERTED'
697+
items << 'INST__HEALTH_STATUS__TEMP3__CONVERTED'
698+
items << 'INST__LATEST__TEMP4__CONVERTED'
699+
items << 'INST__HEALTH_STATUS__DURATION__CONVERTED'
700+
# Use the default stale_time of 30s
701+
vals = @api.get_tlm_values(items)
702+
expect(vals[0][0]).to eql(-100.0)
703+
expect(vals[1][0]).to eql(-100.0)
704+
expect(vals[2][0]).to eql(-100.0)
705+
expect(vals[3][0]).to eql(-100.0)
706+
expect(vals[4][0]).to eql(0.0)
707+
expect(vals[0][1]).to eql :STALE
708+
expect(vals[1][1]).to eql :STALE
709+
expect(vals[2][1]).to eql :STALE
710+
expect(vals[3][1]).to eql :STALE
711+
expect(vals[4][1]).to eql :STALE
712+
713+
vals = @api.get_tlm_values(items, stale_time: 101)
714+
expect(vals[0][0]).to eql(-100.0)
715+
expect(vals[1][0]).to eql(-100.0)
716+
expect(vals[2][0]).to eql(-100.0)
717+
expect(vals[3][0]).to eql(-100.0)
718+
expect(vals[4][0]).to eql(0.0)
719+
expect(vals[0][1]).to eql :RED_LOW
720+
expect(vals[1][1]).to eql :RED_LOW
721+
expect(vals[2][1]).to eql :RED_LOW
722+
expect(vals[3][1]).to eql :RED_LOW
723+
expect(vals[4][1]).to be_nil
724+
end
645725
end
646726

647727
describe "subscribe_packets, get_packets" do

0 commit comments

Comments
 (0)