openc3 6.4.1 → 6.5.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +172 -97
  3. data/data/config/_graph_params.yaml +4 -4
  4. data/data/config/conversions.yaml +274 -0
  5. data/data/config/item_modifiers.yaml +8 -70
  6. data/data/config/parameter_modifiers.yaml +9 -69
  7. data/data/config/plugins.yaml +14 -1
  8. data/data/config/processors.yaml +51 -0
  9. data/data/config/telemetry_modifiers.yaml +1 -0
  10. data/lib/openc3/api/api.rb +1 -1
  11. data/lib/openc3/api/tlm_api.rb +10 -5
  12. data/lib/openc3/conversions/unix_time_conversion.rb +2 -2
  13. data/lib/openc3/conversions/unix_time_formatted_conversion.rb +3 -3
  14. data/lib/openc3/conversions/unix_time_seconds_conversion.rb +3 -3
  15. data/lib/openc3/core_ext/time.rb +2 -9
  16. data/lib/openc3/microservices/cleanup_microservice.rb +2 -2
  17. data/lib/openc3/microservices/decom_microservice.rb +2 -2
  18. data/lib/openc3/microservices/interface_microservice.rb +18 -12
  19. data/lib/openc3/microservices/log_microservice.rb +4 -2
  20. data/lib/openc3/microservices/multi_microservice.rb +2 -2
  21. data/lib/openc3/microservices/periodic_microservice.rb +2 -2
  22. data/lib/openc3/microservices/reducer_microservice.rb +2 -2
  23. data/lib/openc3/microservices/router_microservice.rb +2 -2
  24. data/lib/openc3/microservices/scope_cleanup_microservice.rb +2 -2
  25. data/lib/openc3/microservices/text_log_microservice.rb +19 -6
  26. data/lib/openc3/models/plugin_model.rb +5 -4
  27. data/lib/openc3/models/scope_model.rb +87 -57
  28. data/lib/openc3/models/script_engine_model.rb +93 -0
  29. data/lib/openc3/models/script_status_model.rb +26 -4
  30. data/lib/openc3/models/target_model.rb +33 -5
  31. data/lib/openc3/packets/packet_config.rb +1 -1
  32. data/lib/openc3/script/autonomic.rb +359 -0
  33. data/lib/openc3/script/script.rb +6 -1
  34. data/lib/openc3/script_engines/script_engine.rb +118 -0
  35. data/lib/openc3/topics/interface_topic.rb +23 -3
  36. data/lib/openc3/utilities/cli_generator.rb +42 -15
  37. data/lib/openc3/utilities/running_script.rb +1460 -0
  38. data/lib/openc3/version.rb +6 -6
  39. data/templates/conversion/conversion.py +1 -1
  40. data/templates/conversion/conversion.rb +1 -1
  41. data/templates/processor/processor.py +32 -0
  42. data/templates/processor/processor.rb +36 -0
  43. data/templates/tool_angular/package.json +2 -2
  44. data/templates/tool_react/package.json +1 -1
  45. data/templates/tool_svelte/package.json +1 -1
  46. data/templates/tool_vue/package.json +3 -3
  47. data/templates/widget/package.json +2 -2
  48. metadata +9 -1
@@ -37,36 +37,7 @@ READ_CONVERSION:
37
37
  takes extra parameters and must always implement the `call` method. The conversion
38
38
  factor is applied to the raw value in the telemetry packet before it is displayed
39
39
  to the user. The user still has the ability to see the raw unconverted value
40
- in a details dialog.
41
- ruby_example: |
42
- READ_CONVERSION the_great_conversion.rb 1000
43
-
44
- Defined in the_great_conversion.rb:
45
-
46
- require 'openc3/conversions/conversion'
47
- module OpenC3
48
- class TheGreatConversion < Conversion
49
- def initialize(multiplier)
50
- super()
51
- @multiplier = multiplier.to_f
52
- end
53
- def call(value, packet, buffer)
54
- return value * @multiplier
55
- end
56
- end
57
- end
58
- python_example: |
59
- READ_CONVERSION the_great_conversion.py 1000
60
-
61
- Defined in the_great_conversion.py:
62
-
63
- from openc3.conversions.conversion import Conversion
64
- class TheGreatConversion(Conversion):
65
- def __init__(self, multiplier):
66
- super().__init__()
67
- self.multiplier = float(multiplier)
68
- def call(self, value, packet, buffer):
69
- return value * self.multiplier
40
+ in a details dialog. For more information see the [Conversion](/docs/configuration/conversions) documentation.
70
41
  parameters:
