openc3 5.1.1 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +48 -9
  3. data/data/config/interface_modifiers.yaml +14 -0
  4. data/data/config/parameter_modifiers.yaml +5 -3
  5. data/data/config/screen.yaml +12 -8
  6. data/data/config/target.yaml +33 -0
  7. data/ext/openc3/ext/config_parser/config_parser.c +66 -63
  8. data/ext/openc3/ext/packet/packet.c +1 -4
  9. data/lib/openc3/api/README.md +5 -0
  10. data/lib/openc3/api/api.rb +3 -1
  11. data/lib/openc3/api/cmd_api.rb +43 -112
  12. data/lib/openc3/api/interface_api.rb +3 -3
  13. data/lib/openc3/api/offline_access_api.rb +78 -0
  14. data/lib/openc3/api/settings_api.rb +3 -1
  15. data/lib/openc3/api/stash_api.rb +63 -0
  16. data/lib/openc3/api/target_api.rb +4 -5
  17. data/lib/openc3/config/config_parser.rb +47 -47
  18. data/lib/openc3/interfaces/interface.rb +11 -1
  19. data/lib/openc3/interfaces/protocols/burst_protocol.rb +30 -16
  20. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +8 -2
  21. data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +2 -2
  22. data/lib/openc3/interfaces/protocols/override_protocol.rb +2 -2
  23. data/lib/openc3/interfaces/tcpip_server_interface.rb +3 -1
  24. data/lib/openc3/io/json_api_object.rb +30 -9
  25. data/lib/openc3/io/json_drb.rb +6 -1
  26. data/lib/openc3/io/json_drb_object.rb +18 -9
  27. data/lib/openc3/io/json_rpc.rb +5 -3
  28. data/lib/openc3/logs/buffered_packet_log_writer.rb +1 -1
  29. data/lib/openc3/logs/log_writer.rb +8 -2
  30. data/lib/openc3/microservices/cleanup_microservice.rb +3 -3
  31. data/lib/openc3/microservices/decom_microservice.rb +8 -8
  32. data/lib/openc3/microservices/interface_microservice.rb +86 -71
  33. data/lib/openc3/microservices/log_microservice.rb +5 -3
  34. data/lib/openc3/microservices/microservice.rb +18 -14
  35. data/lib/openc3/microservices/multi_microservice.rb +62 -0
  36. data/lib/openc3/microservices/periodic_microservice.rb +58 -0
  37. data/lib/openc3/microservices/reaction_microservice.rb +61 -47
  38. data/lib/openc3/microservices/reducer_microservice.rb +64 -40
  39. data/lib/openc3/microservices/router_microservice.rb +4 -4
  40. data/lib/openc3/microservices/text_log_microservice.rb +2 -2
  41. data/lib/openc3/microservices/timeline_microservice.rb +44 -30
  42. data/lib/openc3/microservices/trigger_group_microservice.rb +39 -36
  43. data/lib/openc3/migrations/20221202214600_add_target_names.rb +30 -0
  44. data/lib/openc3/migrations/20221210174900_convert_to_multi.rb +65 -0
  45. data/lib/openc3/models/cvt_model.rb +1 -1
  46. data/lib/openc3/models/gem_model.rb +24 -20
  47. data/lib/openc3/models/interface_model.rb +69 -35
  48. data/lib/openc3/models/metadata_model.rb +1 -1
  49. data/lib/openc3/models/microservice_model.rb +7 -24
  50. data/lib/openc3/models/migration_model.rb +52 -0
  51. data/lib/openc3/models/model.rb +2 -7
  52. data/lib/openc3/models/note_model.rb +1 -1
  53. data/lib/openc3/models/offline_access_model.rb +55 -0
  54. data/lib/openc3/models/plugin_model.rb +12 -3
  55. data/lib/openc3/models/reaction_model.rb +6 -2
  56. data/lib/openc3/models/scope_model.rb +89 -13
  57. data/lib/openc3/models/settings_model.rb +1 -1
  58. data/lib/openc3/models/stash_model.rb +53 -0
  59. data/lib/openc3/models/target_model.rb +301 -130
  60. data/lib/openc3/models/tool_model.rb +1 -12
  61. data/lib/openc3/models/widget_model.rb +1 -6
  62. data/lib/openc3/operators/microservice_operator.rb +45 -6
  63. data/lib/openc3/operators/operator.rb +27 -5
  64. data/lib/openc3/packets/commands.rb +1 -25
  65. data/lib/openc3/packets/limits.rb +0 -75
  66. data/lib/openc3/packets/packet.rb +0 -28
  67. data/lib/openc3/packets/packet_item.rb +23 -0
  68. data/lib/openc3/packets/packet_item_limits.rb +2 -2
  69. data/lib/openc3/packets/parsers/state_parser.rb +10 -6
  70. data/lib/openc3/packets/telemetry.rb +1 -45
  71. data/lib/openc3/script/commands.rb +41 -71
  72. data/lib/openc3/script/extract.rb +15 -1
  73. data/lib/openc3/script/{calendar.rb → metadata.rb} +42 -17
  74. data/lib/openc3/script/script.rb +13 -5
  75. data/lib/openc3/script/storage.rb +3 -1
  76. data/lib/openc3/system/system.rb +19 -17
  77. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +4 -4
  78. data/lib/openc3/top_level.rb +3 -3
  79. data/lib/openc3/topics/command_decom_topic.rb +2 -2
  80. data/lib/openc3/topics/command_topic.rb +7 -6
  81. data/lib/openc3/topics/interface_topic.rb +2 -2
  82. data/lib/openc3/topics/router_topic.rb +1 -1
  83. data/lib/openc3/topics/telemetry_topic.rb +2 -1
  84. data/lib/openc3/utilities/authentication.rb +35 -14
  85. data/lib/openc3/utilities/aws_bucket.rb +4 -3
  86. data/lib/openc3/utilities/bucket.rb +4 -2
  87. data/lib/openc3/utilities/bucket_file_cache.rb +3 -8
  88. data/lib/openc3/utilities/bucket_utilities.rb +77 -15
  89. data/lib/openc3/utilities/local_mode.rb +12 -9
  90. data/lib/openc3/utilities/logger.rb +17 -9
  91. data/lib/openc3/utilities/message_log.rb +6 -5
  92. data/lib/openc3/utilities/migration.rb +22 -0
  93. data/lib/openc3/utilities/store_autoload.rb +7 -5
  94. data/lib/openc3/utilities/target_file.rb +9 -7
  95. data/lib/openc3/version.rb +6 -6
  96. data/lib/openc3.rb +2 -1
  97. metadata +14 -3
