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

Allow array index in VALUE widget #1332

Merged
merged 3 commits into from
Jun 17, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ SCREEN AUTO AUTO 1.0
VERTICAL
TITLE "<%= target_name %> Instrument Array Data"
ARRAY <%= target_name %> HEALTH_STATUS ARY
LABELVALUE <%= target_name %> HEALTH_STATUS ARY[0] RAW
LABELVALUE <%= target_name %> HEALTH_STATUS ARY[1] WITH_UNITS
ARRAY <%= target_name %> HEALTH_STATUS ARY 300 65 nil 8 FORMATTED
ARRAY <%= target_name %> HEALTH_STATUS ARY2 300 65 nil 5 WITH_UNITS
TEXTBOX <%= target_name %> HEALTH_STATUS ARY 200 65
TEXTBOX <%= target_name %> HEALTH_STATUS ARY2 300 200
TEXTBOX <%= target_name %> HEALTH_STATUS ARY2 300 65
ARRAY <%= target_name %> HEALTH_STATUS GROUND1STATUS 300 65 nil 8 FORMATTED
ARRAY <%= target_name %> HEALTH_STATUS GROUND2STATUS 300 65 nil 5 WITH_UNITS
END
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ SCREEN AUTO AUTO 1.0
VERTICAL
TITLE "<%= target_name %> Instrument Array Data"
ARRAY <%= target_name %> HEALTH_STATUS ARY
LABELVALUE <%= target_name %> HEALTH_STATUS ARY[0] RAW
LABELVALUE <%= target_name %> HEALTH_STATUS ARY[1] WITH_UNITS
ARRAY <%= target_name %> HEALTH_STATUS ARY 300 65 nil 8 FORMATTED
ARRAY <%= target_name %> HEALTH_STATUS ARY2 300 65 nil 5 WITH_UNITS
TEXTBOX <%= target_name %> HEALTH_STATUS ARY 200 65
TEXTBOX <%= target_name %> HEALTH_STATUS ARY2 300 200
TEXTBOX <%= target_name %> HEALTH_STATUS ARY2 300 65
ARRAY <%= target_name %> HEALTH_STATUS GROUND1STATUS 300 65 nil 8 FORMATTED
ARRAY <%= target_name %> HEALTH_STATUS GROUND2STATUS 300 65 nil 5 WITH_UNITS
END
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,15 @@ export default {
},
computed: {
labelName() {
let label = this.parameters[2]
// Remove double bracket escaping. This means they actually have an item
// with a bracket in the name, not an array index.
if (label.includes('[[')) {
label = label.replace('[[', '[').replace(']]', ']')
}
// LabelWidget uses index 0 from the parameters prop
// so create an array with the label text in the first position
return [this.parameters[2] + ':']
return [label + ':']
},
valueParameters() {
return [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export default {
grayLevel: 80,
grayRate: 5,
valueId: null,
arrayIndex: null,
viewDetails: false,
contextMenuShown: false,
x: 0,
Expand Down Expand Up @@ -96,8 +97,17 @@ export default {
// See store.js for how this is set
if (this.screen) {
if (this.screen.screenValues[this.valueId]) {
this.curValue = this.screen.screenValues[this.valueId][0]
if (
this.arrayIndex !== null &&
this.screen.screenValues[this.valueId][0]
) {
this.curValue =
this.screen.screenValues[this.valueId][0][this.arrayIndex]
} else {
this.curValue = this.screen.screenValues[this.valueId][0]
}
}
// }
} else {
this.curValue = null
}
Expand Down Expand Up @@ -190,6 +200,18 @@ export default {
created() {
// If they're not passing us the value and limitsState we have to register
if (this.value === null || this.limitsState === null) {
// Remove double bracket escaping. This means they actually have an item
// with a bracket in the name, not an array index.
if (this.parameters[2].includes('[[')) {
this.parameters[2] = this.parameters[2]
.replace('[[', '[')
.replace(']]', ']')
} else if (this.parameters[2].includes('[')) {
// Brackets mean array indexes (normally, but see above)
let match = this.parameters[2].match(/\[(\d+)\]/)
this.arrayIndex = parseInt(match[1])
this.parameters[2] = this.parameters[2].replace(match[0], '')
}
this.valueId = `${this.parameters[0]}__${this.parameters[1]}__${
this.parameters[2]
}__${this.getType()}`
Expand Down
7 changes: 5 additions & 2 deletions openc3/lib/openc3/config/config_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ def verify_num_parameters(min_num_params, max_num_params, usage = "")
end

# Verifies the indicated parameter in the config doesn't start or end
# with an underscore, doesn't contain a double underscore, doesn't contain
# spaces and doesn't start with a close bracket.
# with an underscore, doesn't contain a double underscore or double bracket,
# doesn't contain spaces and doesn't start with a close bracket.
#
# @param [Integer] index The index of the parameter to check
def verify_parameter_naming(index, usage = "")
Expand All @@ -259,6 +259,9 @@ def verify_parameter_naming(index, usage = "")
if param.include? '__'
raise Error.new(self, "Parameter #{index} (#{param}) for #{@keyword} cannot contain a double underscore ('__').", usage, @url)
end
if param.include? '[[' or param.include? ']]'
raise Error.new(self, "Parameter #{index} (#{param}) for #{@keyword} cannot contain double brackets ('[[' or ']]').", usage, @url)
end
if param.include? ' '
raise Error.new(self, "Parameter #{index} (#{param}) for #{@keyword} cannot contain a space (' ').", usage, @url)
end
Expand Down
14 changes: 11 additions & 3 deletions openc3/python/openc3/config/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ def verify_num_parameters(self, min_num_params, max_num_params, usage=""):
if max_num_params and self.parameters[max_num_params : max_num_params + 1]:
raise ConfigParser.Error(self, f"Too many parameters for {self.keyword}.", usage, self.url)

# Verifies the indicated parameter in the config doesn't start or
# with an underscore, doesn't contain a double underscore, doesn't contain
# spaces and doesn't start with a close bracket.
# Verifies the indicated parameter in the config doesn't start or end
# with an underscore, doesn't contain a double underscore or double bracket,
# doesn't contain spaces and doesn't start with a close bracket.
#
# self.param [Integer] index The index of the parameter to check
def verify_parameter_naming(self, index, usage=""):
Expand All @@ -142,6 +142,14 @@ def verify_parameter_naming(self, index, usage=""):
self.url,
)

if "[[" in param or "]]" in param:
raise ConfigParser.Error(
self,
f"Parameter {index} ({param}) for {self.keyword} cannot contain double brackets ('[[' or ']]').",
usage,
self.url,
)

if " " in param:
raise ConfigParser.Error(
self,
Expand Down
48 changes: 17 additions & 31 deletions openc3/python/test/config/test_config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ def test_verifies_the_maximum_number_of_parameters(self):

def test_verifies_parameters_do_not_have_bad_characters(self):
tf = tempfile.NamedTemporaryFile(mode="w+t")
line = "KEYWORD BAD1_ BAD__2 'BAD 3' }BAD_4"
line = "KEYWORD BAD1_ BAD__2 'BAD 3' }BAD_4 BAD[[5]]"
tf.writelines(line)
tf.seek(0)

Expand Down Expand Up @@ -382,6 +382,12 @@ def test_verifies_parameters_do_not_have_bad_characters(self):
self.cp.verify_parameter_naming,
4,
)
self.assertRaisesRegex(
ConfigParser.Error,
"cannot contain double brackets",
self.cp.verify_parameter_naming,
5,
)
tf.close()

def test_returns_an_error(self):
Expand Down Expand Up @@ -472,9 +478,7 @@ def test_tfn_returns_values_that_dont_convert(self):
def test_converts_string_constants_to_numbers(self):
for val in range(1, 65):
# Unsigned
self.assertEqual(
ConfigParser.handle_defined_constants("MIN", "UINT", val), 0
)
self.assertEqual(ConfigParser.handle_defined_constants("MIN", "UINT", val), 0)
self.assertEqual(
ConfigParser.handle_defined_constants("MAX", "UINT", val),
(2**val - 1),
Expand All @@ -492,9 +496,7 @@ def test_converts_string_constants_to_numbers(self):
for val in [8, 16, 32, 64]:
# Unsigned
self.assertEqual(ConfigParser.handle_defined_constants(f"MIN_UINT{val}"), 0)
self.assertEqual(
ConfigParser.handle_defined_constants(f"MAX_UINT{val}"), (2**val - 1)
)
self.assertEqual(ConfigParser.handle_defined_constants(f"MAX_UINT{val}"), (2**val - 1))
# Signed
self.assertEqual(
ConfigParser.handle_defined_constants(f"MIN_INT{val}"),
Expand All @@ -506,38 +508,22 @@ def test_converts_string_constants_to_numbers(self):
)

# Float
self.assertLess(
ConfigParser.handle_defined_constants("MIN", "FLOAT", 32), -3.4 * 10**38
)
self.assertLess(
ConfigParser.handle_defined_constants("MIN_FLOAT32"), -3.4 * 10**38
)
self.assertGreater(
ConfigParser.handle_defined_constants("MAX", "FLOAT", 32), 3.4 * 10**38
)
self.assertGreater(
ConfigParser.handle_defined_constants("MAX_FLOAT32"), 3.4 * 10**38
)
self.assertLess(ConfigParser.handle_defined_constants("MIN", "FLOAT", 32), -3.4 * 10**38)
self.assertLess(ConfigParser.handle_defined_constants("MIN_FLOAT32"), -3.4 * 10**38)
self.assertGreater(ConfigParser.handle_defined_constants("MAX", "FLOAT", 32), 3.4 * 10**38)
self.assertGreater(ConfigParser.handle_defined_constants("MAX_FLOAT32"), 3.4 * 10**38)
self.assertEqual(
ConfigParser.handle_defined_constants("MIN", "FLOAT", 64),
-sys.float_info.max,
)
self.assertEqual(
ConfigParser.handle_defined_constants("MIN_FLOAT64"), -sys.float_info.max
)
self.assertEqual(ConfigParser.handle_defined_constants("MIN_FLOAT64"), -sys.float_info.max)
self.assertEqual(
ConfigParser.handle_defined_constants("MAX", "FLOAT", 64),
sys.float_info.max,
)
self.assertEqual(
ConfigParser.handle_defined_constants("MAX_FLOAT64"), sys.float_info.max
)
self.assertEqual(
ConfigParser.handle_defined_constants("POS_INFINITY"), float("inf")
)
self.assertEqual(
ConfigParser.handle_defined_constants("NEG_INFINITY"), float("-inf")
)
self.assertEqual(ConfigParser.handle_defined_constants("MAX_FLOAT64"), sys.float_info.max)
self.assertEqual(ConfigParser.handle_defined_constants("POS_INFINITY"), float("inf"))
self.assertEqual(ConfigParser.handle_defined_constants("NEG_INFINITY"), float("-inf"))
self.assertRaisesRegex(
AttributeError,
"Invalid bit size 16 for FLOAT type.",
Expand Down
41 changes: 21 additions & 20 deletions openc3/spec/config/config_parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# GNU Affero General Public License for more details.

# Modified by OpenC3, Inc.
# All changes Copyright 2022, OpenC3, Inc.
# All changes Copyright 2024, OpenC3, Inc.
# All Rights Reserved
#
# This file may also be used under the terms of a commercial license
Expand Down Expand Up @@ -116,7 +116,7 @@ module OpenC3
tf.puts "<%= render '#{subdir_path}' %>"
tf.close

@cp.parse_file(tf.path) do |keyword, params|
@cp.parse_file(tf.path) do |keyword, _params|
expect(keyword).to eql "SUBDIR"
end
tf.unlink
Expand All @@ -133,7 +133,7 @@ module OpenC3
tf.puts "<%= render '#{tf2.path}' %>"
tf.close

@cp.parse_file(tf.path) do |keyword, params|
@cp.parse_file(tf.path) do |keyword, _params|
expect(keyword).to eql "ABSOLUTE"
end
tf.unlink
Expand Down Expand Up @@ -337,7 +337,7 @@ module OpenC3
tf.close

lines = []
@cp.parse_file(tf.path, true) do |keyword, params|
@cp.parse_file(tf.path, true) do |_keyword, _params|
lines << @cp.line
end
expect(lines).to include("# This is a comment")
Expand Down Expand Up @@ -389,7 +389,7 @@ module OpenC3
tf.puts line
tf.close

@cp.parse_file(tf.path) do |keyword, params|
@cp.parse_file(tf.path) do |keyword, _params|
expect(keyword).to eql "KEYWORD"
expect { @cp.verify_num_parameters(1, 1) }.to raise_error(ConfigParser::Error, "Not enough parameters for KEYWORD.")
end
Expand All @@ -402,7 +402,7 @@ module OpenC3
tf.puts line
tf.close

@cp.parse_file(tf.path) do |keyword, params|
@cp.parse_file(tf.path) do |keyword, _params|
expect(keyword).to eql "KEYWORD"
expect { @cp.verify_num_parameters(1, 1) }.to raise_error(ConfigParser::Error, "Too many parameters for KEYWORD.")
end
Expand All @@ -413,15 +413,16 @@ module OpenC3
describe "verify_parameter_naming" do
it "verifies parameters do not have bad characters" do
tf = Tempfile.new('unittest')
line = "KEYWORD BAD1_ BAD__2 'BAD 3' }BAD_4"
line = "KEYWORD BAD1_ BAD__2 'BAD 3' }BAD_4 BAD[[5]]"
tf.puts line
tf.close

@cp.parse_file(tf.path) do |keyword, params|
@cp.parse_file(tf.path) do |_keyword, _params|
expect { @cp.verify_parameter_naming(1) }.to raise_error(ConfigParser::Error, /cannot end with an underscore/)
expect { @cp.verify_parameter_naming(2) }.to raise_error(ConfigParser::Error, /cannot contain a double underscore/)
expect { @cp.verify_parameter_naming(3) }.to raise_error(ConfigParser::Error, /cannot contain a space/)
expect { @cp.verify_parameter_naming(4) }.to raise_error(ConfigParser::Error, /cannot start with a close bracket/)
expect { @cp.verify_parameter_naming(5) }.to raise_error(ConfigParser::Error, /cannot contain double brackets/)
end
tf.unlink
end
Expand All @@ -434,7 +435,7 @@ module OpenC3
tf.puts line
tf.close

@cp.parse_file(tf.path) do |keyword, params|
@cp.parse_file(tf.path) do |_keyword, _params|
error = @cp.error("Hello")
expect(error.message).to eql "Hello"
expect(error.keyword).to eql "KEYWORD"
Expand All @@ -453,7 +454,7 @@ module OpenC3
tf.close

expect {
@cp.parse_file(tf.path) do |keyword, params|
@cp.parse_file(tf.path) do |keyword, _params|
if keyword == "KEYWORD1"
raise @cp.error("Invalid KEYWORD1")
end
Expand Down Expand Up @@ -540,26 +541,26 @@ module OpenC3
(1..64).each do |val|
# Unsigned
expect(ConfigParser.handle_defined_constants("MIN", :UINT, val)).to eql 0
expect(ConfigParser.handle_defined_constants("MAX", :UINT, val)).to eql (2**val - 1)
expect(ConfigParser.handle_defined_constants("MAX", :UINT, val)).to eql((2**val) - 1)
# Signed
expect(ConfigParser.handle_defined_constants("MIN", :INT, val)).to eql (-(2**val) / 2)
expect(ConfigParser.handle_defined_constants("MAX", :INT, val)).to eql ((2**val) / 2 - 1)
expect(ConfigParser.handle_defined_constants("MIN", :INT, val)).to eql(-(2**val) / 2)
expect(ConfigParser.handle_defined_constants("MAX", :INT, val)).to eql(((2**val) / 2) - 1)
end
[8, 16, 32, 64].each do |val|
# Unsigned
expect(ConfigParser.handle_defined_constants("MIN_UINT#{val}")).to eql 0
expect(ConfigParser.handle_defined_constants("MAX_UINT#{val}")).to eql (2**val - 1)
expect(ConfigParser.handle_defined_constants("MAX_UINT#{val}")).to eql((2**val) - 1)
# Signed
expect(ConfigParser.handle_defined_constants("MIN_INT#{val}")).to eql (-(2**val) / 2)
expect(ConfigParser.handle_defined_constants("MAX_INT#{val}")).to eql ((2**val) / 2 - 1)
expect(ConfigParser.handle_defined_constants("MIN_INT#{val}")).to eql(-(2**val) / 2)
expect(ConfigParser.handle_defined_constants("MAX_INT#{val}")).to eql(((2**val) / 2) - 1)
end
# Float
expect(ConfigParser.handle_defined_constants("MIN_FLOAT32")).to be <= -3.4 * 10**38
expect(ConfigParser.handle_defined_constants("MAX_FLOAT32")).to be >= 3.4 * 10**38
expect(ConfigParser.handle_defined_constants("MIN_FLOAT64")).to eql (-Float::MAX)
expect(ConfigParser.handle_defined_constants("MIN_FLOAT32")).to be <= -3.4 * (10**38)
expect(ConfigParser.handle_defined_constants("MAX_FLOAT32")).to be >= 3.4 * (10**38)
expect(ConfigParser.handle_defined_constants("MIN_FLOAT64")).to eql(-Float::MAX)
expect(ConfigParser.handle_defined_constants("MAX_FLOAT64")).to eql Float::MAX
expect(ConfigParser.handle_defined_constants("POS_INFINITY")).to eql Float::INFINITY
expect(ConfigParser.handle_defined_constants("NEG_INFINITY")).to eql (-Float::INFINITY)
expect(ConfigParser.handle_defined_constants("NEG_INFINITY")).to eql(-Float::INFINITY)
end

it "complains about undefined strings" do
Expand Down
Loading