openc3 5.0.6

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 (307) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +18 -0
  3. data/Guardfile +35 -0
  4. data/LICENSE.txt +727 -0
  5. data/README.md +37 -0
  6. data/Rakefile +131 -0
  7. data/bin/cstol_converter +1178 -0
  8. data/bin/openc3cli +531 -0
  9. data/bin/rubysloc +139 -0
  10. data/data/config/_array_params.yaml +23 -0
  11. data/data/config/_id_items.yaml +24 -0
  12. data/data/config/_id_params.yaml +58 -0
  13. data/data/config/_interfaces.yaml +214 -0
  14. data/data/config/_interfaces.yaml.err +1017 -0
  15. data/data/config/_items.yaml +20 -0
  16. data/data/config/_params.yaml +60 -0
  17. data/data/config/cmd_tlm_server.yaml +136 -0
  18. data/data/config/command.yaml +44 -0
  19. data/data/config/command_modifiers.yaml +160 -0
  20. data/data/config/command_telemetry.yaml +3 -0
  21. data/data/config/interface_modifiers.yaml +104 -0
  22. data/data/config/item_modifiers.yaml +221 -0
  23. data/data/config/microservice.yaml +78 -0
  24. data/data/config/param_item_modifiers.yaml +52 -0
  25. data/data/config/parameter_modifiers.yaml +200 -0
  26. data/data/config/plugins.yaml +80 -0
  27. data/data/config/protocols.yaml +290 -0
  28. data/data/config/screen.yaml +147 -0
  29. data/data/config/table_manager.yaml +89 -0
  30. data/data/config/table_parameter_modifiers.yaml +9 -0
  31. data/data/config/target.yaml +142 -0
  32. data/data/config/target_config.yaml +94 -0
  33. data/data/config/telemetry.yaml +87 -0
  34. data/data/config/telemetry_modifiers.yaml +159 -0
  35. data/data/config/tool.yaml +63 -0
  36. data/data/config/unknown.yaml +3 -0
  37. data/data/config/widgets.yaml +1505 -0
  38. data/ext/mkrf_conf.rb +49 -0
  39. data/ext/openc3/ext/array/array.c +122 -0
  40. data/ext/openc3/ext/array/extconf.rb +13 -0
  41. data/ext/openc3/ext/buffered_file/buffered_file.c +198 -0
  42. data/ext/openc3/ext/buffered_file/extconf.rb +13 -0
  43. data/ext/openc3/ext/config_parser/config_parser.c +280 -0
  44. data/ext/openc3/ext/config_parser/extconf.rb +13 -0
  45. data/ext/openc3/ext/crc/crc.c +351 -0
  46. data/ext/openc3/ext/crc/extconf.rb +13 -0
  47. data/ext/openc3/ext/openc3_io/extconf.rb +13 -0
  48. data/ext/openc3/ext/openc3_io/openc3_io.c +158 -0
  49. data/ext/openc3/ext/packet/extconf.rb +13 -0
  50. data/ext/openc3/ext/packet/packet.c +318 -0
  51. data/ext/openc3/ext/platform/extconf.rb +13 -0
  52. data/ext/openc3/ext/platform/platform.c +134 -0
  53. data/ext/openc3/ext/polynomial_conversion/extconf.rb +13 -0
  54. data/ext/openc3/ext/polynomial_conversion/polynomial_conversion.c +79 -0
  55. data/ext/openc3/ext/string/extconf.rb +13 -0
  56. data/ext/openc3/ext/string/string.c +63 -0
  57. data/ext/openc3/ext/structure/structure.c +1719 -0
  58. data/ext/openc3/ext/tabbed_plots_config/extconf.rb +13 -0
  59. data/ext/openc3/ext/tabbed_plots_config/tabbed_plots_config.c +62 -0
  60. data/ext/openc3/ext/telemetry/extconf.rb +13 -0
  61. data/ext/openc3/ext/telemetry/telemetry.c +336 -0
  62. data/lib/cosmos.rb +20 -0
  63. data/lib/cosmosc2.rb +20 -0
  64. data/lib/openc3/api/api.rb +39 -0
  65. data/lib/openc3/api/authorized_api.rb +30 -0
  66. data/lib/openc3/api/cmd_api.rb +451 -0
  67. data/lib/openc3/api/config_api.rb +58 -0
  68. data/lib/openc3/api/interface_api.rb +117 -0
  69. data/lib/openc3/api/limits_api.rb +375 -0
  70. data/lib/openc3/api/router_api.rb +117 -0
  71. data/lib/openc3/api/settings_api.rb +70 -0
  72. data/lib/openc3/api/target_api.rb +78 -0
  73. data/lib/openc3/api/tlm_api.rb +455 -0
  74. data/lib/openc3/bridge/bridge.rb +54 -0
  75. data/lib/openc3/bridge/bridge_config.rb +167 -0
  76. data/lib/openc3/bridge/bridge_interface_thread.rb +42 -0
  77. data/lib/openc3/bridge/bridge_router_thread.rb +42 -0
  78. data/lib/openc3/ccsds/ccsds_packet.rb +68 -0
  79. data/lib/openc3/ccsds/ccsds_parser.rb +148 -0
  80. data/lib/openc3/config/config_parser.rb +549 -0
  81. data/lib/openc3/config/meta_config_parser.rb +74 -0
  82. data/lib/openc3/conversions/conversion.rb +70 -0
  83. data/lib/openc3/conversions/generic_conversion.rb +83 -0
  84. data/lib/openc3/conversions/packet_time_formatted_conversion.rb +43 -0
  85. data/lib/openc3/conversions/packet_time_seconds_conversion.rb +43 -0
  86. data/lib/openc3/conversions/polynomial_conversion.rb +87 -0
  87. data/lib/openc3/conversions/processor_conversion.rb +70 -0
  88. data/lib/openc3/conversions/received_count_conversion.rb +38 -0
  89. data/lib/openc3/conversions/received_time_formatted_conversion.rb +42 -0
  90. data/lib/openc3/conversions/received_time_seconds_conversion.rb +42 -0
  91. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +171 -0
  92. data/lib/openc3/conversions/unix_time_conversion.rb +68 -0
  93. data/lib/openc3/conversions/unix_time_formatted_conversion.rb +49 -0
  94. data/lib/openc3/conversions/unix_time_seconds_conversion.rb +49 -0
  95. data/lib/openc3/conversions.rb +34 -0
  96. data/lib/openc3/core_ext/array.rb +416 -0
  97. data/lib/openc3/core_ext/binding.rb +29 -0
  98. data/lib/openc3/core_ext/class.rb +72 -0
  99. data/lib/openc3/core_ext/exception.rb +61 -0
  100. data/lib/openc3/core_ext/file.rb +83 -0
  101. data/lib/openc3/core_ext/hash.rb +37 -0
  102. data/lib/openc3/core_ext/io.rb +134 -0
  103. data/lib/openc3/core_ext/kernel.rb +42 -0
  104. data/lib/openc3/core_ext/math.rb +128 -0
  105. data/lib/openc3/core_ext/matrix.rb +156 -0
  106. data/lib/openc3/core_ext/objectspace.rb +36 -0
  107. data/lib/openc3/core_ext/openc3_io.rb +57 -0
  108. data/lib/openc3/core_ext/range.rb +27 -0
  109. data/lib/openc3/core_ext/socket.rb +38 -0
  110. data/lib/openc3/core_ext/string.rb +389 -0
  111. data/lib/openc3/core_ext/stringio.rb +33 -0
  112. data/lib/openc3/core_ext/time.rb +508 -0
  113. data/lib/openc3/core_ext.rb +36 -0
  114. data/lib/openc3/interfaces/interface.rb +498 -0
  115. data/lib/openc3/interfaces/linc_interface.rb +475 -0
  116. data/lib/openc3/interfaces/protocols/burst_protocol.rb +192 -0
  117. data/lib/openc3/interfaces/protocols/crc_protocol.rb +193 -0
  118. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +155 -0
  119. data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +56 -0
  120. data/lib/openc3/interfaces/protocols/length_protocol.rb +165 -0
  121. data/lib/openc3/interfaces/protocols/override_protocol.rb +60 -0
  122. data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +206 -0
  123. data/lib/openc3/interfaces/protocols/protocol.rb +82 -0
  124. data/lib/openc3/interfaces/protocols/template_protocol.rb +261 -0
  125. data/lib/openc3/interfaces/protocols/terminated_protocol.rb +93 -0
  126. data/lib/openc3/interfaces/serial_interface.rb +94 -0
  127. data/lib/openc3/interfaces/simulated_target_interface.rb +168 -0
  128. data/lib/openc3/interfaces/stream_interface.rb +81 -0
  129. data/lib/openc3/interfaces/tcpip_client_interface.rb +69 -0
  130. data/lib/openc3/interfaces/tcpip_server_interface.rb +629 -0
  131. data/lib/openc3/interfaces/udp_interface.rb +169 -0
  132. data/lib/openc3/interfaces.rb +44 -0
  133. data/lib/openc3/io/buffered_file.rb +109 -0
  134. data/lib/openc3/io/io_multiplexer.rb +80 -0
  135. data/lib/openc3/io/json_api_object.rb +208 -0
  136. data/lib/openc3/io/json_drb.rb +335 -0
  137. data/lib/openc3/io/json_drb_object.rb +114 -0
  138. data/lib/openc3/io/json_drb_rack.rb +84 -0
  139. data/lib/openc3/io/json_rpc.rb +420 -0
  140. data/lib/openc3/io/openc3_snmp.rb +58 -0
  141. data/lib/openc3/io/posix_serial_driver.rb +156 -0
  142. data/lib/openc3/io/raw_logger.rb +167 -0
  143. data/lib/openc3/io/raw_logger_pair.rb +77 -0
  144. data/lib/openc3/io/serial_driver.rb +105 -0
  145. data/lib/openc3/io/stderr.rb +43 -0
  146. data/lib/openc3/io/stdout.rb +43 -0
  147. data/lib/openc3/io/udp_sockets.rb +194 -0
  148. data/lib/openc3/io/win32_serial_driver.rb +196 -0
  149. data/lib/openc3/logs/log_writer.rb +302 -0
  150. data/lib/openc3/logs/packet_log_constants.rb +62 -0
  151. data/lib/openc3/logs/packet_log_reader.rb +345 -0
  152. data/lib/openc3/logs/packet_log_writer.rb +299 -0
  153. data/lib/openc3/logs/text_log_writer.rb +68 -0
  154. data/lib/openc3/logs.rb +25 -0
  155. data/lib/openc3/microservices/cleanup_microservice.rb +68 -0
  156. data/lib/openc3/microservices/decom_microservice.rb +136 -0
  157. data/lib/openc3/microservices/interface_microservice.rb +532 -0
  158. data/lib/openc3/microservices/log_microservice.rb +108 -0
  159. data/lib/openc3/microservices/microservice.rb +204 -0
  160. data/lib/openc3/microservices/plugin_microservice.rb +43 -0
  161. data/lib/openc3/microservices/reaction_microservice.rb +541 -0
  162. data/lib/openc3/microservices/reducer_microservice.rb +313 -0
  163. data/lib/openc3/microservices/router_microservice.rb +44 -0
  164. data/lib/openc3/microservices/text_log_microservice.rb +84 -0
  165. data/lib/openc3/microservices/timeline_microservice.rb +363 -0
  166. data/lib/openc3/microservices/trigger_group_microservice.rb +638 -0
  167. data/lib/openc3/models/activity_model.rb +319 -0
  168. data/lib/openc3/models/auth_model.rb +65 -0
  169. data/lib/openc3/models/cvt_model.rb +185 -0
  170. data/lib/openc3/models/environment_model.rb +58 -0
  171. data/lib/openc3/models/gem_model.rb +137 -0
  172. data/lib/openc3/models/info_model.rb +31 -0
  173. data/lib/openc3/models/interface_model.rb +281 -0
  174. data/lib/openc3/models/interface_status_model.rb +117 -0
  175. data/lib/openc3/models/metadata_model.rb +139 -0
  176. data/lib/openc3/models/metric_model.rb +59 -0
  177. data/lib/openc3/models/microservice_model.rb +206 -0
  178. data/lib/openc3/models/microservice_status_model.rb +74 -0
  179. data/lib/openc3/models/model.rb +204 -0
  180. data/lib/openc3/models/note_model.rb +122 -0
  181. data/lib/openc3/models/notification_model.rb +40 -0
  182. data/lib/openc3/models/ping_model.rb +35 -0
  183. data/lib/openc3/models/plugin_model.rb +292 -0
  184. data/lib/openc3/models/process_status_model.rb +76 -0
  185. data/lib/openc3/models/reaction_model.rb +322 -0
  186. data/lib/openc3/models/reducer_model.rb +65 -0
  187. data/lib/openc3/models/router_model.rb +35 -0
  188. data/lib/openc3/models/router_status_model.rb +27 -0
  189. data/lib/openc3/models/scope_model.rb +153 -0
  190. data/lib/openc3/models/settings_model.rb +55 -0
  191. data/lib/openc3/models/sorted_model.rb +167 -0
  192. data/lib/openc3/models/target_model.rb +759 -0
  193. data/lib/openc3/models/timeline_model.rb +154 -0
  194. data/lib/openc3/models/tool_config_model.rb +38 -0
  195. data/lib/openc3/models/tool_model.rb +262 -0
  196. data/lib/openc3/models/trigger_group_model.rb +186 -0
  197. data/lib/openc3/models/trigger_model.rb +330 -0
  198. data/lib/openc3/models/widget_model.rb +138 -0
  199. data/lib/openc3/operators/microservice_operator.rb +128 -0
  200. data/lib/openc3/operators/operator.rb +277 -0
  201. data/lib/openc3/packets/binary_accessor.rb +1207 -0
  202. data/lib/openc3/packets/commands.rb +373 -0
  203. data/lib/openc3/packets/json_packet.rb +134 -0
  204. data/lib/openc3/packets/limits.rb +271 -0
  205. data/lib/openc3/packets/limits_response.rb +53 -0
  206. data/lib/openc3/packets/packet.rb +1168 -0
  207. data/lib/openc3/packets/packet_config.rb +625 -0
  208. data/lib/openc3/packets/packet_item.rb +586 -0
  209. data/lib/openc3/packets/packet_item_limits.rb +162 -0
  210. data/lib/openc3/packets/parsers/format_string_parser.rb +65 -0
  211. data/lib/openc3/packets/parsers/limits_parser.rb +159 -0
  212. data/lib/openc3/packets/parsers/limits_response_parser.rb +61 -0
  213. data/lib/openc3/packets/parsers/packet_item_parser.rb +272 -0
  214. data/lib/openc3/packets/parsers/packet_parser.rb +134 -0
  215. data/lib/openc3/packets/parsers/processor_parser.rb +73 -0
  216. data/lib/openc3/packets/parsers/state_parser.rb +127 -0
  217. data/lib/openc3/packets/parsers/xtce_converter.rb +442 -0
  218. data/lib/openc3/packets/parsers/xtce_parser.rb +722 -0
  219. data/lib/openc3/packets/structure.rb +553 -0
  220. data/lib/openc3/packets/structure_item.rb +365 -0
  221. data/lib/openc3/packets/telemetry.rb +487 -0
  222. data/lib/openc3/processors/processor.rb +86 -0
  223. data/lib/openc3/processors/statistics_processor.rb +82 -0
  224. data/lib/openc3/processors/watermark_processor.rb +58 -0
  225. data/lib/openc3/processors.rb +24 -0
  226. data/lib/openc3/script/api_shared.rb +828 -0
  227. data/lib/openc3/script/calendar.rb +89 -0
  228. data/lib/openc3/script/commands.rb +227 -0
  229. data/lib/openc3/script/exceptions.rb +29 -0
  230. data/lib/openc3/script/extract.rb +161 -0
  231. data/lib/openc3/script/limits.rb +60 -0
  232. data/lib/openc3/script/script.rb +299 -0
  233. data/lib/openc3/script/script_runner.rb +238 -0
  234. data/lib/openc3/script/storage.rb +146 -0
  235. data/lib/openc3/script/suite.rb +542 -0
  236. data/lib/openc3/script/suite_results.rb +196 -0
  237. data/lib/openc3/script/suite_runner.rb +217 -0
  238. data/lib/openc3/script.rb +21 -0
  239. data/lib/openc3/streams/serial_stream.rb +167 -0
  240. data/lib/openc3/streams/stream.rb +63 -0
  241. data/lib/openc3/streams/tcpip_client_stream.rb +116 -0
  242. data/lib/openc3/streams/tcpip_socket_stream.rb +195 -0
  243. data/lib/openc3/system/system.rb +127 -0
  244. data/lib/openc3/system/system_config.rb +411 -0
  245. data/lib/openc3/system/target.rb +269 -0
  246. data/lib/openc3/system.rb +24 -0
  247. data/lib/openc3/tools/cmd_tlm_server/api.rb +20 -0
  248. data/lib/openc3/tools/cmd_tlm_server/cmd_tlm_server_config.rb +320 -0
  249. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +294 -0
  250. data/lib/openc3/tools/table_manager/table.rb +77 -0
  251. data/lib/openc3/tools/table_manager/table_config.rb +273 -0
  252. data/lib/openc3/tools/table_manager/table_item.rb +90 -0
  253. data/lib/openc3/tools/table_manager/table_item_parser.rb +66 -0
  254. data/lib/openc3/tools/table_manager/table_manager_core.rb +333 -0
  255. data/lib/openc3/tools/table_manager/table_parser.rb +93 -0
  256. data/lib/openc3/tools/test_runner/test.rb +67 -0
  257. data/lib/openc3/top_level.rb +595 -0
  258. data/lib/openc3/topics/autonomic_topic.rb +52 -0
  259. data/lib/openc3/topics/calendar_topic.rb +44 -0
  260. data/lib/openc3/topics/command_decom_topic.rb +76 -0
  261. data/lib/openc3/topics/command_topic.rb +83 -0
  262. data/lib/openc3/topics/config_topic.rb +68 -0
  263. data/lib/openc3/topics/interface_topic.rb +73 -0
  264. data/lib/openc3/topics/limits_event_topic.rb +109 -0
  265. data/lib/openc3/topics/notifications_topic.rb +28 -0
  266. data/lib/openc3/topics/router_topic.rb +85 -0
  267. data/lib/openc3/topics/telemetry_decom_topic.rb +54 -0
  268. data/lib/openc3/topics/telemetry_topic.rb +36 -0
  269. data/lib/openc3/topics/timeline_topic.rb +45 -0
  270. data/lib/openc3/topics/topic.rb +53 -0
  271. data/lib/openc3/utilities/authentication.rb +141 -0
  272. data/lib/openc3/utilities/authorization.rb +51 -0
  273. data/lib/openc3/utilities/crc.rb +278 -0
  274. data/lib/openc3/utilities/csv.rb +153 -0
  275. data/lib/openc3/utilities/logger.rb +187 -0
  276. data/lib/openc3/utilities/message_log.rb +91 -0
  277. data/lib/openc3/utilities/metric.rb +141 -0
  278. data/lib/openc3/utilities/process_manager.rb +139 -0
  279. data/lib/openc3/utilities/quaternion.rb +257 -0
  280. data/lib/openc3/utilities/ruby_lex_utils.rb +568 -0
  281. data/lib/openc3/utilities/s3.rb +202 -0
  282. data/lib/openc3/utilities/s3_autoload.rb +9 -0
  283. data/lib/openc3/utilities/s3_file_cache.rb +274 -0
  284. data/lib/openc3/utilities/simulated_target.rb +117 -0
  285. data/lib/openc3/utilities/sleeper.rb +51 -0
  286. data/lib/openc3/utilities/store.rb +23 -0
  287. data/lib/openc3/utilities/store_autoload.rb +237 -0
  288. data/lib/openc3/utilities/zip.rb +21 -0
  289. data/lib/openc3/utilities.rb +35 -0
  290. data/lib/openc3/version.rb +14 -0
  291. data/lib/openc3/win32/excel.rb +132 -0
  292. data/lib/openc3/win32/win32.rb +402 -0
  293. data/lib/openc3/win32/win32_main.rb +333 -0
  294. data/lib/openc3.rb +49 -0
  295. data/tasks/gemfile_stats.rake +113 -0
  296. data/tasks/spec.rake +30 -0
  297. data/templates/plugin-template/README.md +15 -0
  298. data/templates/plugin-template/Rakefile +12 -0
  299. data/templates/plugin-template/plugin.gemspec +23 -0
  300. data/templates/plugin-template/plugin.txt +9 -0
  301. data/templates/plugin-template/targets/TARGET/cmd_tlm/cmd.txt +8 -0
  302. data/templates/plugin-template/targets/TARGET/cmd_tlm/tlm.txt +8 -0
  303. data/templates/plugin-template/targets/TARGET/lib/target.rb +10 -0
  304. data/templates/plugin-template/targets/TARGET/procedures/procedure.rb +3 -0
  305. data/templates/plugin-template/targets/TARGET/screens/status.txt +9 -0
  306. data/templates/plugin-template/targets/TARGET/target.txt +5 -0
  307. metadata +849 -0