@@ -17,12 +17,13 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'openc3/models/target_model'
24
24
  require 'openc3/topics/command_topic'
25
25
  require 'openc3/topics/interface_topic'
26
+ require 'openc3/script/extract'
26
27
 
27
28
  module OpenC3
28
29
  module Api
@@ -49,134 +50,48 @@ module OpenC3
49
50
  'get_cmd_cnts',
50
51
  ])
51
52
 
52
- # Send a command packet to a target.
53
+ # The following methods send a command packet to a target. The 'raw' version of the equivalent
54
+ # command methods do not perform command parameter conversions.
53
55
  #
54
56
  # Accepts two different calling styles:
55
57
  # cmd("TGT CMD with PARAM1 val, PARAM2 val")
56
58
  # cmd('TGT','CMD','PARAM1'=>val,'PARAM2'=>val)
57
59
  #
58
60
  # Favor the first syntax where possible as it is more succinct.
59
- #
60
- # @param args [String|Array<String>] See the description for calling style
61
- # @return [Array<String, String, Hash>] target_name, command_name, parameters
62
- def cmd(*args, scope: $openc3_scope, token: $openc3_token, **kwargs)
63
- args << kwargs unless kwargs.empty?
64
- cmd_implementation(true, true, false, 'cmd', *args, scope: scope, token: token)
61
+ def cmd(*args, **kwargs)
62
+ cmd_implementation('cmd', *args, range_check: true, hazardous_check: true, raw: false, **kwargs)
63
+ end
64
+ def cmd_raw(*args, **kwargs)
65
+ cmd_implementation('cmd_raw', *args, range_check: true, hazardous_check: true, raw: true, **kwargs)
65
66
  end
66
67
 
67
68
  # Send a command packet to a target without performing any value range