71
42
  - name: Class Filename
72
43
  required: true
@@ -80,50 +51,16 @@ READ_CONVERSION:
80
51
  description: Additional parameter values for the conversion which are passed
81
52
  to the class constructor.
82
53
  values: .*
54
+ ruby_example: |
55
+ READ_CONVERSION ip_read_conversion.rb
56
+ python_example: |
57
+ READ_CONVERSION openc3/conversions/ip_read_conversion.rb
83
58
  POLY_READ_CONVERSION:
84
59
  summary: Adds a polynomial conversion factor to the current telemetry item
85
- description: The conversion factor is applied to raw value in the telemetry
86
- packet before it is displayed to the user. The user still has the ability
87
- to see the raw unconverted value in a details dialog.
88
- example: POLY_READ_CONVERSION 10 0.5 0.25
89
- parameters:
90
- - name: C0
91
- required: true
92
- description: Coefficient
93
- values: .*
94
- - name: Cx
95
- required: false
96
- description: Additional coefficient values for the conversion. Any order
97
- polynomial conversion may be used so the value of 'x' will vary with the
98
- order of the polynomial. Note that larger order polynomials take longer
99
- to process than shorter order polynomials, but are sometimes more accurate.
100
- values: .*
60
+ description: See [Polynomial Conversion](/docs/configuration/conversions#polynomial_conversion) for more information.
101
61
  SEG_POLY_READ_CONVERSION:
102
62
  summary: Adds a segmented polynomial conversion factor to the current telemetry item
103
- description: This conversion factor is applied to the raw value in the telemetry packet
104
- before it is displayed to the user. The user still has the ability to see the raw
105
- unconverted value in a details dialog.
106
- example: |
107
- SEG_POLY_READ_CONVERSION 0 10 0.5 0.25 # Apply the conversion to all values < 50
108
- SEG_POLY_READ_CONVERSION 50 11 0.5 0.275 # Apply the conversion to all values >= 50 and < 100
109
- SEG_POLY_READ_CONVERSION 100 12 0.5 0.3 # Apply the conversion to all values >= 100
110
- parameters:
111
- - name: Lower Bound
112
- required: true
113
- description: Defines the lower bound of the range of values that this segmented
114
- polynomial applies to. Is ignored for the segment with the smallest lower bound.
115
- values: .*
116
- - name: C0
117
- required: true
118
- description: Coefficient
119
- values: .*
120
- - name: Cx
121
- required: false
122
- description: Additional coefficient values for the conversion. Any order
123
- polynomial conversion may be used so the value of 'x' will vary with the
124
- order of the polynomial. Note that larger order polynomials take longer
125
- to process than shorter order polynomials, but are sometimes more accurate.
126
- values: .*
63
+ description: See [Segmented Polynomial Conversion](/docs/configuration/conversions#segmented_polynomial_conversion) for more information.
127
64
  GENERIC_READ_CONVERSION_START:
128
65
  summary: Start a generic read conversion
129
66
  description: Adds a generic conversion function to the current telemetry item.
@@ -225,6 +162,7 @@ LIMITS:
225
162
  values: .+
226
163
  LIMITS_RESPONSE:
227
164
  summary: Defines a response class that is called when the limits state of the current item changes
165
+ description: See the [Limits Response](/docs/configuration/limits-response) documentation for more information.
228
166
  ruby_example: LIMITS_RESPONSE example_limits_response.rb 10
229
167
  python_example: LIMITS_RESPONSE example_limits_response.py 10
230
168
  parameters:
@@ -69,7 +69,7 @@ WRITE_CONVERSION:
69
69
  It must implement the `initialize` (Ruby) or `__init__` (Python) method if it
70
70
  takes extra parameters and must always implement the `call` method. The conversion
71
71
  factor is applied to the value entered by the user before it is written into
72
- the binary command packet and sent.
72
+ the binary command packet and sent. For more information see the [Conversion](/docs/configuration/conversions) documentation.
73
73
 
74
74
  When applying a write_conversion sometimes the data type changes,
75
75
  e.g. creating a UINT from an input STRING (for an example of this see
@@ -90,90 +90,30 @@ WRITE_CONVERSION:
90
90
  given_values() method which can be used to retrieve a hash of the user provided
91
91
  values to the command. That can be used to check parameter values passed in.
92
92
  :::
93
- ruby_example: |
94
- WRITE_CONVERSION the_great_conversion.rb 1000
95
-
96
- Defined in the_great_conversion.rb:
97
-
98
- require 'openc3/conversions/conversion'
99
- module OpenC3
100
- class TheGreatConversion < Conversion
101
- def initialize(multiplier)
102
- super()
103
- @multiplier = multiplier.to_f
104
- end
105
- def call(value, packet, buffer)
106
- return value * multiplier
107
- end
108
- end
109
- end
110
- python_example: |
111
- WRITE_CONVERSION the_great_conversion.py 1000
112
-
113
- Defined in the_great_conversion.py:
114
-
115
- from openc3.conversions.conversion import Conversion
116
- class TheGreatConversion(Conversion):
117
- def __init__(self, multiplier):
118
- super().__init__()
119
- self.multiplier = float(multiplier)
120
- def call(self, value, packet, buffer):
121
- return value * self.multiplier
122
93
  parameters:
123
94
  - name: Class Filename
124
95
  required: true
125
96
  description: The filename which contains the Ruby or Python class. The filename must
126
97
  be named after the class such that the class is a CamelCase version of the
127
98
  underscored filename. For example, 'the_great_conversion.rb' should contain
128
- 'class TheGreatConversion'.
99
+ 'class TheGreatConversion'. Note the built-in Python conversions must specify
100
+ the full path to the file, e.g. 'openc3/conversions/bit_reverse_conversion.py'.
129
101
  values: .*
130
102
  - name: Parameter
131
103
  required: false
132
104
  description: Additional parameter values for the conversion which are passed
133
105
  to the class constructor.
134
106
  values: .*
107
+ ruby_example: |
108
+ WRITE_CONVERSION ip_write_conversion.rb
109
+ python_example: |
110
+ WRITE_CONVERSION openc3/conversions/ip_write_conversion.py
135
111
  POLY_WRITE_CONVERSION:
136
112
  summary: Adds a polynomial conversion factor to the current command parameter
137
- description: The conversion factor is applied to the value entered by the user
138
- before it is written into the binary command packet and sent.
139
- example: POLY_WRITE_CONVERSION 10 0.5 0.25
140
- parameters:
141
- - name: C0
142
- required: true
143
- description: Coefficient
144
- values: .*
145
- - name: Cx
146
- required: false
147
- description: Additional coefficient values for the conversion. Any order
148
- polynomial conversion may be used so the value of 'x' will vary with the
149
- order of the polynomial. Note that larger order polynomials take longer
150
- to process than shorter order polynomials, but are sometimes more accurate.
151
- values: .*
113
+ description: See [Polynomial Conversion](/docs/configuration/conversions#polynomial_conversion) for more information.
152
114
  SEG_POLY_WRITE_CONVERSION:
153
115
  summary: Adds a segmented polynomial conversion factor to the current command parameter
154
- description: This conversion factor is applied to the value entered by the user
155
- before it is written into the binary command packet and sent.
156
- example: |
157
- SEG_POLY_WRITE_CONVERSION 0 10 0.5 0.25 # Apply the conversion to all values < 50
158
- SEG_POLY_WRITE_CONVERSION 50 11 0.5 0.275 # Apply the conversion to all values >= 50 and < 100
159
- SEG_POLY_WRITE_CONVERSION 100 12 0.5 0.3 # Apply the conversion to all values >= 100
160
- parameters:
161
- - name: Lower Bound
162
- required: true
163
- description: Defines the lower bound of the range of values that this segmented
164
- polynomial applies to. Is ignored for the segment with the smallest lower bound.
165
- values: .*
166
- - name: C0
167
- required: true
168
- description: Coefficient
169
- values: .*
170
- - name: Cx
171
- required: false
172
- description: Additional coefficient values for the conversion. Any order
173
- polynomial conversion may be used so the value of 'x' will vary with the
174
- order of the polynomial. Note that larger order polynomials take longer
175
- to process than shorter order polynomials, but are sometimes more accurate.
176
- values: .*
116
+ description: See [Segmented Polynomial Conversion](/docs/configuration/conversions#segmented_polynomial_conversion) for more information.
177
117
  GENERIC_WRITE_CONVERSION_START:
178
118
  summary: Start a generic write conversion
179
119
  description: |
@@ -88,4 +88,17 @@ WIDGET:
88
88
  - name: Regex
89
89
  required: false
90
90
  description: Regex to match against filenames. If match, then no ERB processing
91
- values: .+
91
+ values: .+
92
+ SCRIPT_ENGINE:
93
+ summary: Define a script engine to add language support to Script Runner
94
+ example: SCRIPT_ENGINE .print print_script_engine.py
95
+ description: Defines a script engine to add language support to Script Runner
96
+ parameters:
97
+ - name: Extension
98
+ description: Extension that will use this script engine
99
+ required: true
100
+ values: .+
101
+ - name: Script Engine Filename
102
+ description: Filename that implements the script engine. Should be in top level lib folder in plugin.
103
+ required: true
104
+ values: .+
@@ -0,0 +1,51 @@
1
+ ---
2
+ WATERMARK_PROCESSOR:
3
+ summary: Calculates high and low values for a given item
4
+ description: |
5
+ Stores high and low values for a given item as HIGH_WATER and LOW_WATER.
6
+ Values are retrieved using a [ProcessorConversion](/docs/configuration/conversions#processor_conversion).
7
+ parameters:
8
+ - name: Item Name
9
+ description: The item name to calculate high and low values for
10
+ required: true
11
+ values: .+
12
+ - name: Value Type
13
+ required: false
14
+ description: The type of the value to display. Default is CONVERTED.
15
+ values: <%= %w(RAW CONVERTED) %>
16
+ ruby_example: |
17
+ PROCESSOR TEMP1WATER watermark_processor.rb TEMP1
18
+ ITEM TEMP1HIGH 0 0 DERIVED "High-water mark for TEMP1"
19
+ READ_CONVERSION processor_conversion.rb TEMP1WATER HIGH_WATER
20
+ python_example: |
21
+ PROCESSOR TEMP1WATER openc3/conversions/watermark_processor.py TEMP1
22
+ ITEM TEMP1HIGH 0 0 DERIVED "High-water mark for TEMP1"
23
+ READ_CONVERSION openc3/conversions/processor_conversion.py TEMP1WATER HIGH_WATER
24
+ STATISTICS_PROCESSOR:
25
+ summary: Calculates statistics for a given item
26
+ description: |
27
+ This processor calculates statistics for a given item as MIN, MAX, MEAN, and STDDEV
28
+ over a specified number of samples. Values are retrieved using a [ProcessorConversion](/docs/configuration/conversions#processor_conversion).
29
+ parameters:
30
+ - name: Item Name
31
+ description: The item name to calculate statistics for
32
+ required: true
33
+ values: .+
34
+ - name: Samples to Average
35
+ required: true
36
+ description: The number of samples to average for statistics
37
+ values: .*
38
+ - name: Value Type
39
+ required: false
40
+ description: The type of the value to display. Default is CONVERTED.
41
+ values: <%= %w(RAW CONVERTED) %>
42
+ ruby_example: PROCESSOR TEMP1STAT statistics_processor.rb TEMP1 100
43
+ python_example: PROCESSOR TEMP1STAT openc3/processors/statistics_processor.rb TEMP1 100
44
+ ruby_example: |
45
+ PROCESSOR TEMP1STAT statistics_processor.rb TEMP1 100
46
+ ITEM TEMP1STDDEV 0 0 DERIVED "Stddev of most recent 100 samples for TEMP1"
47
+ READ_CONVERSION processor_conversion.rb TEMP1STAT STDDEV FLOAT 64
48
+ python_example: |
49
+ PROCESSOR TEMP1STAT openc3/conversions/statistics_processor.py TEMP1 100
50
+ ITEM TEMP1STDDEV 0 0 DERIVED "Stddev of most recent 100 samples for TEMP1"
51
+ READ_CONVERSION openc3/conversions/processor_conversion.py TEMP1STAT STDDEV FLOAT 64
@@ -135,6 +135,7 @@ META:
135
135
  values: .*
136
136
  PROCESSOR:
137
137
  summary: Defines a processor class that executes code every time a packet is received
138
+ description: See the [Processor](/docs/configuration/processors) documentation for more information.
138
139
  ruby_example: PROCESSOR TEMP1HIGH watermark_processor.rb TEMP1
139
140
  python_example: PROCESSOR TEMP1HIGH watermark_processor.py TEMP1
140
141
  parameters:
@@ -38,7 +38,7 @@ require 'openc3/topics/topic'
38
38
  begin
39
39
  require 'openc3-enterprise/api/cmd_authority_api'
40
40
  rescue LoadError
41
- # LoadError expected for Open Source Edition
41
+ # LoadError expected for Core
42
42
  end
43
43
 
44
44
 
@@ -114,8 +114,8 @@ module OpenC3
114
114
  #
115
115
  # @param args [String|Array<String>] See the description for calling style
116
116
  # @param type [Symbol] Telemetry type, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
117
- def set_tlm(*args, type: :CONVERTED, manual: false, scope: $openc3_scope, token: $openc3_token)
118
- target_name, packet_name, item_name, value = _set_tlm_process_args(args, __method__, scope: scope)
117
+ def set_tlm(*args, type: :CONVERTED, manual: false, cache_timeout: nil, scope: $openc3_scope, token: $openc3_token)
118
+ target_name, packet_name, item_name, value = _set_tlm_process_args(args, __method__, cache_timeout: cache_timeout, scope: scope)
119
119
  authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, manual: manual, scope: scope, token: token)
120
120
  CvtModel.set_item(target_name, packet_name, item_name, value, type: type.intern, scope: scope)
121
121
  end
@@ -528,7 +528,7 @@ module OpenC3
528
528
  return [target_name, packet_name, item_name]
529
529
  end
530
530
 
531
- def _set_tlm_process_args(args, method_name, scope: $openc3_scope, token: $openc3_token)
531
+ def _set_tlm_process_args(args, method_name, cache_timeout: nil, scope: $openc3_scope, token: $openc3_token)
532
532
  case args.length
533
533
  when 1
534
534
  target_name, packet_name, item_name, value = extract_fields_from_set_tlm_text(args[0])
@@ -544,8 +544,13 @@ module OpenC3
544
544
  target_name = target_name.upcase
545
545
  packet_name = packet_name.upcase
546
546
  item_name = item_name.upcase
547
- # Determine if this item exists, it will raise appropriate errors if not
548
- TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
547
+
548
+ if packet_name == 'LATEST'
549
+ packet_name = CvtModel.determine_latest_packet_for_item(target_name, item_name, cache_timeout: cache_timeout, scope: scope)
550
+ else
551
+ # Determine if this item exists, it will raise appropriate errors if not
552
+ TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
553
+ end
549
554
 
550
555
  return [target_name, packet_name, item_name, value]
551
556
  end
@@ -14,7 +14,7 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2022, OpenC3, Inc.
17
+ # All changes Copyright 2025, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -35,7 +35,7 @@ module OpenC3
35
35
  super()
36
36
  @seconds_item_name = seconds_item_name
37
37
  @microseconds_item_name = microseconds_item_name
38
- @converted_type = :RUBY_TIME
38
+ @converted_type = :TIME
39
39
  @converted_bit_size = 0
40
40
  @seconds_type = seconds_type.to_sym
41
41
  @microseconds_type = microseconds_type.to_sym
@@ -14,7 +14,7 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2024, OpenC3, Inc.
17
+ # All changes Copyright 2025, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -32,9 +32,9 @@ module OpenC3
32
32
  # represents the number of seconds since the UNIX time epoch
33
33
  # @param microseconds_item_name [String] The telemetry item in the packet
34
34
  # which represents microseconds
35
- def initialize(seconds_item_name, microseconds_item_name = nil)
35
+ def initialize(seconds_item_name, microseconds_item_name = nil, seconds_type = 'RAW', microseconds_type = 'RAW')
36
36
  # @params is set by the parent class in super()
37
- super(seconds_item_name, microseconds_item_name)
37
+ super(seconds_item_name, microseconds_item_name, seconds_type, microseconds_type)
38
38
  @converted_type = :STRING
39
39
  @converted_bit_size = 0
40
40
  end
@@ -14,7 +14,7 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2024, OpenC3, Inc.
17
+ # All changes Copyright 2025, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -32,9 +32,9 @@ module OpenC3
32
32
  # represents the number of seconds since the UNIX time epoch
33
33
  # @param microseconds_item_name [String] The telemetry item in the packet
34
34
  # which represents microseconds
35
- def initialize(seconds_item_name, microseconds_item_name = nil)
35
+ def initialize(seconds_item_name, microseconds_item_name = nil, seconds_type = 'RAW', microseconds_type = 'RAW')
36
36
  # @params is set by the parent class in super()
37
- super(seconds_item_name, microseconds_item_name)
37
+ super(seconds_item_name, microseconds_item_name, seconds_type, microseconds_type)
38
38
  @converted_type = :FLOAT
39
39
  @converted_bit_size = 64
40
40
  end
@@ -249,19 +249,12 @@ class Time
249
249
  Time.total_seconds(self.hour, self.min, self.sec, self.usec)
250
250
  end
251
251
 
252
- # @return [String] Date formatted as YYYY/MM/DD HH:MM:SS.US UTC_OFFSET
253
- def formatted(include_year = true, fractional_digits = 3, include_utc_offset = false)
252
+ # @return [String] Date formatted as YYYY/MM/DD HH:MM:SS.US
253
+ def formatted(include_year = true, fractional_digits = 3)
254
254
  str = ""
255
255
  str << "%Y/%m/%d " if include_year
256
256
  str << "%H:%M:%S"
257
257
  str << ".%#{fractional_digits}N" if fractional_digits > 0
258
- if include_utc_offset
259
- if self.utc?
260
- str << " UTC"
261
- else
262
- str << " %z"
263
- end
264
- end
265
258
  self.strftime(str)
266
259
  end
267
260
 
@@ -102,6 +102,6 @@ end
102
102
 
103
103
  if __FILE__ == $0
104
104
  OpenC3::CleanupMicroservice.run
105
- ThreadManager.instance.shutdown
106
- ThreadManager.instance.join
105
+ OpenC3::ThreadManager.instance.shutdown
106
+ OpenC3::ThreadManager.instance.join
107
107
  end
@@ -254,6 +254,6 @@ end
254
254
 
255
255
  if __FILE__ == $0
256
256
  OpenC3::DecomMicroservice.run
257
- ThreadManager.instance.shutdown
258
- ThreadManager.instance.join
257
+ OpenC3::ThreadManager.instance.shutdown
258
+ OpenC3::ThreadManager.instance.join
259
259
  end
@@ -128,15 +128,21 @@ module OpenC3
128
128
  end
129
129
  if msg_hash['raw']
130
130
  if @interface.connected?
131
- @logger.info "#{@interface.name}: Write raw"
132
- # A raw interface write results in an UNKNOWN packet
133
- command = System.commands.packet('UNKNOWN', 'UNKNOWN')
134
- command.received_count = TargetModel.increment_command_count('UNKNOWN', 'UNKNOWN', 1, scope: @scope)
135
- command = command.clone
136
- command.buffer = msg_hash['raw']
137
- command.received_time = Time.now
138
- CommandTopic.write_packet(command, scope: @scope)
139
- @interface.write_raw(msg_hash['raw'])
131
+ begin
132
+ @logger.info "#{@interface.name}: Write raw"
133
+ # A raw interface write results in an UNKNOWN packet
134
+ command = System.commands.packet('UNKNOWN', 'UNKNOWN')
135
+ command.received_count = TargetModel.increment_command_count('UNKNOWN', 'UNKNOWN', 1, scope: @scope)
136
+ command = command.clone
137
+ command.buffer = msg_hash['raw']
138
+ command.received_time = Time.now
139
+ CommandTopic.write_packet(command, scope: @scope)
140
+ @logger.info("write_raw sent #{msg_hash['raw'].length} bytes to #{@interface.name}", scope: @scope)
141
+ @interface.write_raw(msg_hash['raw'])
142
+ rescue => e
143
+ @logger.error "#{@interface.name}: write_raw: #{e.formatted}"
144
+ next e.message
145
+ end
140
146
  next 'SUCCESS'
141
147
  else
142
148
  next "Interface not connected: #{@interface.name}"
@@ -824,7 +830,7 @@ module OpenC3
824
830
  end
825
831
 
826
832
  def sync_tlm_packet_counts(packet)
827
- if @sync_packet_count_delay_seconds <= 0
833
+ if @sync_packet_count_delay_seconds <= 0 or $openc3_redis_cluster
828
834
  # Perfect but slow method
829
835
  packet.received_count = TargetModel.increment_telemetry_count(packet.target_name, packet.packet_name, 1, scope: @scope)
830
836
  else
@@ -880,6 +886,6 @@ end
880
886
 
881
887
  if __FILE__ == $0
882
888
  OpenC3::InterfaceMicroservice.run
883
- ThreadManager.instance.shutdown
884
- ThreadManager.instance.join
889
+ OpenC3::ThreadManager.instance.shutdown
890
+ OpenC3::ThreadManager.instance.join
885
891
  end
@@ -55,6 +55,8 @@ module OpenC3
55
55
  @cycle_size = 50_000_000 unless @cycle_size # ~50 MB
56
56
 
57
57
  @buffer_depth = DEFAULT_BUFFER_DEPTH unless @buffer_depth
58
+ @plws = {}
59
+
58
60
  @error_count = 0
59
61
  @metric.set(name: 'log_total', value: @count, type: 'counter')
60
62
  @metric.set(name: 'log_error_total', value: @error_count, type: 'counter')
@@ -144,6 +146,6 @@ end
144
146
 
145
147
  if __FILE__ == $0
146
148
  OpenC3::LogMicroservice.run
147
- ThreadManager.instance.shutdown
148
- ThreadManager.instance.join
149
+ OpenC3::ThreadManager.instance.shutdown
150
+ OpenC3::ThreadManager.instance.join
149
151
  end
@@ -53,6 +53,6 @@ module OpenC3
53
53
  end
54
54
  if __FILE__ == $0
55
55
  OpenC3::MultiMicroservice.run
56
- ThreadManager.instance.shutdown
57
- ThreadManager.instance.join
56
+ OpenC3::ThreadManager.instance.shutdown
57
+ OpenC3::ThreadManager.instance.join
58
58
  end
@@ -91,6 +91,6 @@ end
91
91
 
92
92
  if __FILE__ == $0
93
93
  OpenC3::PeriodicMicroservice.run
94
- ThreadManager.instance.shutdown
95
- ThreadManager.instance.join
94
+ OpenC3::ThreadManager.instance.shutdown
95
+ OpenC3::ThreadManager.instance.join
96
96
  end
@@ -635,6 +635,6 @@ end
635
635
 
636
636
  if __FILE__ == $0
637
637
  OpenC3::ReducerMicroservice.run
638
- ThreadManager.instance.shutdown
639
- ThreadManager.instance.join
638
+ OpenC3::ThreadManager.instance.shutdown
639
+ OpenC3::ThreadManager.instance.join
640
640
  end
@@ -88,6 +88,6 @@ end
88
88
 
89
89
  if __FILE__ == $0
90
90
  OpenC3::RouterMicroservice.run
91
- ThreadManager.instance.shutdown
92
- ThreadManager.instance.join
91
+ OpenC3::ThreadManager.instance.shutdown
92
+ OpenC3::ThreadManager.instance.join
93
93
  end
@@ -40,6 +40,6 @@ end
40
40
 
41
41
  if __FILE__ == $0
42
42
  OpenC3::ScopeCleanupMicroservice.run
43
- ThreadManager.instance.shutdown
44
- ThreadManager.instance.join
43
+ OpenC3::ThreadManager.instance.shutdown
44
+ OpenC3::ThreadManager.instance.join
45
45
  end
@@ -43,6 +43,7 @@ module OpenC3
43
43
  @cycle_time = 600 unless @cycle_time # 10 minutes
44
44
  @cycle_size = 50_000_000 unless @cycle_size # ~50 MB
45
45
 
46
+ @tlws = {}
46
47
  @error_count = 0
47
48
  @metric.set(name: 'text_log_total', value: @count, type: 'counter')
48
49
  @metric.set(name: 'text_error_total', value: @error_count, type: 'counter')
@@ -50,15 +51,27 @@ module OpenC3
50
51
 
51
52
  def run
52
53
  setup_tlws()
54
+
55
+ individual_topics = []
56
+ @topics.each do |topic|
57
+ individual_topics << [topic]
58
+ end
59
+
53
60
  while true
54
61
  break if @cancel_thread
55
62
 
56
- Topic.read_topics(@topics) do |topic, msg_id, msg_hash, redis|
63
+ # Read each topic separately to support multiple redis shards
64
+ # This is needed with Redis cluster because the topics will likely be on different shards and we don't use {} in the topic names
65
+ individual_topics.each do |individual_topic|
57
66
  break if @cancel_thread
67
+ # 500ms timeout - To support completing within 1 second with two topics (DEFAULT and NOSCOPE)
68
+ Topic.read_topics(individual_topic, nil, 500) do |topic, msg_id, msg_hash, redis|
69
+ break if @cancel_thread
58
70
 
59
- log_data(topic, msg_id, msg_hash, redis)
60
- @count += 1
61
- @metric.set(name: 'text_log_total', value: @count, type: 'counter')
71
+ log_data(topic, msg_id, msg_hash, redis)
72
+ @count += 1
73
+ @metric.set(name: 'text_log_total', value: @count, type: 'counter')
74
+ end
62
75
  end
63
76
  end
64
77
  end
@@ -104,6 +117,6 @@ end
104
117
 
105
118
  if __FILE__ == $0
106
119
  OpenC3::TextLogMicroservice.run
107
- ThreadManager.instance.shutdown
108
- ThreadManager.instance.join
120
+ OpenC3::ThreadManager.instance.shutdown
121
+ OpenC3::ThreadManager.instance.join
109
122
  end
@@ -31,6 +31,7 @@ require 'openc3/models/gem_model'
31
31
  require 'openc3/models/target_model'
32
32
  require 'openc3/models/interface_model'
33
33
  require 'openc3/models/router_model'
34
+ require 'openc3/models/script_engine_model'
34
35
  require 'openc3/models/tool_model'
35
36
  require 'openc3/models/widget_model'
36
37
  require 'openc3/models/microservice_model'
@@ -249,7 +250,7 @@ module OpenC3
249
250
  case keyword
250
251
  when 'VARIABLE', 'NEEDS_DEPENDENCIES'
251
252
  # Ignore during phase 2
252
- when 'TARGET', 'INTERFACE', 'ROUTER', 'MICROSERVICE', 'TOOL', 'WIDGET'
253
+ when 'TARGET', 'INTERFACE', 'ROUTER', 'MICROSERVICE', 'TOOL', 'WIDGET', 'SCRIPT_ENGINE'
253
254
  begin
254
255
  if current_model
255
256
  current_model.create unless validate_only
@@ -260,7 +261,7 @@ module OpenC3
260
261
  # Otherwise we're stuck constantly iterating on the last model
261
262
  ensure
262
263
  current_model = nil
263
- current_model = OpenC3.const_get((keyword.capitalize + 'Model').intern).handle_config(parser,
264
+ current_model = OpenC3.const_get((keyword.split('_').collect(&:capitalize).join + 'Model').intern).handle_config(parser,
264
265
  keyword, params, plugin: plugin_model.name, needs_dependencies: needs_dependencies, scope: scope)
265
266
  end
266
267
  else
@@ -339,7 +340,7 @@ module OpenC3
339
340
  sleep 15 if microservice_count > 0 # Cycle time 5s times 2 plus 5s wait for soft stop and then hard stop
340
341
  # Remove all the other models now that the processes have stopped
341
342
  # Save TargetModel for last as it has the most to cleanup
342
- [InterfaceModel, RouterModel, ToolModel, WidgetModel, TargetModel].each do |model|
343
+ [InterfaceModel, RouterModel, ToolModel, WidgetModel, TargetModel, ScriptEngineModel].each do |model|
343
344
  model.find_all_by_plugin(plugin: @name, scope: @scope).each do |_name, model_instance|
344
345
  begin
345
346
  model_instance.destroy
@@ -369,7 +370,7 @@ module OpenC3
369
370
  ensure
370
371
  # Double check everything is gone
371
372
  found = []
372
- [MicroserviceModel, InterfaceModel, RouterModel, ToolModel, WidgetModel, TargetModel].each do |model|
373
+ [MicroserviceModel, InterfaceModel, RouterModel, ToolModel, WidgetModel, TargetModel, ScriptEngineModel].each do |model|
373
374
  model.find_all_by_plugin(plugin: @name, scope: @scope).each do |_name, model_instance|
374
375
  found << model_instance
375
376
  end