@@ -0,0 +1,137 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 Ball Aerospace & Technologies Corp.
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
+ # Modified by OpenC3, Inc.
17
+ # All changes Copyright 2022, OpenC3, Inc.
18
+ # All Rights Reserved
19
+
20
+ require 'open-uri'
21
+ require 'nokogiri'
22
+ require 'httpclient'
23
+ require 'rubygems'
24
+ require 'rubygems/uninstaller'
25
+ require 'tempfile'
26
+ require 'openc3/utilities/s3'
27
+ require 'openc3/utilities/process_manager'
28
+ require 'openc3/api/api'
29
+
30
+ module OpenC3
31
+ # This class acts like a Model but doesn't inherit from Model because it doesn't
32
+ # actual interact with the Store (Redis). Instead we implement names, get, put
33
+ # and destroy to allow interaction with gem files from the PluginModel and
34
+ # the GemsController.
35
+ class GemModel
36
+ extend Api
37
+
38
+ @@bucket_initialized = false
39
+
40
+ def self.names
41
+ rubys3_client = initialize_bucket()
42
+ gems = []
43
+ rubys3_client.list_objects(bucket: 'gems').contents.each do |object|
44
+ gems << object.key
45
+ end
46
+ gems
47
+ end
48
+
49
+ def self.get(dir, name)
50
+ rubys3_client = initialize_bucket()
51
+ path = File.join(dir, name)
52
+ rubys3_client.get_object(bucket: 'gems', key: name, response_target: path)
53
+ return path
54
+ end
55
+
56
+ def self.put(gem_file_path, gem_install: true, scope:)
57
+ rubys3_client = initialize_bucket()
58
+ if File.file?(gem_file_path)
59
+ gem_filename = File.basename(gem_file_path)
60
+ Logger.info "Installing gem: #{gem_filename}"
61
+ File.open(gem_file_path, 'rb') do |file|
62
+ rubys3_client.put_object(bucket: 'gems', key: gem_filename, body: file)
63
+ end
64
+ if gem_install
65
+ result = OpenC3::ProcessManager.instance.spawn(["ruby", "/openc3/bin/openc3cli", "geminstall", gem_filename], "gem_install", gem_filename, Time.now + 3600.0, scope: scope)
66
+ return result
67
+ end
68
+ else
69
+ message = "Gem file #{gem_file_path} does not exist!"
70
+ Logger.error message
71
+ raise message
72
+ end
73
+ return nil
74
+ end
75
+
76
+ def self.install(name_or_path, scope:)
77
+ temp_dir = Dir.mktmpdir
78
+ begin
79
+ if File.exist?(name_or_path)
80
+ gem_file_path = name_or_path
81
+ else
82
+ gem_file_path = get(temp_dir, name_or_path)
83
+ end
84
+ begin
85
+ rubygems_url = get_setting('rubygems_url', scope: scope)
86
+ rescue
87
+ # If Redis isn't running try the ENV, then simply rubygems.org
88
+ rubygems_url = ENV['RUBYGEMS_URL']
89
+ rubygems_url ||= 'https://rubygems.org'
90
+ end
91
+ Gem.sources = [rubygems_url] if rubygems_url
92
+ Gem.done_installing_hooks.clear
93
+ Gem.install(gem_file_path, "> 0.pre", :build_args => ['--no-document'], :prerelease => true)
94
+ rescue => err
95
+ message = "Gem file #{gem_file_path} error installing to /gems\n#{err.formatted}"
96
+ Logger.error message
97
+ ensure
98
+ FileUtils.remove_entry(temp_dir) if temp_dir and File.exist?(temp_dir)
99
+ end
100
+ end
101
+
102
+ def self.destroy(name)
103
+ rubys3_client = initialize_bucket()
104
+ Logger.info "Removing gem: #{name}"
105
+ rubys3_client.delete_object(bucket: 'gems', key: name)
106
+ gem_name, version = self.extract_name_and_version(name)
107
+ begin
108
+ Gem::Uninstaller.new(gem_name, {:version => version, :force => true}).uninstall
109
+ rescue => err
110
+ message = "Gem file #{name} error uninstalling\n#{err.formatted}"
111
+ Logger.error message
112
+ end
113
+ end
114
+
115
+ def self.extract_name_and_version(name)
116
+ split_name = name.split('-')
117
+ gem_name = split_name[0..-2].join('-')
118
+ version = split_name[-1]
119
+ return gem_name, version
120
+ end
121
+
122
+ # private
123
+
124
+ def self.initialize_bucket
125
+ rubys3_client = Aws::S3::Client.new
126
+ unless @@bucket_initialized
127
+ begin
128
+ rubys3_client.head_bucket(bucket: 'gems')
129
+ rescue Aws::S3::Errors::NotFound
130
+ rubys3_client.create_bucket(bucket: 'gems')
131
+ end
132
+ @@bucket_initialized = true
133
+ end
134
+ return rubys3_client
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 Ball Aerospace & Technologies Corp.
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
+ # Modified by OpenC3, Inc.
17
+ # All changes Copyright 2022, OpenC3, Inc.
18
+ # All Rights Reserved
19
+
20
+ require 'openc3/utilities/store'
21
+
22
+ module OpenC3
23
+ class InfoModel
24
+
25
+ # @return Hash information and statistics about the redis server
26
+ def self.get()
27
+ return [Store.info(), EphemeralStore.info]
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,281 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 Ball Aerospace & Technologies Corp.
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
+ # Modified by OpenC3, Inc.
17
+ # All changes Copyright 2022, OpenC3, Inc.
18
+ # All Rights Reserved
19
+
20
+ require 'openc3/models/model'
21
+ require 'openc3/models/microservice_model'
22
+
23
+ module OpenC3
24
+ class InterfaceModel < Model
25
+ INTERFACES_PRIMARY_KEY = 'openc3_interfaces'
26
+ ROUTERS_PRIMARY_KEY = 'openc3_routers'
27
+
28
+ attr_accessor :config_params
29
+ attr_accessor :target_names
30
+ attr_accessor :connect_on_startup
31
+ attr_accessor :auto_reconnect
32
+ attr_accessor :reconnect_delay
33
+ attr_accessor :disable_disconnect
34
+ attr_accessor :options
35
+ attr_accessor :protocols
36
+ attr_accessor :interfaces
37
+ attr_accessor :log
38
+ attr_accessor :log_raw
39
+ attr_accessor :needs_dependencies
40
+
41
+ # NOTE: The following three class methods are used by the ModelController
42
+ # and are reimplemented to enable various Model class methods to work
43
+ def self.get(name:, scope:)
44
+ super("#{scope}__#{_get_key}", name: name)
45
+ end
46
+
47
+ def self.names(scope:)
48
+ super("#{scope}__#{_get_key}")
49
+ end
50
+
51
+ def self.all(scope:)
52
+ super("#{scope}__#{_get_key}")
53
+ end
54
+ # END NOTE
55
+
56
+ # Called by the PluginModel to allow this class to validate it's top-level keyword: "INTERFACE"
57
+ # Interface/Router specific keywords are handled by the instance method "handle_config"
58
+ # NOTE: See RouterModel for the router method implementation
59
+ def self.handle_config(parser, keyword, parameters, plugin: nil, needs_dependencies: false, scope:)
60
+ case keyword
61
+ when 'INTERFACE'
62
+ parser.verify_num_parameters(2, nil, "INTERFACE <Name> <Filename> <Specific Parameters>")
63
+ return self.new(name: parameters[0].upcase, config_params: parameters[1..-1], plugin: plugin, needs_dependencies: needs_dependencies, scope: scope)
64
+ else
65
+ raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Interface: #{keyword} #{parameters.join(" ")}")
66
+ end
67
+ end
68
+
69
+ # Helper method to return the correct type based on class name
70
+ def self._get_type
71
+ self.name.to_s.split("Model")[0].upcase.split("::")[-1]
72
+ end
73
+
74
+ # Helper method to return the correct primary key based on class name
75
+ def self._get_key
76
+ type = _get_type
77
+ case type
78
+ when 'INTERFACE'
79
+ INTERFACES_PRIMARY_KEY
80
+ when 'ROUTER'
81
+ ROUTERS_PRIMARY_KEY
82
+ else
83
+ raise "Unknown type #{type} from class #{self.name}"
84
+ end
85
+ end
86
+
87
+ def initialize(
88
+ name:,
89
+ config_params: [],
90
+ target_names: [],
91
+ connect_on_startup: true,
92
+ auto_reconnect: true,
93
+ reconnect_delay: 5.0,
94
+ disable_disconnect: false,
95
+ options: [],
96
+ protocols: [],
97
+ log: true,
98
+ log_raw: false,
99
+ updated_at: nil,
100
+ plugin: nil,
101
+ needs_dependencies: false,
102
+ scope:
103
+ )
104
+ if self.class._get_type == 'INTERFACE'
105
+ super("#{scope}__#{INTERFACES_PRIMARY_KEY}", name: name, updated_at: updated_at, plugin: plugin, scope: scope)
106
+ else
107
+ super("#{scope}__#{ROUTERS_PRIMARY_KEY}", name: name, updated_at: updated_at, plugin: plugin, scope: scope)
108
+ end
109
+ @config_params = config_params
110
+ @target_names = target_names
111
+ @connect_on_startup = connect_on_startup
112
+ @auto_reconnect = auto_reconnect
113
+ @reconnect_delay = reconnect_delay
114
+ @disable_disconnect = disable_disconnect
115
+ @options = options
116
+ @protocols = protocols
117
+ @log = log
118
+ @log_raw = log_raw
119
+ @needs_dependencies = needs_dependencies
120
+ end
121
+
122
+ # Called by InterfaceMicroservice to instantiate the Interface defined
123
+ # by the model configuration. Must be called after get_model which
124
+ # calls from_json to instantiate the class and populate the attributes.
125
+ def build
126
+ klass = OpenC3.require_class(@config_params[0])
127
+ if @config_params.length > 1
128
+ interface_or_router = klass.new(*@config_params[1..-1])
129
+ else
130
+ interface_or_router = klass.new
131
+ end
132
+ interface_or_router.target_names = @target_names.dup
133
+ interface_or_router.connect_on_startup = @connect_on_startup
134
+ interface_or_router.auto_reconnect = @auto_reconnect
135
+ interface_or_router.reconnect_delay = @reconnect_delay
136
+ interface_or_router.disable_disconnect = @disable_disconnect
137
+ @options.each do |option|
138
+ interface_or_router.set_option(option[0], option[1..-1])
139
+ end
140
+ @protocols.each do |protocol|
141
+ klass = OpenC3.require_class(protocol[1])
142
+ interface_or_router.add_protocol(klass, protocol[2..-1], protocol[0].upcase.intern)
143
+ end
144
+ interface_or_router
145
+ end
146
+
147
+ def as_json(*a)
148
+ {
149
+ 'name' => @name,
150
+ 'config_params' => @config_params,
151
+ 'target_names' => @target_names,
152
+ 'connect_on_startup' => @connect_on_startup,
153
+ 'auto_reconnect' => @auto_reconnect,
154
+ 'reconnect_delay' => @reconnect_delay,
155
+ 'disable_disconnect' => @disable_disconnect,
156
+ 'options' => @options,
157
+ 'protocols' => @protocols,
158
+ 'log' => @log,
159
+ 'log_raw' => @log_raw,
160
+ 'plugin' => @plugin,
161
+ 'needs_dependencies' => @needs_dependencies,
162
+ 'updated_at' => @updated_at
163
+ }
164
+ end
165
+
166
+ # TODO: Not currently used but may be used by a XTCE or other format to OpenC3 conversion
167
+ def as_config
168
+ result = "#{self.class._get_type} #{@name} #{@config_params.join(' ')}\n"
169
+ @target_names.each do |target_name|
170
+ result << " MAP_TARGET #{target_name}\n"
171
+ end
172
+ result << " DONT_CONNECT\n" unless @connect_on_startup
173
+ result << " DONT_RECONNECT\n" unless @auto_reconnect
174
+ result << " RECONNECT_DELAY #{@reconnect_delay}\n"
175
+ result << " DISABLE_DISCONNECT\n" if @disable_disconnect
176
+ @options.each do |option|
177
+ result << " OPTION #{option.join(' ')}\n"
178
+ end
179
+ @protocols.each do |protocol|
180
+ result << " PROTOCOL #{protocol.join(' ')}\n"
181
+ end
182
+ result << " DONT_LOG" unless @log
183
+ result << " LOG_RAW" if @log_raw
184
+ result
185
+ end
186
+
187
+ # Handles Interface/Router specific configuration keywords
188
+ def handle_config(parser, keyword, parameters)
189
+ case keyword
190
+ when 'MAP_TARGET'
191
+ parser.verify_num_parameters(1, 1, "#{keyword} <Target Name>")
192
+ @target_names << parameters[0].upcase
193
+
194
+ when 'DONT_CONNECT'
195
+ parser.verify_num_parameters(0, 0, "#{keyword}")
196
+ @connect_on_startup = false
197
+
198
+ when 'DONT_RECONNECT'
199
+ parser.verify_num_parameters(0, 0, "#{keyword}")
200
+ @auto_reconnect = false
201
+
202
+ when 'RECONNECT_DELAY'
203
+ parser.verify_num_parameters(1, 1, "#{keyword} <Delay in Seconds>")
204
+ @reconnect_delay = Float(parameters[0])
205
+
206
+ when 'DISABLE_DISCONNECT'
207
+ parser.verify_num_parameters(0, 0, "#{keyword}")
208
+ @disable_disconnect = true
209
+
210
+ when 'OPTION'
211
+ parser.verify_num_parameters(2, nil, "#{keyword} <Option Name> <Option Value 1> <Option Value 2 (optional)> <etc>")
212
+ @options << parameters.dup
213
+
214
+ when 'PROTOCOL'
215
+ usage = "#{keyword} <READ WRITE READ_WRITE> <protocol filename or classname> <Protocol specific parameters>"
216
+ parser.verify_num_parameters(2, nil, usage)
217
+ unless %w(READ WRITE READ_WRITE).include? parameters[0].upcase
218
+ raise parser.error("Invalid protocol type: #{parameters[0]}", usage)
219
+ end
220
+
221
+ @protocols << parameters.dup
222
+
223
+ when 'DONT_LOG'
224
+ parser.verify_num_parameters(0, 0, "#{keyword}")
225
+ @log = false
226
+
227
+ when 'LOG_RAW'
228
+ parser.verify_num_parameters(0, 0, "#{keyword}")
229
+ @log_raw = true
230
+
231
+ else
232
+ raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Interface/Router: #{keyword} #{parameters.join(" ")}")
233
+
234
+ end
235
+
236
+ return nil
237
+ end
238
+
239
+ # Creates a MicroserviceModel to deploy the Interface/Router
240
+ def deploy(gem_path, variables, validate_only: false)
241
+ type = self.class._get_type
242
+ microservice_name = "#{@scope}__#{type}__#{@name}"
243
+ microservice = MicroserviceModel.new(
244
+ name: microservice_name,
245
+ work_dir: '/openc3/lib/openc3/microservices',
246
+ cmd: ["ruby", "#{type.downcase}_microservice.rb", microservice_name],
247
+ target_names: @target_names,
248
+ plugin: @plugin,
249
+ needs_dependencies: @needs_dependencies,
250
+ scope: @scope
251
+ )
252
+ unless validate_only
253
+ microservice.create
254
+ microservice.deploy(gem_path, variables)
255
+ ConfigTopic.write({ kind: 'created', type: type.downcase, name: @name, plugin: @plugin }, scope: @scope)
256
+ Logger.info "Configured #{type.downcase} microservice #{microservice_name}"
257
+ end
258
+ microservice
259
+ end
260
+
261
+ # Looks up the deployed MicroserviceModel and destroy the microservice model
262
+ # should should trigger the operator to kill the microservice that in turn
263
+ # will destroy the InterfaceStatusModel when a stop is called.
264
+ def undeploy
265
+ type = self.class._get_type
266
+ name = "#{@scope}__#{type}__#{@name}"
267
+ model = MicroserviceModel.get_model(name: name, scope: @scope)
268
+ if model
269
+ model.destroy
270
+ ConfigTopic.write({ kind: 'deleted', type: type.downcase, name: @name, plugin: @plugin }, scope: @scope)
271
+ end
272
+
273
+ if type == 'INTERFACE'
274
+ status_model = InterfaceStatusModel.get_model(name: @name, scope: @scope)
275
+ else
276
+ status_model = RouterStatusModel.get_model(name: @name, scope: @scope)
277
+ end
278
+ status_model.destroy if status_model
279
+ end
280
+ end
281
+ end
@@ -0,0 +1,117 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 Ball Aerospace & Technologies Corp.
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
+ # Modified by OpenC3, Inc.
17
+ # All changes Copyright 2022, OpenC3, Inc.
18
+ # All Rights Reserved
19
+
20
+ require 'openc3/models/model'
21
+
22
+ module OpenC3
23
+ # Stores the status about an interface. This class also implements logic
24
+ # to handle status for a router since the functionality is identical
25
+ # (only difference is the Redis key used).
26
+ class InterfaceStatusModel < EphemeralModel
27
+ INTERFACES_PRIMARY_KEY = 'openc3_interface_status'
28
+ ROUTERS_PRIMARY_KEY = 'openc3_router_status'
29
+
30
+ attr_accessor :state
31
+ attr_accessor :clients
32
+ attr_accessor :txsize
33
+ attr_accessor :rxsize
34
+ attr_accessor :txbytes
35
+ attr_accessor :rxbytes
36
+ attr_accessor :txcnt
37
+ attr_accessor :rxcnt
38
+
39
+ # NOTE: The following three class methods are used by the ModelController
40
+ # and are reimplemented to enable various Model class methods to work
41
+ def self.get(name:, scope:)
42
+ super("#{scope}__#{_get_key}", name: name)
43
+ end
44
+
45
+ def self.names(scope:)
46
+ super("#{scope}__#{_get_key}")
47
+ end
48
+
49
+ def self.all(scope:)
50
+ super("#{scope}__#{_get_key}")
51
+ end
52
+ # END NOTE
53
+
54
+ # Helper method to return the correct type based on class name
55
+ def self._get_type
56
+ self.name.to_s.split("Model")[0].upcase.split("::")[-1]
57
+ end
58
+
59
+ # Helper method to return the correct primary key based on class name
60
+ def self._get_key
61
+ type = _get_type
62
+ case type
63
+ when 'INTERFACESTATUS'
64
+ INTERFACES_PRIMARY_KEY
65
+ when 'ROUTERSTATUS'
66
+ ROUTERS_PRIMARY_KEY
67
+ else
68
+ raise "Unknown type #{type} from class #{self.name}"
69
+ end
70
+ end
71
+
72
+ def initialize(
73
+ name:,
74
+ state:,
75
+ clients: 0,
76
+ txsize: 0,
77
+ rxsize: 0,
78
+ txbytes: 0,
79
+ rxbytes: 0,
80
+ txcnt: 0,
81
+ rxcnt: 0,
82
+ updated_at: nil,
83
+ plugin: nil,
84
+ scope:
85
+ )
86
+ if self.class._get_type == 'INTERFACESTATUS'
87
+ super("#{scope}__#{INTERFACES_PRIMARY_KEY}", name: name, updated_at: updated_at, plugin: plugin, scope: scope)
88
+ else
89
+ super("#{scope}__#{ROUTERS_PRIMARY_KEY}", name: name, updated_at: updated_at, plugin: plugin, scope: scope)
90
+ end
91
+ @state = state
92
+ @clients = clients
93
+ @txsize = txsize
94
+ @rxsize = rxsize
95
+ @txbytes = txbytes
96
+ @rxbytes = rxbytes
97
+ @txcnt = txcnt
98
+ @rxcnt = rxcnt
99
+ end
100
+
101
+ def as_json(*a)
102
+ {
103
+ 'name' => @name,
104
+ 'state' => @state,
105
+ 'clients' => @clients,
106
+ 'txsize' => @txsize,
107
+ 'rxsize' => @rxsize,
108
+ 'txbytes' => @txbytes,
109
+ 'rxbytes' => @rxbytes,
110
+ 'txcnt' => @txcnt,
111
+ 'rxcnt' => @rxcnt,
112
+ 'plugin' => @plugin,
113
+ 'updated_at' => @updated_at
114
+ }
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,139 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 Ball Aerospace & Technologies Corp.
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
+ # Modified by OpenC3, Inc.
17
+ # All changes Copyright 2022, OpenC3, Inc.
18
+ # All Rights Reserved
19
+
20
+ # https://www.rubydoc.info/gems/redis/Redis/Commands/SortedSets
21
+
22
+ require 'openc3/models/sorted_model'
23
+
24
+ module OpenC3
25
+ class MetadataModel < SortedModel
26
+ METADATA_TYPE = 'metadata'.freeze
27
+ PRIMARY_KEY = '__METADATA'.freeze
28
+
29
+ def self.pk(scope)
30
+ "#{scope}#{PRIMARY_KEY}"
31
+ end
32
+
33
+ attr_reader :color, :metadata, :constraints, :type
34
+
35
+ # @param [Integer] start - Time metadata is active in seconds from Epoch
36
+ # @param [String] color - The event color
37
+ # @param [Hash] metadata - Hash of metadata values
38
+ # @param [Hash] constraints - Constraints to apply to the metadata
39
+ # @param [String] scope - OpenC3 scope to track event to
40
+ def initialize(
41
+ scope:,
42
+ start:,
43
+ color: nil,
44
+ metadata:,
45
+ constraints: nil,
46
+ type: METADATA_TYPE,
47
+ updated_at: 0
48
+ )
49
+ super(start: start, scope: scope, updated_at: updated_at)
50
+ @start = start
51
+ @color = color
52
+ @metadata = metadata
53
+ @constraints = constraints if constraints
54
+ @type = type # For the as_json, from_json round trip
55
+ end
56
+
57
+ # Validates the instance variables: @start, @color, @metadata
58
+ def validate(update: false)
59
+ validate_start(update: update)
60
+ validate_color()
61
+ validate_metadata()
62
+ validate_constraints() if @constraints
63
+ end
64
+
65
+ def validate_color()
66
+ if @color.nil?
67
+ @color = '#%06x' % (rand * 0xffffff)
68
+ end
69
+ unless @color =~ /(#*)([0-9,a-f,A-f]{6})/
70
+ raise SortedInputError.new "invalid color, must be in hex format, e.g. #FF0000"
71
+ end
72
+ @color = "##{@color}" unless @color.start_with?('#')
73
+ end
74
+
75
+ def validate_metadata()
76
+ unless @metadata.is_a?(Hash)
77
+ raise SortedInputError.new "Metadata must be a hash/object: #{@metadata}"
78
+ end
79
+ # Convert keys to strings. This isn't quite as efficient as symbols
80
+ # but we store as JSON which is all strings and it makes comparisons easier.
81
+ @metadata = @metadata.transform_keys(&:to_s)
82
+ end
83
+
84
+ def validate_constraints()
85
+ unless @constraints.is_a?(Hash)
86
+ raise SortedInputError.new "Constraints must be a hash/object: #{@constraints}"
87
+ end
88
+ # Convert keys to strings. This isn't quite as efficient as symbols
89
+ # but we store as JSON which is all strings and it makes comparisons easier.
90
+ @constraints = @constraints.transform_keys(&:to_s)
91
+ unless (@constraints.keys - @metadata.keys).empty?
92
+ raise SortedInputError.new "Constraints keys must be subset of metadata: #{@constraints.keys} subset #{@metadata.keys}"
93
+ end
94
+ @constraints.each do |key, constraint|
95
+ unless constraint.include?(@metadata[key])
96
+ raise SortedInputError.new "Constraint violation! key:#{key} value:#{@metadata[key]} constraint:#{constraint}"
97
+ end
98
+ end
99
+ end
100
+
101
+ # Update the Redis hash at primary_key based on the initial passed start
102
+ # The member is set to the JSON generated via calling as_json
103
+ def create(update: false)
104
+ validate(update: update)
105
+ @updated_at = Time.now.to_nsec_from_epoch
106
+ MetadataModel.destroy(scope: @scope, start: update) if update
107
+ Store.zadd(@primary_key, @start, JSON.generate(as_json(:allow_nan => true)))
108
+ if update
109
+ notify(kind: 'updated')
110
+ else
111
+ notify(kind: 'created')
112
+ end
113
+ end
114
+
115
+ # Update the model. All arguments are optional, only those set will be updated.
116
+ def update(start: nil, color: nil, metadata: nil, constraints: nil)
117
+ orig_start = @start
118
+ @start = start if start
119
+ @color = color if color
120
+ @metadata = metadata if metadata
121
+ @constraints = constraints if constraints
122
+ create(update: orig_start)
123
+ end
124
+
125
+ # @return [Hash] generated from the MetadataModel
126
+ def as_json(*a)
127
+ {
128
+ 'scope' => @scope,
129
+ 'start' => @start,
130
+ 'color' => @color,
131
+ 'metadata' => @metadata.as_json(*a),
132
+ 'constraints' => @constraints,
133
+ 'type' => METADATA_TYPE,
134
+ 'updated_at' => @updated_at,
135
+ }
136
+ end
137
+ alias to_s as_json
138
+ end
139
+ end