68
69
  # checks on the parameters. Useful for testing to allow sending command
69
70
  # parameters outside the allowable range as defined in the configuration.
70
- #
71
- # Accepts two different calling styles:
72
- # cmd_no_range_check("TGT CMD with PARAM1 val, PARAM2 val")
73
- # cmd_no_range_check('TGT','CMD','PARAM1'=>val,'PARAM2'=>val)
74
- #
75
- # Favor the first syntax where possible as it is more succinct.
76
- #
77
- # @param (see #cmd)
78
- # @return (see #cmd)
79
- def cmd_no_range_check(*args, scope: $openc3_scope, token: $openc3_token, **kwargs)
80
- args << kwargs unless kwargs.empty?
81
- cmd_implementation(false, true, false, 'cmd_no_range_check', *args, scope: scope, token: token)
71
+ def cmd_no_range_check(*args, **kwargs)
72
+ cmd_implementation('cmd_no_range_check', *args, range_check: false, hazardous_check: true, raw: false, **kwargs)
73
+ end
74
+ def cmd_raw_no_range_check(*args, **kwargs)
75
+ cmd_implementation('cmd_raw_no_range_check', *args, range_check: false, hazardous_check: true, raw: true, **kwargs)
82
76
  end
83
77
 
84
78
  # Send a command packet to a target without performing any hazardous checks
85
79
  # both on the command itself and its parameters. Useful in scripts to
86
80
  # prevent popping up warnings to the user.
87
- #
88
- # Accepts two different calling styles:
89
- # cmd_no_hazardous_check("TGT CMD with PARAM1 val, PARAM2 val")
90
- # cmd_no_hazardous_check('TGT','CMD','PARAM1'=>val,'PARAM2'=>val)
91
- #
92
- # Favor the first syntax where possible as it is more succinct.
93
- #
94
- # @param (see #cmd)
95
- # @return (see #cmd)
96
- def cmd_no_hazardous_check(*args, scope: $openc3_scope, token: $openc3_token, **kwargs)
97
- args << kwargs unless kwargs.empty?
98
- cmd_implementation(true, false, false, 'cmd_no_hazardous_check', *args, scope: scope, token: token)
81
+ def cmd_no_hazardous_check(*args, **kwargs)
82
+ cmd_implementation('cmd_no_hazardous_check', *args, range_check: true, hazardous_check: false, raw: false, **kwargs)
99
83
  end
100
-
101
- # Send a command packet to a target without performing any value range
102
- # checks or hazardous checks both on the command itself and its parameters.
103
- #
104
- # Accepts two different calling styles:
105
- # cmd_no_checks("TGT CMD with PARAM1 val, PARAM2 val")
106
- # cmd_no_checks('TGT','CMD','PARAM1'=>val,'PARAM2'=>val)
107
- #
108
- # Favor the first syntax where possible as it is more succinct.
109
- #
110
- # @param (see #cmd)
111
- # @return (see #cmd)
112
- def cmd_no_checks(*args, scope: $openc3_scope, token: $openc3_token, **kwargs)
113
- args << kwargs unless kwargs.empty?
114
- cmd_implementation(false, false, false, 'cmd_no_checks', *args, scope: scope, token: token)
115
- end
116
-
117
- # Send a command packet to a target without running conversions.
118
- #
119
- # Accepts two different calling styles:
120
- # cmd_raw("TGT CMD with PARAM1 val, PARAM2 val")
121
- # cmd_raw('TGT','CMD','PARAM1'=>val,'PARAM2'=>val)
122
- #
123
- # Favor the first syntax where possible as it is more succinct.
124
- #
125
- # @param args [String|Array<String>] See the description for calling style
126
- # @return [Array<String, String, Hash>] target_name, command_name, parameters
127
- def cmd_raw(*args, scope: $openc3_scope, token: $openc3_token, **kwargs)
128
- args << kwargs unless kwargs.empty?
129
- cmd_implementation(true, true, true, 'cmd_raw', *args, scope: scope, token: token)
84
+ def cmd_raw_no_hazardous_check(*args, **kwargs)
85
+ cmd_implementation('cmd_raw_no_hazardous_check', *args, range_check: true, hazardous_check: false, raw: true, **kwargs)
130
86
  end
