openc3 5.1.1 → 5.2.0

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.
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