fprime-gds 3.5.2a2__py3-none-any.whl → 3.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fprime_gds/common/data_types/cmd_data.py +2 -2
- fprime_gds/flask/commands.py +1 -1
- fprime_gds/flask/static/addons/commanding/arguments.js +50 -20
- fprime_gds/flask/static/addons/commanding/command-history.js +3 -22
- fprime_gds/flask/static/addons/commanding/command-input.js +6 -1
- fprime_gds/flask/static/addons/commanding/command-string-template.js +2 -1
- fprime_gds/flask/static/addons/commanding/command-string.js +5 -1
- fprime_gds/flask/static/addons/sequencer/addon.js +2 -0
- fprime_gds/flask/static/js/json.js +340 -0
- fprime_gds/flask/static/js/loader.js +3 -114
- {fprime_gds-3.5.2a2.dist-info → fprime_gds-3.6.1.dist-info}/METADATA +1 -1
- {fprime_gds-3.5.2a2.dist-info → fprime_gds-3.6.1.dist-info}/RECORD +17 -16
- {fprime_gds-3.5.2a2.dist-info → fprime_gds-3.6.1.dist-info}/WHEEL +1 -1
- {fprime_gds-3.5.2a2.dist-info → fprime_gds-3.6.1.dist-info}/LICENSE.txt +0 -0
- {fprime_gds-3.5.2a2.dist-info → fprime_gds-3.6.1.dist-info}/NOTICE.txt +0 -0
- {fprime_gds-3.5.2a2.dist-info → fprime_gds-3.6.1.dist-info}/entry_points.txt +0 -0
- {fprime_gds-3.5.2a2.dist-info → fprime_gds-3.6.1.dist-info}/top_level.txt +0 -0
@@ -148,13 +148,13 @@ class CmdData(sys_data.SysData):
|
|
148
148
|
args = []
|
149
149
|
for val, arg_tuple in zip(input_values, self.template.arguments):
|
150
150
|
try:
|
151
|
-
|
151
|
+
arg_name, _, arg_type = arg_tuple
|
152
152
|
arg_value = arg_type()
|
153
153
|
self.convert_arg_value(val, arg_value)
|
154
154
|
args.append(arg_value)
|
155
155
|
errors.append("")
|
156
156
|
except Exception as exc:
|
157
|
-
errors.append(
|
157
|
+
errors.append(f"{arg_name}[{arg_type.__name__}]: {exc}")
|
158
158
|
return args, errors
|
159
159
|
|
160
160
|
@staticmethod
|
fprime_gds/flask/commands.py
CHANGED
@@ -49,7 +49,7 @@ class CommandArgumentsInvalidException(werkzeug.exceptions.BadRequest):
|
|
49
49
|
"""Command arguments failed to validate properly"""
|
50
50
|
|
51
51
|
def __init__(self, errors):
|
52
|
-
super().__init__("Failed to validate all arguments")
|
52
|
+
super().__init__(f"Failed to validate all arguments: {', '.join(errors)}")
|
53
53
|
self.args = errors
|
54
54
|
|
55
55
|
|
@@ -74,7 +74,7 @@ export function command_argument_assignment_helper(argument, squashed_argument_v
|
|
74
74
|
command_argument_array_serializable_assignment_helper(argument, squashed_argument_value);
|
75
75
|
} else {
|
76
76
|
let is_not_string = typeof(argument.type.MAX_LENGTH) === "undefined";
|
77
|
-
argument.value = (is_not_string && (squashed_argument_value === FILL_NEEDED)) ? null : squashed_argument_value;
|
77
|
+
argument.value = (is_not_string && (squashed_argument_value === FILL_NEEDED)) ? null : squashed_argument_value.toString();
|
78
78
|
}
|
79
79
|
}
|
80
80
|
|
@@ -119,7 +119,21 @@ export function squashify_argument(argument) {
|
|
119
119
|
let field = argument.type.MEMBER_LIST[i][0];
|
120
120
|
value[field] = squashify_argument(argument.value[field]);
|
121
121
|
}
|
122
|
-
} else if (["U64Type"
|
122
|
+
} else if (["U64Type"].indexOf(argument.type.name) !== -1) {
|
123
|
+
if (argument.value.startsWith("0x")) {
|
124
|
+
// Hexadecimal
|
125
|
+
value = BigInt(argument.value, 16);
|
126
|
+
} else if (argument.value.startsWith("0b")) {
|
127
|
+
// Binary
|
128
|
+
value = BigInt(argument.value.slice(2), 2);
|
129
|
+
} else if (argument.value.startsWith("0o")) {
|
130
|
+
// Octal
|
131
|
+
value = BigInt(argument.value.slice(2), 8);
|
132
|
+
} else {
|
133
|
+
// Decimal
|
134
|
+
value = BigInt(argument.value, 10);
|
135
|
+
}
|
136
|
+
} else if (["U32Type", "U16Type", "U8Type"].indexOf(argument.type.name) !== -1) {
|
123
137
|
if (argument.value.startsWith("0x")) {
|
124
138
|
// Hexadecimal
|
125
139
|
value = parseInt(argument.value, 16);
|
@@ -134,10 +148,13 @@ export function squashify_argument(argument) {
|
|
134
148
|
value = parseInt(argument.value, 10);
|
135
149
|
}
|
136
150
|
}
|
137
|
-
else if (["I64Type"
|
151
|
+
else if (["I64Type"].indexOf(argument.type.name) !== -1) {
|
152
|
+
value = BigInt(argument.value, 10);
|
153
|
+
}
|
154
|
+
else if (["I32Type", "I16Type", "I8Type"].indexOf(argument.type.name) !== -1) {
|
138
155
|
value = parseInt(argument.value, 10);
|
139
156
|
}
|
140
|
-
else if (["F64Type", "F32Type"].indexOf(argument.type.name)
|
157
|
+
else if (["F64Type", "F32Type"].indexOf(argument.type.name) !== -1) {
|
141
158
|
value = parseFloat(argument.value);
|
142
159
|
}
|
143
160
|
else if (argument.type.name == "BoolType") {
|
@@ -160,22 +177,35 @@ export function squashify_argument(argument) {
|
|
160
177
|
* @returns: string to display
|
161
178
|
*/
|
162
179
|
export function argument_display_string(argument) {
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
let
|
172
|
-
|
180
|
+
let string = FILL_NEEDED;
|
181
|
+
try {
|
182
|
+
// Check for array
|
183
|
+
if (argument.type.LENGTH) {
|
184
|
+
string = `[${argument.value.map((argument) => argument_display_string(argument)).join(", ")}]`;
|
185
|
+
}
|
186
|
+
// Serializable
|
187
|
+
else if (argument.type.MEMBER_LIST) {
|
188
|
+
let fields = [];
|
189
|
+
for (let i = 0; i < argument.type.MEMBER_LIST.length; i++) {
|
190
|
+
let field = argument.type.MEMBER_LIST[i][0];
|
191
|
+
fields.push(`${field}: ${argument_display_string(argument.value[field])}`);
|
192
|
+
}
|
193
|
+
string = `{${fields.join(", ")}}`
|
194
|
+
}
|
195
|
+
// String type
|
196
|
+
else if (argument.type.MAX_LENGTH) {
|
197
|
+
let value = (argument.value == null) ? "" : argument.value;
|
198
|
+
value = value.replace(/"/g, '\\\"');
|
199
|
+
string = `"${value}"`
|
200
|
+
}
|
201
|
+
// Unassigned values
|
202
|
+
else if (argument.value == null || argument.value === "") {
|
203
|
+
string = FILL_NEEDED;
|
204
|
+
} else {
|
205
|
+
string = squashify_argument(argument);
|
173
206
|
}
|
174
|
-
|
175
|
-
|
176
|
-
let value = (argument.value == null) ? "" : argument.value;
|
177
|
-
value = value.replace(/"/g, '\\\"');
|
178
|
-
string = `"${value}"`
|
207
|
+
} catch (e) {
|
208
|
+
string = FILL_NEEDED;
|
179
209
|
}
|
180
210
|
return string;
|
181
211
|
}
|
@@ -291,7 +321,7 @@ Vue.component("command-scalar-argument", {
|
|
291
321
|
return ["text", "0[bB][01]+|0[oO][0-7]+|0[xX][0-9a-fA-F]+|[1-9]\\d*|0", ""];
|
292
322
|
}
|
293
323
|
else if (["I64Type", "I32Type", "I16Type", "I8Type"].indexOf(this.argument.type.name) != -1) {
|
294
|
-
return ["
|
324
|
+
return ["text", "-?[1-9]\\d*|0", ""];
|
295
325
|
}
|
296
326
|
else if (["F64Type", "F32Type"].indexOf(this.argument.type.name) != -1) {
|
297
327
|
return ["number", null, "any"];
|
@@ -10,7 +10,8 @@ import {command_argument_assignment_helper} from "./arguments.js";
|
|
10
10
|
import {listExistsAndItemNameNotInList, timeToString} from "../../js/vue-support/utils.js";
|
11
11
|
import {command_history_template} from "./command-history-template.js";
|
12
12
|
import {command_display_string} from "./command-string.js";
|
13
|
-
|
13
|
+
import { SaferParser } from "../../js/json.js";
|
14
|
+
SaferParser.register();
|
14
15
|
|
15
16
|
/**
|
16
17
|
* command-history:
|
@@ -103,27 +104,7 @@ Vue.component("command-history", {
|
|
103
104
|
// Can only set command if it is a child of a command input
|
104
105
|
if (this.$parent.selectCmd) {
|
105
106
|
// command-input expects an array of strings as arguments
|
106
|
-
this.$parent.selectCmd(cmd.full_name,
|
107
|
-
}
|
108
|
-
},
|
109
|
-
/**
|
110
|
-
* Process the arguments for a command. If the argument is (or contains) a number, it
|
111
|
-
* is converted to a string. Other types that should be pre-processed can be added here.
|
112
|
-
*
|
113
|
-
* @param {*} args
|
114
|
-
* @returns args processed for command input (numbers converted to strings)
|
115
|
-
*/
|
116
|
-
preprocess_args(args) {
|
117
|
-
if (Array.isArray(args)) {
|
118
|
-
return args.map(el => this.preprocess_args(el));
|
119
|
-
} else if (typeof args === 'object' && args !== null) {
|
120
|
-
return Object.fromEntries(
|
121
|
-
Object.entries(args).map(([key, value]) => [key, this.preprocess_args(value)])
|
122
|
-
);
|
123
|
-
} else if (typeof args === 'number') {
|
124
|
-
return args.toString();
|
125
|
-
} else {
|
126
|
-
return args;
|
107
|
+
this.$parent.selectCmd(cmd.full_name, cmd.args);
|
127
108
|
}
|
128
109
|
}
|
129
110
|
}
|
@@ -15,6 +15,8 @@ import {
|
|
15
15
|
} from "../../addons/commanding/arguments.js";
|
16
16
|
import {_settings} from "../../js/settings.js";
|
17
17
|
import {command_input_template} from "./command-input-template.js";
|
18
|
+
import { SaferParser } from "../../js/json.js";
|
19
|
+
SaferParser.register();
|
18
20
|
|
19
21
|
/**
|
20
22
|
* This helper will help assign command and values in a safe manner by searching the command store, finding a reference,
|
@@ -38,6 +40,7 @@ function command_assignment_helper(desired_command_name, desired_command_args, p
|
|
38
40
|
return null;
|
39
41
|
}
|
40
42
|
let selected = _datastore.commands[command_name];
|
43
|
+
|
41
44
|
// Set arguments here
|
42
45
|
for (let i = 0; i < selected.args.length; i++) {
|
43
46
|
let assign_value = (desired_command_args.length > i)? desired_command_args[i] : null;
|
@@ -147,6 +150,7 @@ Vue.component("command-input", {
|
|
147
150
|
* command reaches the ground system.
|
148
151
|
*/
|
149
152
|
sendCommand() {
|
153
|
+
|
150
154
|
// Validate the command before sending anything
|
151
155
|
if (!this.validate()) {
|
152
156
|
return;
|
@@ -158,8 +162,9 @@ Vue.component("command-input", {
|
|
158
162
|
let _self = this;
|
159
163
|
_self.active = true;
|
160
164
|
let command = this.selected;
|
165
|
+
let squashed_args = command.args.map(serialize_arg);
|
161
166
|
this.loader.load("/commands/" + command.full_name, "PUT",
|
162
|
-
{"key":0xfeedcafe, "arguments":
|
167
|
+
{"key":0xfeedcafe, "arguments": squashed_args})
|
163
168
|
.then(function() {
|
164
169
|
_self.active = false;
|
165
170
|
// Clear errors, as there is not a problem further
|
@@ -3,7 +3,8 @@
|
|
3
3
|
*
|
4
4
|
* Contains the templates used to render the command string input box.
|
5
5
|
*/
|
6
|
-
export let COMMAND_FORMAT_SPEC = "FULL_COMMAND_NAME[[[, ARG1], ARG2], ...]"
|
6
|
+
export let COMMAND_FORMAT_SPEC = "FULL_COMMAND_NAME[[[, ARG1], ARG2], ...] " +
|
7
|
+
"where ARGN is a decimal number, quoted string, or an enumerated constant";
|
7
8
|
|
8
9
|
export let command_string_template = `
|
9
10
|
<div class="fp-flex-repeater">
|
@@ -10,6 +10,8 @@ import {
|
|
10
10
|
command_string_template
|
11
11
|
} from "./command-string-template.js";
|
12
12
|
import {argument_display_string, FILL_NEEDED} from "./arguments.js"
|
13
|
+
import {SaferParser} from "../../js/json.js";
|
14
|
+
SaferParser.register();
|
13
15
|
|
14
16
|
let STRING_PREPROCESSOR = new RegExp(`(?:"((?:[^\"]|\\\")*)")|([a-zA-Z_][a-zA-Z_0-9.]*)|(${FILL_NEEDED})`, "g");
|
15
17
|
|
@@ -61,7 +63,9 @@ Vue.component("command-text", {
|
|
61
63
|
} catch (e) {
|
62
64
|
// JSON parsing exceptions
|
63
65
|
if (e instanceof SyntaxError) {
|
64
|
-
this.error = `Expected command string of the form: ${COMMAND_FORMAT_SPEC}
|
66
|
+
this.error = `Expected command string of the form: ${COMMAND_FORMAT_SPEC}.`;
|
67
|
+
} else {
|
68
|
+
throw e;
|
65
69
|
}
|
66
70
|
}
|
67
71
|
}
|
@@ -16,6 +16,8 @@ import {_datastore} from "../../js/datastore.js";
|
|
16
16
|
import {basicSetup, EditorState, EditorView, linter} from "./third/code-mirror.es.js"
|
17
17
|
import {sequenceLanguageSupport} from "./autocomplete.js"
|
18
18
|
import {processResponse} from "./lint.js";
|
19
|
+
import { SaferParser } from "../../js/json.js";
|
20
|
+
SaferParser.register();
|
19
21
|
|
20
22
|
/**
|
21
23
|
* Sequence sender function used to uplink the sequence and return a promise of how to handle the server's return.
|
@@ -0,0 +1,340 @@
|
|
1
|
+
/**
|
2
|
+
* json.js:
|
3
|
+
*
|
4
|
+
* Contains specialized JSON parser to handle non-standard JSON values from the JavaScript perspective. These values
|
5
|
+
* are legal in Python and scala, but not in JavaScript. This parser will safely handle these values.
|
6
|
+
*
|
7
|
+
* @author mstarch
|
8
|
+
*/
|
9
|
+
|
10
|
+
|
11
|
+
/**
|
12
|
+
* Lexer for JSON built using JSON
|
13
|
+
*/
|
14
|
+
class RegExLexer {
|
15
|
+
static TOKEN_EXPRESSIONS = new Map([
|
16
|
+
// String tokens: " then
|
17
|
+
// any number of:
|
18
|
+
// not a quote or \
|
19
|
+
// \ followed by not a quote
|
20
|
+
// even number of \
|
21
|
+
// odd number of \ then " (escaped quotation)
|
22
|
+
// then even number of \ then " (terminating non-escaped ")
|
23
|
+
["STRING", /^"([^"\\]|(\\[^"\\])|((\\\\)*)|(\\(\\\\)*)")*(?!\\(\\\\)*)"/],
|
24
|
+
// Floating point tokens
|
25
|
+
["NUMBER", /^-?\d+(\.\d+)?([eE][+\-]?\d+)?/],
|
26
|
+
// Infinity token
|
27
|
+
["INFINITY", /^-?Infinity/],
|
28
|
+
// Null token
|
29
|
+
["NULL", /^null/],
|
30
|
+
// NaN token
|
31
|
+
["NAN", /^NaN/],
|
32
|
+
// boolean token
|
33
|
+
["BOOLEAN", /^(true)|^(false)/],
|
34
|
+
// Open object token
|
35
|
+
["OPEN_OBJECT", /^\{/],
|
36
|
+
// Close object token
|
37
|
+
["CLOSE_OBJECT", /^}/],
|
38
|
+
// Field separator token
|
39
|
+
["FIELD_SEPARATOR", /^,/],
|
40
|
+
// Open list token
|
41
|
+
["OPEN_ARRAY", /^\[/],
|
42
|
+
// Close a list token
|
43
|
+
["CLOSE_ARRAY", /^]/],
|
44
|
+
// Key Value Separator
|
45
|
+
["VALUE_SEPARATOR", /^:/],
|
46
|
+
// Any amount of whitespace is an implicit token
|
47
|
+
["WHITESPACE", /^\s+/]
|
48
|
+
]);
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Tokenize the input string based on JSON tokens.
|
52
|
+
* @param input_string: input string to tokenize
|
53
|
+
* @return {*[]}: list of tokens in-order
|
54
|
+
*/
|
55
|
+
static tokenize(original_string) {
|
56
|
+
let tokens = [];
|
57
|
+
let input_string = original_string;
|
58
|
+
let total_length = 0;
|
59
|
+
let last_token_type = "--NONE--"
|
60
|
+
// Consume the whole string
|
61
|
+
while (input_string !== "") {
|
62
|
+
let matched_something = false;
|
63
|
+
for (let [token_type, token_matcher] of RegExLexer.TOKEN_EXPRESSIONS.entries()) {
|
64
|
+
let match = token_matcher.exec(input_string)
|
65
|
+
|
66
|
+
// Token detected
|
67
|
+
if (match != null && match.index == 0 ) {
|
68
|
+
matched_something = true;
|
69
|
+
let matched = match[0];
|
70
|
+
tokens.push([token_type, matched]);
|
71
|
+
// Consume the string
|
72
|
+
input_string = input_string.substring(matched.length);
|
73
|
+
total_length += matched.length;
|
74
|
+
last_token_type = token_type;
|
75
|
+
break;
|
76
|
+
}
|
77
|
+
}
|
78
|
+
// Check for no token match
|
79
|
+
if (!matched_something) {
|
80
|
+
let say = "Failed to match valid token: '" + input_string.substring(0, 20);
|
81
|
+
say += "' Context: '" + original_string.substring(Math.max(total_length - 20, 0), total_length + 20);
|
82
|
+
say += "' Last token's type: " + last_token_type + ".";
|
83
|
+
throw SyntaxError(say);
|
84
|
+
}
|
85
|
+
}
|
86
|
+
return tokens;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
/**
|
91
|
+
* Helper to determine if value is a string
|
92
|
+
* @param value: value to check.
|
93
|
+
* @return {boolean}: true if string, false otherwise
|
94
|
+
*/
|
95
|
+
function isString(value) {
|
96
|
+
return value instanceof String || typeof value === 'string';
|
97
|
+
}
|
98
|
+
|
99
|
+
/**
|
100
|
+
* Helper to determine if value is a function
|
101
|
+
* @param value: value to check
|
102
|
+
* @return {boolean}: true if function, false otherwise
|
103
|
+
*/
|
104
|
+
function isFunction(value) {
|
105
|
+
return value instanceof Function || typeof value == "function";
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Convert a string to a number
|
110
|
+
* @param value: value to convert
|
111
|
+
* @return {bigint|number}: number to return
|
112
|
+
*/
|
113
|
+
function stringToNumber(value) {
|
114
|
+
value = value.trim(); // Should be unnecessary
|
115
|
+
// Process floats (containing . e or E)
|
116
|
+
if (value.search(/[.eE]/) !== -1) {
|
117
|
+
return Number.parseFloat(value);
|
118
|
+
}
|
119
|
+
let number_value = Number.parseInt(value);
|
120
|
+
// When the big and normal numbers match, then return the normal number
|
121
|
+
if (value !== number_value.toString()) {
|
122
|
+
return BigInt(value);
|
123
|
+
}
|
124
|
+
return number_value;
|
125
|
+
}
|
126
|
+
|
127
|
+
/**
|
128
|
+
* Parser to safely handle potential JSON object from Python. Python can produce some non-standard values (infinities,
|
129
|
+
* NaNs, etc.) These values then break on the JS Javascript parser. To localize these faults, they are replaced before
|
130
|
+
* processing with strings and then formally set during parsing.
|
131
|
+
*
|
132
|
+
* This is done by looking for tokens in unquoted text and replacing them with string representations.
|
133
|
+
*
|
134
|
+
* This parser will handle:
|
135
|
+
* - -Infinity
|
136
|
+
* - Infinity
|
137
|
+
* - NaN
|
138
|
+
* - null
|
139
|
+
* - BigInt
|
140
|
+
*/
|
141
|
+
export class SaferParser {
|
142
|
+
static CONVERSION_KEY = "fprime{replacement";
|
143
|
+
|
144
|
+
static CONVERSION_MAP = new Map([
|
145
|
+
["INFINITY", (value) => (value[0] === "-") ? -Infinity : Infinity],
|
146
|
+
["NAN", NaN],
|
147
|
+
["NULL", null],
|
148
|
+
["NUMBER", stringToNumber]
|
149
|
+
]);
|
150
|
+
|
151
|
+
static STRINGIFY_TOKENS = [
|
152
|
+
Infinity,
|
153
|
+
-Infinity,
|
154
|
+
NaN,
|
155
|
+
"number",
|
156
|
+
"bigint",
|
157
|
+
null
|
158
|
+
];
|
159
|
+
|
160
|
+
|
161
|
+
// Store the language variants the first time
|
162
|
+
static language_parse = JSON.parse;
|
163
|
+
static language_stringify = JSON.stringify;
|
164
|
+
|
165
|
+
/**
|
166
|
+
* @brief safely process F Prime JSON syntax
|
167
|
+
*
|
168
|
+
* Parse method that will replace JSON.parse. This method pre-processes the string data incoming (to be transformed
|
169
|
+
* into JavaScript objects) for detection of entities not expressible in JavaScript's JSON implementation. This will
|
170
|
+
* replace those entities with a JSON flag object.
|
171
|
+
*
|
172
|
+
* Then the data is processed by the JavaScript built-in JSON parser (now done safely). The reviver function will
|
173
|
+
* safely revive the flag objects into JavaScript representations of those object.
|
174
|
+
*
|
175
|
+
* Handles:
|
176
|
+
* 1. BigInts
|
177
|
+
* 2. Inf/-Inf
|
178
|
+
* 3. NaN
|
179
|
+
* 4. null
|
180
|
+
*
|
181
|
+
* @param json_string: JSON string data containing potentially bad values
|
182
|
+
* @param reviver: reviver function to be combined with our reviver
|
183
|
+
* @return {{}}: Javascript Object representation of data safely represented in JavaScript types
|
184
|
+
*/
|
185
|
+
static parse(json_string, reviver) {
|
186
|
+
let converted_data = SaferParser.preprocess(json_string);
|
187
|
+
// Set up a composite reviver of the one passed in and ours
|
188
|
+
let input_reviver = reviver || ((key, value) => value);
|
189
|
+
let full_reviver = (key, value) => input_reviver(key, SaferParser.reviver(key, value));
|
190
|
+
try {
|
191
|
+
let language_parsed = SaferParser.language_parse(converted_data, full_reviver);
|
192
|
+
return language_parsed;
|
193
|
+
} catch (e) {
|
194
|
+
let message = e.toString();
|
195
|
+
const matcher = /line (\d+) column (\d+)/
|
196
|
+
|
197
|
+
// Process the match
|
198
|
+
let snippet = "";
|
199
|
+
let match = message.match(matcher);
|
200
|
+
if (match != null) {
|
201
|
+
let lines = converted_data.split("\n");
|
202
|
+
let line = lines[Number.parseInt(match[1]) - 1]
|
203
|
+
snippet = line.substring(Number.parseInt(match[2]) - 6, Number.parseInt(match[2]) + 5);
|
204
|
+
message += ". Offending snippet: " + snippet;
|
205
|
+
throw new SyntaxError(message);
|
206
|
+
}
|
207
|
+
throw e;
|
208
|
+
}
|
209
|
+
}
|
210
|
+
|
211
|
+
/**
|
212
|
+
* @brief safely write the F Prime JSON syntax
|
213
|
+
*
|
214
|
+
* Stringify method that will replace JSON.stringify. This method post-processes the string data outgoing from
|
215
|
+
* JavaScript's built-in stringify method to replace flag-objects with the correct F Prime representation in
|
216
|
+
* JavaScript.
|
217
|
+
*
|
218
|
+
* This uses the javascript stringify handler method to pre-convert unsupported types into a flag object. This flag
|
219
|
+
* object is post-converted into a normal string after JSON.stringify has done its best.
|
220
|
+
*
|
221
|
+
* Handles:
|
222
|
+
* 1. BigInts
|
223
|
+
* 2. Inf/-Inf
|
224
|
+
* 3. NaN
|
225
|
+
* 4. null
|
226
|
+
*
|
227
|
+
* @param data: data object to stringify
|
228
|
+
* @param replacer: replacer Array or Function
|
229
|
+
* @param space: space for passing into JSON.stringify
|
230
|
+
* @return {{}}: JSON string using JSON support for big-ints Int/-Inf, NaN and null.
|
231
|
+
*/
|
232
|
+
static stringify(data, replacer, space) {
|
233
|
+
let full_replacer = (key, value) => {
|
234
|
+
// Handle array case for excluded field
|
235
|
+
if (Array.isArray(replacer) && replacer.indexOf(key) === -1) {
|
236
|
+
return undefined;
|
237
|
+
}
|
238
|
+
// Run input replacer first
|
239
|
+
else if (isFunction(replacer)) {
|
240
|
+
value = replacer(key, value);
|
241
|
+
}
|
242
|
+
// Then run our safe replacer
|
243
|
+
let replaced = SaferParser.replaceFromObject(key, value);
|
244
|
+
return replaced;
|
245
|
+
};
|
246
|
+
// Stringify JSON using built-in JSON parser and the special replacer
|
247
|
+
let json_string = SaferParser.language_stringify(data, full_replacer, space);
|
248
|
+
// Post-process JSON string to rework JSON into the wider specification
|
249
|
+
let post_replace = SaferParser.postReplacer(json_string);
|
250
|
+
return post_replace
|
251
|
+
}
|
252
|
+
|
253
|
+
/**
|
254
|
+
* Get replacement object from a JavaScript type
|
255
|
+
* @param _: unused
|
256
|
+
* @param value: value to replace
|
257
|
+
*/
|
258
|
+
static replaceFromObject(_, value) {
|
259
|
+
for (let i = 0; i < SaferParser.STRINGIFY_TOKENS.length; i++) {
|
260
|
+
let replacer_type = SaferParser.STRINGIFY_TOKENS[i];
|
261
|
+
let mapper_is_string = isString(replacer_type);
|
262
|
+
if ((!mapper_is_string && value === replacer_type) || (mapper_is_string && typeof value === replacer_type)) {
|
263
|
+
let replace_object = {};
|
264
|
+
replace_object[SaferParser.CONVERSION_KEY] = (value == null) ? "null" : value.toString();
|
265
|
+
return replace_object;
|
266
|
+
}
|
267
|
+
}
|
268
|
+
return value;
|
269
|
+
}
|
270
|
+
|
271
|
+
/**
|
272
|
+
* Replace JSON notation for fprime-replacement objects with the wider JSON specification
|
273
|
+
*
|
274
|
+
* Replace {"fprime-replacement: "some value"} with <some value> restoring the full JSON specification for items not
|
275
|
+
* supported by JavaScript.
|
276
|
+
*
|
277
|
+
* @param json_string: JSON string to rework
|
278
|
+
* @return reworked JSON string
|
279
|
+
*/
|
280
|
+
static postReplacer(json_string) {
|
281
|
+
return json_string.replace(/\{\s*"fprime\{replacement"\s*:\s*"([^"]+)"\s*}/sg, "$1");
|
282
|
+
}
|
283
|
+
|
284
|
+
/**
|
285
|
+
* Apply process function to raw json string only for data that is not qu
|
286
|
+
* @param json_string: JSON string to preprocess
|
287
|
+
* @return {string}
|
288
|
+
*/
|
289
|
+
static preprocess(json_string) {
|
290
|
+
const CONVERSION_KEYS = Array.from(SaferParser.CONVERSION_MAP.keys());
|
291
|
+
let tokens = RegExLexer.tokenize(json_string);
|
292
|
+
let converted_text = tokens.map(
|
293
|
+
([token_type, token_text]) => {
|
294
|
+
if (CONVERSION_KEYS.indexOf(token_type) !== -1) {
|
295
|
+
let replacement_object = {};
|
296
|
+
replacement_object[SaferParser.CONVERSION_KEY] = token_type;
|
297
|
+
replacement_object["value"] = token_text;
|
298
|
+
return SaferParser.language_stringify(replacement_object)
|
299
|
+
}
|
300
|
+
return token_text;
|
301
|
+
});
|
302
|
+
return converted_text.join("");
|
303
|
+
}
|
304
|
+
|
305
|
+
/**
|
306
|
+
* Inverse of convert removing string and replacing back invalid JSON tokens.
|
307
|
+
* @param key: JSON key
|
308
|
+
* @param value: JSON value search for the converted value.
|
309
|
+
* @return {*}: reverted value or value
|
310
|
+
*/
|
311
|
+
static reviver(key, value) {
|
312
|
+
// Look for fprime-replacement and quickly abort if not there
|
313
|
+
let replacement_type = value[SaferParser.CONVERSION_KEY];
|
314
|
+
if (typeof replacement_type === "undefined") {
|
315
|
+
return value;
|
316
|
+
}
|
317
|
+
let string_value = value["value"];
|
318
|
+
let replacer = SaferParser.CONVERSION_MAP.get(replacement_type);
|
319
|
+
return isFunction(replacer) ? replacer(string_value) : replacer;
|
320
|
+
}
|
321
|
+
|
322
|
+
/**
|
323
|
+
* @brief force all calls to JSON.parse and JSON.stringify to use the SafeParser
|
324
|
+
*/
|
325
|
+
static register() {
|
326
|
+
// Override the singleton
|
327
|
+
JSON.parse = SaferParser.parse;
|
328
|
+
JSON.stringify = SaferParser.stringify;
|
329
|
+
}
|
330
|
+
|
331
|
+
/**
|
332
|
+
* @brief remove the JSON.parse safe override
|
333
|
+
*/
|
334
|
+
static deregister() {
|
335
|
+
JSON.parse = SaferParser.language_parse;
|
336
|
+
JSON.stringify = SaferParser.language_stringify;
|
337
|
+
}
|
338
|
+
}
|
339
|
+
// Take over all JSON.parse and JSON.stringify calls
|
340
|
+
SaferParser.register();
|
@@ -13,7 +13,8 @@
|
|
13
13
|
*/
|
14
14
|
import {config} from "./config.js";
|
15
15
|
import {_settings} from "./settings.js";
|
16
|
-
import {
|
16
|
+
import {SaferParser} from "./json.js";
|
17
|
+
SaferParser.register();
|
17
18
|
|
18
19
|
/**
|
19
20
|
* Function allowing for the saving of some data to a downloadable file.
|
@@ -50,118 +51,6 @@ export function loadTextFileInputData(event) {
|
|
50
51
|
});
|
51
52
|
}
|
52
53
|
|
53
|
-
/**
|
54
|
-
* Parser to safely handle potential JSON object from Python. Python can produce some non-standard values (infinities,
|
55
|
-
* NaNs, etc.) These values then break on the JS Javascript parser. To localize these faults, they are replaced before
|
56
|
-
* processing with strings and then formally set during parsing.
|
57
|
-
*
|
58
|
-
* This is done by looking for tokens in unquoted text and replacing them with string representations.
|
59
|
-
*
|
60
|
-
*/
|
61
|
-
class SaferParser {
|
62
|
-
/**
|
63
|
-
* Set up the parser
|
64
|
-
*/
|
65
|
-
constructor() {
|
66
|
-
this.STATES = {
|
67
|
-
UNQUOTED: 0,
|
68
|
-
QUOTED: 1
|
69
|
-
};
|
70
|
-
this.FLAG = "-_-您好"; // Extended character usage make collisions less-likely
|
71
|
-
this.MAPPINGS = [
|
72
|
-
["-Infinity", this.FLAG + "-inf", -Infinity],
|
73
|
-
["Infinity", this.FLAG + "inf", Infinity],
|
74
|
-
["NaN", this.FLAG + "nan", NaN],
|
75
|
-
["null", this.FLAG + "null", null]
|
76
|
-
];
|
77
|
-
this.state = this.STATES.UNQUOTED;
|
78
|
-
}
|
79
|
-
|
80
|
-
/**
|
81
|
-
* Parse method that will replace JSON.parse. This handles known bad cases and also prints better error messages
|
82
|
-
* including the working snippets of text.
|
83
|
-
* @param rawData: string data
|
84
|
-
* @return {{}|any}: Javascript Object representation of data.
|
85
|
-
*/
|
86
|
-
parse(rawData) {
|
87
|
-
let converted_data = this.convert(rawData);
|
88
|
-
try {
|
89
|
-
return JSON.parse(converted_data, this.revert.bind(this));
|
90
|
-
} catch (e) {
|
91
|
-
let message = e.toString();
|
92
|
-
const matcher = /line (\d+) column (\d+)/
|
93
|
-
|
94
|
-
// Process the match
|
95
|
-
let snippet = "";
|
96
|
-
let match = message.match(matcher);
|
97
|
-
if (match != null) {
|
98
|
-
let lines = converted_data.split("\n");
|
99
|
-
let line = lines[Number.parseInt(match[1]) - 1]
|
100
|
-
snippet = line.substring(Number.parseInt(match[2]) - 6, Number.parseInt(match[2]) + 5);
|
101
|
-
message += ". Offending snippet: " + snippet;
|
102
|
-
}
|
103
|
-
_validator.updateErrors([message]);
|
104
|
-
}
|
105
|
-
return {};
|
106
|
-
}
|
107
|
-
|
108
|
-
/**
|
109
|
-
* Convert data from invalid form to strings.
|
110
|
-
* @param rawData: raw data including potentially invalid data
|
111
|
-
* @return {string}: string data in correct JSON format
|
112
|
-
*/
|
113
|
-
convert(rawData) {
|
114
|
-
let unprocessed = rawData;
|
115
|
-
let transformed_data = "";
|
116
|
-
|
117
|
-
while (unprocessed.length > 0) {
|
118
|
-
let next_quote = unprocessed.indexOf("\"");
|
119
|
-
let section = (next_quote !== -1) ? unprocessed.substring(0, next_quote + 1) : unprocessed.substring(0);
|
120
|
-
unprocessed = unprocessed.substring(section.length);
|
121
|
-
transformed_data += this.processChunk(section);
|
122
|
-
this.state = (this.state === this.STATES.QUOTED) ? this.STATES.UNQUOTED : this.STATES.QUOTED;
|
123
|
-
}
|
124
|
-
return transformed_data;
|
125
|
-
}
|
126
|
-
|
127
|
-
/**
|
128
|
-
* Inverse of convert removing string and replacing back invalid JSON tokens.
|
129
|
-
* @param key: JSON key
|
130
|
-
* @param value: JSON value search for the converted value.
|
131
|
-
* @return {*}: reverted value or value
|
132
|
-
*/
|
133
|
-
revert(key, value) {
|
134
|
-
for (let i = 0; i < this.MAPPINGS.length; i++) {
|
135
|
-
if ((this.MAPPINGS[i][1]) === value) {
|
136
|
-
return this.MAPPINGS[i][2];
|
137
|
-
}
|
138
|
-
}
|
139
|
-
return value;
|
140
|
-
}
|
141
|
-
|
142
|
-
/**
|
143
|
-
* Process a section of the JSON string looking for values to convert. This is intended to handle a section of
|
144
|
-
* quoted or unquoted text but should never handle quoted and unquoted data in one call.
|
145
|
-
* @param section: section of the data
|
146
|
-
* @return {*}: converted data
|
147
|
-
*/
|
148
|
-
processChunk(section) {
|
149
|
-
// Replaces all the above mappings with a flagged value
|
150
|
-
let replace_all = (section) => {
|
151
|
-
for (let i = 0; i < this.MAPPINGS.length; i++) {
|
152
|
-
section = section.replace(this.MAPPINGS[i][0], "\"" + this.MAPPINGS[i][1] + "\"");
|
153
|
-
}
|
154
|
-
return section;
|
155
|
-
}
|
156
|
-
|
157
|
-
// When out of quoted space,
|
158
|
-
if (this.state === this.STATES.UNQUOTED) {
|
159
|
-
return replace_all(section);
|
160
|
-
}
|
161
|
-
return section;
|
162
|
-
}
|
163
|
-
}
|
164
|
-
|
165
54
|
/**
|
166
55
|
* Loader:
|
167
56
|
*
|
@@ -308,7 +197,7 @@ class Loader {
|
|
308
197
|
if (this.readyState === 4 && this.status === 200 && raw) {
|
309
198
|
resolve(this.responseText);
|
310
199
|
} else if (this.readyState === 4 && this.status === 200) {
|
311
|
-
let dataObj =
|
200
|
+
let dataObj = JSON.parse(this.responseText);
|
312
201
|
resolve(dataObj);
|
313
202
|
} else if(this.readyState === 4) {
|
314
203
|
reject(this.responseText);
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: fprime-gds
|
3
|
-
Version: 3.
|
3
|
+
Version: 3.6.1
|
4
4
|
Summary: F Prime Flight Software Ground Data System layer
|
5
5
|
Author-email: Michael Starch <Michael.D.Starch@jpl.nasa.gov>, Thomas Boyer-Chammard <Thomas.Boyer.Chammard@jpl.nasa.gov>
|
6
6
|
License:
|
@@ -17,7 +17,7 @@ fprime_gds/common/communication/adapters/uart.py,sha256=6SrN42ShVjwNubFg-1YrO09o
|
|
17
17
|
fprime_gds/common/controllers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
18
|
fprime_gds/common/data_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
19
|
fprime_gds/common/data_types/ch_data.py,sha256=RP9zSyzNcH0nJ3MYyW_IATnmnHYZ6d0KmoJUJantdBI,6111
|
20
|
-
fprime_gds/common/data_types/cmd_data.py,sha256=
|
20
|
+
fprime_gds/common/data_types/cmd_data.py,sha256=hsNJHCPKfqDRx4EzZ5DjobyD-oFlcXEWBS8NbDNfqgk,7090
|
21
21
|
fprime_gds/common/data_types/event_data.py,sha256=7_vA6Xwvs9kK1-xJzc6lwO_TtUeWdI7p29B6QJNMc40,5372
|
22
22
|
fprime_gds/common/data_types/exceptions.py,sha256=C16L2lofigH8UmnsYO_fuY6yR20U-ckRcl14HZjQlJc,1054
|
23
23
|
fprime_gds/common/data_types/file_data.py,sha256=4_G9kf4ThC5NzkxnKa0xNYBdi8UDvZg8f5Vw0DdGIBE,3904
|
@@ -117,7 +117,7 @@ fprime_gds/executables/utils.py,sha256=SbzXRe1p41qMPdifvPap5_4v0T42gZZ_Rs_OYfITd
|
|
117
117
|
fprime_gds/flask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
118
118
|
fprime_gds/flask/app.py,sha256=kJDCziri_BwZWKUszkR7u3RaNG_FWRzDkdCPsVDAtYM,6720
|
119
119
|
fprime_gds/flask/channels.py,sha256=sOeL-UmWPh2hqYvqj81STpABLlPcjdPgkRwjd3Qx77k,735
|
120
|
-
fprime_gds/flask/commands.py,sha256=
|
120
|
+
fprime_gds/flask/commands.py,sha256=62R3b0mnjc3_XpULpqJyUSvAcfOjAyZNifQ3wqHKO7s,3658
|
121
121
|
fprime_gds/flask/components.py,sha256=a-eG8XJfSrqR8MIzIc9StwbNwxcBqkxYMEYq46S2Bmk,4176
|
122
122
|
fprime_gds/flask/default_settings.py,sha256=SkNfd5R4tv59rcmPiHERIZNIEmzXP3KJcJZektgtZCA,603
|
123
123
|
fprime_gds/flask/errors.py,sha256=yN3jDsJd30jL6aOIF-SqbVoesvReHqPvXlIt8qWB87M,2133
|
@@ -154,20 +154,20 @@ fprime_gds/flask/static/addons/chart-display/vendor/chartjs-adapter-luxon.min.js
|
|
154
154
|
fprime_gds/flask/static/addons/chart-display/vendor/hammer.min.js,sha256=SKSRJkZzVMqQzhFeghSWUsCtUon42GUc4gDWk914lDw,20727
|
155
155
|
fprime_gds/flask/static/addons/commanding/addon.js,sha256=d027BtN0LTSIWh1vtL1LIdf0k33tAw1kJ9GIDPbNY4o,263
|
156
156
|
fprime_gds/flask/static/addons/commanding/argument-templates.js,sha256=vx5FP1ZCFT2w_Rc5fIIpaXnOoEq6vhy3tjIbOddwX3w,4528
|
157
|
-
fprime_gds/flask/static/addons/commanding/arguments.js,sha256=
|
157
|
+
fprime_gds/flask/static/addons/commanding/arguments.js,sha256=6UYPwa7KW5nso-pBPs9n9TsGbEq9FUDBtG3eFectThY,14274
|
158
158
|
fprime_gds/flask/static/addons/commanding/command-history-template.js,sha256=2ak2B9eio3PLq6Bnie8iEsQN3HDJYokl0usMMP1D6lE,753
|
159
|
-
fprime_gds/flask/static/addons/commanding/command-history.js,sha256=
|
159
|
+
fprime_gds/flask/static/addons/commanding/command-history.js,sha256=rEJBsYCFtCXCQewlIUjW2ATG0vPVjQywsbZQ9u8iN0g,3995
|
160
160
|
fprime_gds/flask/static/addons/commanding/command-input-template.js,sha256=Z3fHmPTaAnXDhHMu07bRMBse6wjJSexAStgV9pSeh8Q,2959
|
161
|
-
fprime_gds/flask/static/addons/commanding/command-input.js,sha256=
|
162
|
-
fprime_gds/flask/static/addons/commanding/command-string-template.js,sha256=
|
163
|
-
fprime_gds/flask/static/addons/commanding/command-string.js,sha256=
|
161
|
+
fprime_gds/flask/static/addons/commanding/command-input.js,sha256=I2SZslctv7DyOmlmEhVsYRq6tPRAkWuNgikGDOmJn0o,9094
|
162
|
+
fprime_gds/flask/static/addons/commanding/command-string-template.js,sha256=tb0NJ24XtiEWaeY6e2MpNnb2uATRi7q7d2TrZEumvqw,825
|
163
|
+
fprime_gds/flask/static/addons/commanding/command-string.js,sha256=MAPG9Q0QyWTNxF1Urnxbz1AYC06p8mWPdThEZwpxFF8,2686
|
164
164
|
fprime_gds/flask/static/addons/dictionary/addon-templates.js,sha256=19hYvx_Gf8B5j4po0YKeQrhS2UbUIBAEfqgbDGE2peE,950
|
165
165
|
fprime_gds/flask/static/addons/dictionary/addon.js,sha256=eKggJNvOzj2ssXOenc7ccRUTRgeOJ5d4grWliaxgV40,1350
|
166
166
|
fprime_gds/flask/static/addons/image-display/addon.js,sha256=Uo1JzqsqF4be9B3DAPH-LIm2MhqwUAhvTvobigpZB0Y,1522
|
167
167
|
fprime_gds/flask/static/addons/image-display/dashboard.xml,sha256=t0g0v35YxV85MBhGSR_EkKBLRyglQ2Z7IQwfwvhuJAU,263
|
168
168
|
fprime_gds/flask/static/addons/sequencer/README.md,sha256=-9DULIhPIZIWNSHZmenqrPA-G4OAxmclHukoaHlgIeM,1681
|
169
169
|
fprime_gds/flask/static/addons/sequencer/addon-templates.js,sha256=jdZqBQBAKCwKsv60uWhF1Sd6A6WBeBcZ39yXAUlGpLE,3241
|
170
|
-
fprime_gds/flask/static/addons/sequencer/addon.js,sha256=
|
170
|
+
fprime_gds/flask/static/addons/sequencer/addon.js,sha256=FX3gkVtA7TuvvjmHNRAZ1wAfJvQ5qk4wtY0o3jYUetc,5188
|
171
171
|
fprime_gds/flask/static/addons/sequencer/autocomplete.js,sha256=O6qT8SjN7xuK0edaCpphENN_dLD8-ceRvE6lPNsZui8,6990
|
172
172
|
fprime_gds/flask/static/addons/sequencer/lint.js,sha256=QPt9bt0SMZxMu6cXFkqILwgLlcTmcO0WbRQE8X2XIbQ,1472
|
173
173
|
fprime_gds/flask/static/addons/sequencer/third/code-mirror.es.js,sha256=u-m1Nh7jnodMrnO1zR7eg0k7Lhwp8nt4nUwwJiKwyuU,813045
|
@@ -184,7 +184,8 @@ fprime_gds/flask/static/img/success.svg,sha256=wCfYG4cPfSCcsZ76JI4SwAJ-y62rahx9r
|
|
184
184
|
fprime_gds/flask/static/js/config.js,sha256=3CNrVmUtUGeiomAuoAE22w34r7wA-X0OtXN3JtLZYJ8,1066
|
185
185
|
fprime_gds/flask/static/js/datastore.js,sha256=mx0ZaUq-Nlb2LParYfnhu0WMUH-JGKKKgRQD_nOcZNE,15437
|
186
186
|
fprime_gds/flask/static/js/gds.js,sha256=OeDJrNmNA8hUPi8QIHP-s33MW_IYT3QIccxzL75MsjA,1297
|
187
|
-
fprime_gds/flask/static/js/
|
187
|
+
fprime_gds/flask/static/js/json.js,sha256=kfwzTqoyLu_Feqcp9WEhDWpZ3lziwHy9LGkOg8PCo6s,12429
|
188
|
+
fprime_gds/flask/static/js/loader.js,sha256=IbuJ7Jh70umyZog0iIupOuRGwc1tO5thT88jTW0XZc4,11414
|
188
189
|
fprime_gds/flask/static/js/performance.js,sha256=fGBbK5anf5UB9iAr6rO4u9sCkrQGlOHHQLm1b3nvrvg,5359
|
189
190
|
fprime_gds/flask/static/js/settings.js,sha256=Cfnn1ybZUOmsp48t_b23TDaHBRctvpD_h2ivBVKz6HM,1101
|
190
191
|
fprime_gds/flask/static/js/uploader.js,sha256=HdFUGwJ-SN_OEMXiXXJw0YblC0j_QiOA1y9yVWyS4SI,2730
|
@@ -227,10 +228,10 @@ fprime_gds/flask/static/third-party/webfonts/fa-solid-900.woff2,sha256=mDS4KtJuK
|
|
227
228
|
fprime_gds/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
228
229
|
fprime_gds/plugin/definitions.py,sha256=5rHGSOrr62qRNVfX9bZIo4HDAKG62lKteNum9G40y3g,2347
|
229
230
|
fprime_gds/plugin/system.py,sha256=uWd6DVW90Re0FoNMPNCx0cXXTJUdpgAAO0mtakzRNgk,8564
|
230
|
-
fprime_gds-3.
|
231
|
-
fprime_gds-3.
|
232
|
-
fprime_gds-3.
|
233
|
-
fprime_gds-3.
|
234
|
-
fprime_gds-3.
|
235
|
-
fprime_gds-3.
|
236
|
-
fprime_gds-3.
|
231
|
+
fprime_gds-3.6.1.dist-info/LICENSE.txt,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
232
|
+
fprime_gds-3.6.1.dist-info/METADATA,sha256=nxDoqKpYEs-Fv2wpXgiNUpmT1D0fjvGHsYgqjqJT7Hs,24770
|
233
|
+
fprime_gds-3.6.1.dist-info/NOTICE.txt,sha256=vXjA_xRcQhd83Vfk5D_vXg5kOjnnXvLuMi5vFKDEVmg,1612
|
234
|
+
fprime_gds-3.6.1.dist-info/WHEEL,sha256=nn6H5-ilmfVryoAQl3ZQ2l8SH5imPWFpm1A5FgEuFV4,91
|
235
|
+
fprime_gds-3.6.1.dist-info/entry_points.txt,sha256=oqUiO3xhJCR943jdU3zcxbqEvSXNeVgshk7dVaf_nGY,322
|
236
|
+
fprime_gds-3.6.1.dist-info/top_level.txt,sha256=6vzFLIX6ANfavKaXFHDMSLFtS94a6FaAsIWhjgYuSNE,27
|
237
|
+
fprime_gds-3.6.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|