131
87
 
132
88
  # Send a command packet to a target without performing any value range
133
- # checks on the parameters or running conversions. Useful for testing to allow sending command
134
- # parameters outside the allowable range as defined in the configuration.
135
- #
136
- # Accepts two different calling styles:
137
- # cmd_raw_no_range_check("TGT CMD with PARAM1 val, PARAM2 val")
138
- # cmd_raw_no_range_check('TGT','CMD','PARAM1'=>val,'PARAM2'=>val)
139
- #
140
- # Favor the first syntax where possible as it is more succinct.
141
- #
142
- # @param (see #cmd)
143
- # @return (see #cmd)
144
- def cmd_raw_no_range_check(*args, scope: $openc3_scope, token: $openc3_token, **kwargs)
145
- args << kwargs unless kwargs.empty?
146
- cmd_implementation(false, true, true, 'cmd_raw_no_range_check', *args, scope: scope, token: token)
147
- end
148
-
149
- # Send a command packet to a target without running conversions or performing any hazardous checks
150
- # both on the command itself and its parameters. Useful in scripts to
151
- # prevent popping up warnings to the user.
152
- #
153
- # Accepts two different calling styles:
154
- # cmd_raw_no_hazardous_check("TGT CMD with PARAM1 val, PARAM2 val")
155
- # cmd_raw_no_hazardous_check('TGT','CMD','PARAM1'=>val,'PARAM2'=>val)
156
- #
157
- # Favor the first syntax where possible as it is more succinct.
158
- #
159
- # @param (see #cmd)
160
- # @return (see #cmd)
161
- def cmd_raw_no_hazardous_check(*args, scope: $openc3_scope, token: $openc3_token, **kwargs)
162
- args << kwargs unless kwargs.empty?
163
- cmd_implementation(true, false, true, 'cmd_raw_no_hazardous_check', *args, scope: scope, token: token)
164
- end
165
-
166
- # Send a command packet to a target without running conversions or performing any value range
167
89
  # checks or hazardous checks both on the command itself and its parameters.
168
- #
169
- # Accepts two different calling styles:
170
- # cmd_raw_no_checks("TGT CMD with PARAM1 val, PARAM2 val")
171
- # cmd_raw_no_checks('TGT','CMD','PARAM1'=>val,'PARAM2'=>val)
172
- #
173
- # Favor the first syntax where possible as it is more succinct.
174
- #
175
- # @param (see #cmd)
176
- # @return (see #cmd)
177
- def cmd_raw_no_checks(*args, scope: $openc3_scope, token: $openc3_token, **kwargs)
178
- args << kwargs unless kwargs.empty?
179
- cmd_implementation(false, false, true, 'cmd_raw_no_checks', *args, scope: scope, token: token)
90
+ def cmd_no_checks(*args, **kwargs)
91
+ cmd_implementation('cmd_no_checks', *args, range_check: false, hazardous_check: false, raw: false, **kwargs)
92
+ end
93
+ def cmd_raw_no_checks(*args, **kwargs)
94
+ cmd_implementation('cmd_raw_no_checks', *args, range_check: false, hazardous_check: false, raw: true, **kwargs)
180
95
  end
181
96
 
182
97
  # Send a raw binary string to the specified interface.
@@ -258,7 +173,7 @@ module OpenC3
258
173
  # @param args [String|Array<String>] See the description for calling style
259
174
  # @return [Boolean] Whether the command is hazardous
260
175
  def get_cmd_hazardous(*args, scope: $openc3_scope, token: $openc3_token, **kwargs)
261
- args << kwargs unless kwargs.empty?
176
+ extract_string_kwargs_to_args(args, kwargs)
262
177
  case args.length
263
178
  when 1
264
179
  target_name, command_name, params = extract_fields_from_cmd_text(args[0], scope: scope)
@@ -375,7 +290,10 @@ module OpenC3
375
290
  # PRIVATE implementation details
376
291
  ###########################################################################
377
292
 
378
- def cmd_implementation(range_check, hazardous_check, raw, method_name, *args, scope:, token:)
293
+ def cmd_implementation(method_name, *args, range_check:, hazardous_check:, raw:, timeout: nil,
294
+ scope: $openc3_scope, token: $openc3_token, **kwargs)
295
+ extract_string_kwargs_to_args(args, kwargs)
296
+
379
297
  case args.length
380
298
  when 1
381
299
  target_name, cmd_name, cmd_params = extract_fields_from_cmd_text(args[0], scope: scope)
@@ -402,8 +320,21 @@ module OpenC3
402
320
  'hazardous_check' => hazardous_check.to_s,
403
321
  'raw' => raw.to_s
404
322
  }
405
- Logger.info(build_cmd_output_string(target_name, cmd_name, cmd_params, packet, raw), scope: scope) if !packet["messages_disabled"]
406
- CommandTopic.send_command(command, scope: scope)
323
+ log_cmd = true
324
+ if packet["messages_disabled"]
325
+ log_cmd = false
326
+ else
327
+ cmd_params.each do |key, value|
328
+ item = packet['items'].find { |item| item['name'] == key.to_s }
329
+ if item['states'] && item['states'][value] && item['states'][value]["messages_disabled"]
330
+ log_cmd = false
331
+ end
332
+ end
333
+ end
334
+ if log_cmd
335
+ Logger.info(build_cmd_output_string(target_name, cmd_name, cmd_params, packet, raw), scope: scope)
336
+ end
337
+ CommandTopic.send_command(command, timeout: timeout, scope: scope)
407
338
  end
408
339
 
409
340
  def build_cmd_output_string(target_name, cmd_name, cmd_params, packet, raw)
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'openc3/models/interface_model'
@@ -125,7 +125,7 @@ module OpenC3
125
125
  #
126
126
  # @param target_name [String/Array] The name of the target(s)
127
127
  # @param interface_name (see #connect_interface)
128
- def map_target_to_interface(target_name, interface_name, scope: $openc3_scope, token: $openc3_token)
128
+ def map_target_to_interface(target_name, interface_name, cmd_only: false, tlm_only: false, unmap_old: true, scope: $openc3_scope, token: $openc3_token)
129
129
  authorize(permission: 'system_set', interface_name: interface_name, scope: scope, token: token)
130
130
  new_interface = InterfaceModel.get_model(name: interface_name, scope: scope)
131
131
  if Array === target_name
@@ -134,7 +134,7 @@ module OpenC3
134
134
  target_names = [target_name]
135
135
  end
136
136
  target_names.each do |name|
137
- new_interface.map_target(name)
137
+ new_interface.map_target(name, cmd_only: cmd_only, tlm_only: tlm_only, unmap_old: unmap_old)
138
138
  Logger.info("Target #{name} mapped to Interface #{interface_name}", scope: scope)
139
139
  end
140
140
  nil
@@ -0,0 +1,78 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 OpenC3, Inc
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is free software; you can modify and/or redistribute it
7
+ # under the terms of the GNU Affero General Public License
8
+ # as published by the Free Software Foundation; version 3 with
9
+ # attribution addendums as found in the LICENSE.txt
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+
16
+ # This file may also be used under the terms of a commercial license
17
+ # if purchased from OpenC3, Inc.
18
+
19
+ require 'openc3/models/offline_access_model'
20
+ require 'openc3/utilities/authentication'
21
+
22
+ module OpenC3
23
+ module Api
24
+ WHITELIST ||= []
25
+ WHITELIST.concat([
26
+ 'offline_access_needed',
27
+ 'set_offline_access'
28
+ ])
29
+
30
+ def offline_access_needed(scope: $openc3_scope, token: $openc3_token)
31
+ authorize(permission: 'system', scope: scope, token: token)
32
+ begin
33
+ authorize(permission: 'script_run', scope: scope, token: token)
34
+ rescue
35
+ # Not needed if can't run scripts
36
+ return false
37
+ end
38
+ info = user_info(token)
39
+ if info['roles'].to_s.include?("offline_access")
40
+ username = info['username']
41
+ if username and username != ''
42
+ model = OfflineAccessModel.get_model(name: username, scope: scope)
43
+ if model and model.offline_access_token
44
+ auth = OpenC3KeycloakAuthentication.new(ENV['OPENC3_KEYCLOAK_URL'])
45
+ valid_token = auth.get_token_from_refresh_token(model.offline_access_token)
46
+ if valid_token
47
+ return false
48
+ else
49
+ model.offline_access_token = nil
50
+ model.update
51
+ return true
52
+ end
53
+ end
54
+ return true
55
+ else
56
+ return false
57
+ end
58
+ else
59
+ return false
60
+ end
61
+ end
62
+
63
+ def set_offline_access(offline_access_token, scope: $openc3_scope, token: $openc3_token)
64
+ authorize(permission: 'script_run', scope: scope, token: token)
65
+ info = user_info(token)
66
+ username = info['username']
67
+ raise "Invalid username" if not username or username == ''
68
+ model = OfflineAccessModel.get_model(name: username, scope: scope)
69
+ if model
70
+ model.offline_access_token = offline_access_token
71
+ model.update
72
+ else
73
+ model = OfflineAccessModel.new(name: username, offline_access_token: offline_access_token, scope: scope)
74
+ model.create
75
+ end
76
+ end
77
+ end
78
+ end
@@ -17,9 +17,11 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
+ require 'openc3/models/settings_model'
24
+
23
25
  module OpenC3
24
26
  module Api
25
27
  WHITELIST ||= []
@@ -0,0 +1,63 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 OpenC3, Inc
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is free software; you can modify and/or redistribute it
7
+ # under the terms of the GNU Affero General Public License
8
+ # as published by the Free Software Foundation; version 3 with
9
+ # attribution addendums as found in the LICENSE.txt
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+
16
+ # This file may also be used under the terms of a commercial license
17
+ # if purchased from OpenC3, Inc.
18
+
19
+ require 'openc3/models/stash_model'
20
+
21
+ module OpenC3
22
+ module Api
23
+ WHITELIST ||= []
24
+ WHITELIST.concat([
25
+ 'stash_set',
26
+ 'stash_get',
27
+ 'stash_all',
28
+ 'stash_keys',
29
+ 'stash_delete'
30
+ ])
31
+
32
+ def stash_set(key, value, scope: $openc3_scope, token: $openc3_token)
33
+ authorize(permission: 'script_run', scope: scope, token: token)
34
+ StashModel.set( {name: key, value: JSON.generate(value.as_json(:allow_nan => true)) }, scope: scope)
35
+ end
36
+
37
+ def stash_get(key, scope: $openc3_scope, token: $openc3_token)
38
+ authorize(permission: 'script_view', scope: scope, token: token)
39
+ result = StashModel.get(name: key, scope: scope)
40
+ if result
41
+ JSON.parse(result['value'], :allow_nan => true, :create_additions => true)
42
+ else
43
+ nil
44
+ end
45
+ end
46
+
47
+ def stash_all(scope: $openc3_scope, token: $openc3_token)
48
+ authorize(permission: 'script_view', scope: scope, token: token)
49
+ StashModel.all(scope: scope).transform_values { |hash| JSON.parse(hash["value"], :allow_nan => true, :create_additions => true) }
50
+ end
51
+
52
+ def stash_keys(scope: $openc3_scope, token: $openc3_token)
53
+ authorize(permission: 'script_view', scope: scope, token: token)
54
+ StashModel.names(scope: scope)
55
+ end
56
+
57
+ def stash_delete(key, scope: $openc3_scope, token: $openc3_token)
58
+ authorize(permission: 'script_run', scope: scope, token: token)
59
+ model = StashModel.get_model(name: key, scope: scope)
60
+ model.destroy if model
61
+ end
62
+ end
63
+ end
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'openc3/models/target_model'
@@ -66,14 +66,13 @@ module OpenC3
66
66
  packets.each do |packet|
67
67
  tlm_cnt += Topic.get_cnt("#{scope}__TELEMETRY__{#{target_name}}__#{packet['packet_name']}")
68
68
  end
69
- interface_name = ''
69
+ interface_names = []
70
70
  InterfaceModel.all(scope: scope).each do |name, interface|
71
71
  if interface['target_names'].include? target_name
72
- interface_name = interface['name']
73
- break
72
+ interface_names << interface['name']
74
73
  end
75
74
  end
76
- info << [target_name, interface_name, cmd_cnt, tlm_cnt]
75
+ info << [target_name, interface_names.join(","), cmd_cnt, tlm_cnt]
77
76
  end
78
77
  info
79
78
  end
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'openc3/top_level'
@@ -449,12 +449,11 @@ module OpenC3
449
449
  if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
450
450
  # Iterates over each line of the io object and yields the keyword and parameters
451
451
  def parse_loop(io, yield_non_keyword_lines, remove_quotes, size, rx)
452
- line_continuation = false
453
-
452
+ string_concat = false
454
453
  @line_number = 0
455
454
  @keyword = nil
456
455
  @parameters = []
457
- @line = nil
456
+ @line = ''
458
457
 
459
458
  while true
460
459
  @line_number += 1
@@ -470,37 +469,57 @@ module OpenC3
470
469
  end
471
470
 
472
471
  line.strip!
473
- data = line.scan(rx)
474
- first_item = data[0].to_s
472
+ # Ensure the line length is not 0
473
+ next if line.length == 0
475
474
 
476
- if line_continuation
477
- @line << line
478
- # Carry over keyword and parameters
479
- else
480
- @line = line
481
- if (first_item.length == 0) || (first_item[0] == '#')
482
- @keyword = nil
483
- else
484
- @keyword = first_item.upcase
475
+ if string_concat
476
+ # Skip comment lines after a string concatenation
477
+ if (line[0] == '#')
478
+ next
485
479
  end
486
- @parameters = []
480
+ # Remove the opening quote if we're continuing the line
481
+ line = line[1..-1]
487
482
  end
488
483
 
489
- # Ignore comments and blank lines
490
- if @keyword.nil?
491
- if (yield_non_keyword_lines) && (!line_continuation)
492
- yield(@keyword, @parameters)
493
- end
484
+ # Check for string continuation
485
+ case line[-1]
486
+ when '+', '\\' # String concatenation
487
+ newline = line[-1] == '+'
488
+ # Trim off the concat character plus any spaces, e.g. "line" \
489
+ trim = line[0..-2].strip()
490
+ # Now trim off the last quote so it will flow into the next line
491
+ @line += trim[0..-2]
492
+ @line += "\n" if newline
493
+ string_concat = true
494
+ next
495
+ when '&' # Line continuation
496
+ @line += line[0..-2]
494
497
  next
498
+ else
499
+ @line += line
500
+ end
501
+ string_concat = false
502
+
503
+ data = @line.scan(rx)
504
+ first_item = ''
505
+ if data.length > 0
506
+ first_item += data[0]
495
507
  end
496
508
 
497
- if line_continuation
498
- if remove_quotes
499
- @parameters << first_item.remove_quotes
500
- else
501
- @parameters << first_item
509
+ if (first_item.length == 0) || (first_item[0] == '#')
510
+ @keyword = nil
511
+ else
512
+ @keyword = first_item.upcase
513
+ end
514
+ @parameters = []
515
+
516
+ # Ignore lines without keywords: comments and blank lines
517
+ if @keyword.nil?
518
+ if yield_non_keyword_lines
519
+ yield(@keyword, @parameters)
502
520
  end
503
- line_continuation = false
521
+ @line = ''
522
+ next
504
523
  end
505
524
 
506
525
  length = data.length
@@ -518,13 +537,6 @@ module OpenC3
518
537
  end
519
538
  end
520
539
 
521
- # If the string is simply '&' and its the last string then its a line continuation so break the loop
522
- if (string.length == 1) && (string[0] == '&') && (index == (length - 1))
523
- line_continuation = true
524
- next
525
- end
526
-
527
- line_continuation = false
528
540
  if remove_quotes
529
541
  @parameters << string.remove_quotes
530
542
  else
@@ -533,20 +545,8 @@ module OpenC3
533
545
  end
534
546
  end
535
547
 
536
- # If we detected a line continuation while going through all the
537
- # strings on the line then we strip off the continuation character and
538
- # return to the top of the loop to continue processing the line.
539
- if line_continuation
540
- # Strip the continuation character
541
- if @line.length >= 1
542
- @line = @line[0..-2]
543
- else
544
- @line = ""
545
- end
546
- next
547
- end
548
-
549
548
  yield(@keyword, @parameters)
549
+ @line = ''
550
550
  end
551
551
 
552
552
  @@progress_callback.call(1.0) if @@progress_callback
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'openc3/api/api'
@@ -38,6 +38,12 @@ module OpenC3
38
38
  # @return [Array<String>] Array of target names associated with this interface
39
39
  attr_accessor :target_names
40
40
 
41
+ # @return [Array<String>] Array of cmd target names associated with this interface
42
+ attr_accessor :cmd_target_names
43
+
44
+ # @return [Array<String>] Array of tlm target names associated with this interface
45
+ attr_accessor :tlm_target_names
46
+
41
47
  # @return [Boolean] Flag indicating if the interface should be connected
42
48
  # to on startup
43
49
  attr_accessor :connect_on_startup
@@ -133,6 +139,8 @@ module OpenC3
133
139
  @name = self.class.to_s.split("::")[-1] # Remove namespacing if present
134
140
  @state = 'DISCONNECTED'
135
141
  @target_names = []
142
+ @cmd_target_names = []
143
+ @tlm_target_names = []
136
144
  @connect_on_startup = true
137
145
  @auto_reconnect = true
138
146
  @reconnect_delay = 5.0
@@ -381,6 +389,8 @@ module OpenC3
381
389
  def copy_to(other_interface)
382
390
  other_interface.name = self.name.clone
383
391
  other_interface.target_names = self.target_names.clone
392
+ other_interface.cmd_target_names = self.cmd_target_names.clone
393
+ other_interface.tlm_target_names = self.tlm_target_names.clone
384
394
  other_interface.connect_on_startup = self.connect_on_startup
385
395
  other_interface.auto_reconnect = self.auto_reconnect
386
396
  other_interface.reconnect_delay = self.reconnect_delay
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'openc3/config/config_parser'
@@ -55,30 +55,44 @@ module OpenC3
55
55
  # creating a Packet. It can discard a set number of bytes at the beginning
56
56
  # before creating the Packet.
57
57
  #
58
+ # Note: On the first call to this from any interface read(), data will contain a blank
59
+ # string. Blank string is an opportunity for protocols to return any queued up packets.
60
+ # If they have no queued up packets, they should pass the blank string down to chained
61
+ # protocols giving them the same opportunity.
62
+ #
58
63
  # @return [String|nil] Data for a packet consisting of the bytes read
59
64
  def read_data(data)
60
65
  @data << data
61
66
 
62
- control = handle_sync_pattern()
63
- return control if control and data.length > 0
67
+ while true
68
+ control = handle_sync_pattern()
69
+ return control if control and data.length > 0 # Only return here if not blank string test
64
70
 
65
- # Reduce the data to a single packet
66
- packet_data = reduce_to_single_packet()
71
+ # Reduce the data to a single packet
72
+ packet_data = reduce_to_single_packet()
73
+ if packet_data == :RESYNC
74
+ @sync_state = :SEARCHING
75
+ next if data.length > 0 # Only immediately resync if not blank string test
76
+ end
67
77
 
68
- # Potentially allow blank string to be sent to other protocols if no packet is ready in this one
69
- if Symbol === packet_data
70
- if (data.length <= 0) and packet_data == :STOP
71
- return super(data)
72
- else
73
- return packet_data
78
+ # Potentially allow blank string to be sent to other protocols if no packet is ready in this one
79
+ if Symbol === packet_data
80
+ if (data.length <= 0) and packet_data != :DISCONNECT
81
+ # On blank string test, return blank string (unless we had a packet or need disconnect)
82
+ # The base class handles the special case of returning STOP if on the last protocol in the
83
+ # chain
84
+ return super(data)
85
+ else
86
+ return packet_data # Return any control code if not on blank string test
87
+ end
74
88
  end
75
- end
76
89
 
77
- @sync_state = :SEARCHING
90
+ @sync_state = :SEARCHING
78
91
 
79
- # Discard leading bytes if necessary
80
- packet_data.replace(packet_data[@discard_leading_bytes..-1]) if @discard_leading_bytes > 0
81
- packet_data
92
+ # Discard leading bytes if necessary
93
+ packet_data.replace(packet_data[@discard_leading_bytes..-1]) if @discard_leading_bytes > 0
94
+ return packet_data
95
+ end
82
96
  end
83
97
 
84
98
  # Called to perform modifications on a command packet before it is sent