rsmp 0.46.0 → 0.48.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 (433) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/copilot-setup-steps.yml +1 -1
  3. data/.github/workflows/rubocop.yaml +1 -1
  4. data/.github/workflows/sus.yaml +1 -1
  5. data/CHANGELOG.md +14 -0
  6. data/Gemfile.lock +5 -5
  7. data/README.md +3 -1
  8. data/documentation/cli.md +225 -0
  9. data/documentation/configuration.md +83 -0
  10. data/lib/rsmp/cli/configuration.rb +30 -0
  11. data/lib/rsmp/cli.rb +45 -45
  12. data/lib/rsmp/collect/command_matcher.rb +13 -4
  13. data/lib/rsmp/collect/status_matcher.rb +14 -4
  14. data/lib/rsmp/command_list.rb +1 -1
  15. data/lib/rsmp/config.rb +63 -0
  16. data/lib/rsmp/convert/export/json_schema/index.rb +64 -0
  17. data/lib/rsmp/convert/export/json_schema/items.rb +9 -1
  18. data/lib/rsmp/convert/export/json_schema/outputs.rb +65 -25
  19. data/lib/rsmp/convert/export/json_schema/values.rb +40 -11
  20. data/lib/rsmp/convert/export/json_schema.rb +3 -1
  21. data/lib/rsmp/message/sxl_codec.rb +194 -0
  22. data/lib/rsmp/message.rb +2 -0
  23. data/lib/rsmp/node/site/connections.rb +80 -0
  24. data/lib/rsmp/node/site/site.rb +21 -35
  25. data/lib/rsmp/node/supervisor/supervisor.rb +42 -0
  26. data/lib/rsmp/options/options.rb +21 -9
  27. data/lib/rsmp/options/schemas/config_common.json +129 -0
  28. data/lib/rsmp/options/schemas/log.json +71 -0
  29. data/lib/rsmp/options/schemas/site.json +12 -57
  30. data/lib/rsmp/options/schemas/supervisor.json +4 -29
  31. data/lib/rsmp/options/schemas/supervisor_site.json +19 -31
  32. data/lib/rsmp/options/schemas/traffic_controller_site.json +1 -2
  33. data/lib/rsmp/options/site_options.rb +31 -23
  34. data/lib/rsmp/options/supervisor_options.rb +1 -0
  35. data/lib/rsmp/proxy/modules/receive.rb +3 -1
  36. data/lib/rsmp/proxy/modules/send.rb +26 -14
  37. data/lib/rsmp/proxy/proxy.rb +17 -0
  38. data/lib/rsmp/proxy/site/connection.rb +79 -0
  39. data/lib/rsmp/proxy/site/modules/commands.rb +24 -1
  40. data/lib/rsmp/proxy/site/site_proxy.rb +6 -15
  41. data/lib/rsmp/proxy/supervisor/modules/commands.rb +55 -0
  42. data/lib/rsmp/proxy/supervisor/modules/message_buffer.rb +0 -5
  43. data/lib/rsmp/proxy/supervisor/modules/status.rb +13 -104
  44. data/lib/rsmp/proxy/supervisor/modules/status_updates.rb +148 -0
  45. data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +19 -0
  46. data/lib/rsmp/schema.rb +38 -1
  47. data/lib/rsmp/tlc/detector_logic.rb +2 -2
  48. data/lib/rsmp/tlc/modules/helpers.rb +0 -22
  49. data/lib/rsmp/tlc/modules/inputs.rb +4 -3
  50. data/lib/rsmp/tlc/modules/modes.rb +2 -2
  51. data/lib/rsmp/tlc/modules/plans.rb +7 -3
  52. data/lib/rsmp/tlc/modules/system.rb +1 -1
  53. data/lib/rsmp/tlc/proxy/control.rb +19 -19
  54. data/lib/rsmp/tlc/proxy/detectors.rb +3 -3
  55. data/lib/rsmp/tlc/proxy/io.rb +15 -12
  56. data/lib/rsmp/tlc/proxy/plans.rb +14 -14
  57. data/lib/rsmp/tlc/proxy/status.rb +9 -5
  58. data/lib/rsmp/tlc/proxy/system.rb +12 -6
  59. data/lib/rsmp/tlc/signal_group.rb +2 -2
  60. data/lib/rsmp/tlc/supervisor_interface.rb +42 -0
  61. data/lib/rsmp/tlc/traffic_controller_site.rb +1 -18
  62. data/lib/rsmp/version.rb +1 -1
  63. data/lib/rsmp.rb +5 -0
  64. data/schemas/core/3.1.4/command_response.json +11 -0
  65. data/schemas/core/3.1.5/command_response.json +11 -0
  66. data/schemas/core/3.2.0/command_response.json +11 -0
  67. data/schemas/core/3.2.1/command_response.json +11 -0
  68. data/schemas/core/3.2.2/command_response.json +11 -0
  69. data/schemas/core/3.3.0/command_response.json +11 -0
  70. data/schemas/tlc/1.0.10/alarms/A0008.json +21 -15
  71. data/schemas/tlc/1.0.10/alarms/A0201.json +26 -20
  72. data/schemas/tlc/1.0.10/alarms/A0202.json +26 -20
  73. data/schemas/tlc/1.0.10/alarms/A0301.json +69 -63
  74. data/schemas/tlc/1.0.10/alarms/A0302.json +87 -81
  75. data/schemas/tlc/1.0.10/commands/M0001.json +66 -60
  76. data/schemas/tlc/1.0.10/commands/M0002.json +48 -42
  77. data/schemas/tlc/1.0.10/commands/M0003.json +48 -42
  78. data/schemas/tlc/1.0.10/commands/M0004.json +35 -29
  79. data/schemas/tlc/1.0.10/commands/M0005.json +48 -42
  80. data/schemas/tlc/1.0.10/commands/M0006.json +48 -42
  81. data/schemas/tlc/1.0.10/commands/M0007.json +35 -29
  82. data/schemas/tlc/1.0.10/commands/M0008.json +48 -42
  83. data/schemas/tlc/1.0.10/commands/M0010.json +35 -29
  84. data/schemas/tlc/1.0.10/commands/M0011.json +35 -29
  85. data/schemas/tlc/1.0.10/commands/M0012.json +35 -29
  86. data/schemas/tlc/1.0.10/commands/M0013.json +35 -29
  87. data/schemas/tlc/1.0.10/commands/M0019.json +61 -55
  88. data/schemas/tlc/1.0.10/commands/M0103.json +51 -45
  89. data/schemas/tlc/1.0.10/commands/M0104.json +100 -94
  90. data/schemas/tlc/1.0.10/commands/command_requests.json +995 -1
  91. data/schemas/tlc/1.0.10/defs/guards.json +19 -0
  92. data/schemas/tlc/1.0.10/sxl.yaml +56 -56
  93. data/schemas/tlc/1.0.10/sxl_index.json +226 -244
  94. data/schemas/tlc/1.0.13/alarms/A0008.json +21 -15
  95. data/schemas/tlc/1.0.13/alarms/A0201.json +26 -20
  96. data/schemas/tlc/1.0.13/alarms/A0202.json +26 -20
  97. data/schemas/tlc/1.0.13/alarms/A0301.json +69 -63
  98. data/schemas/tlc/1.0.13/alarms/A0302.json +87 -81
  99. data/schemas/tlc/1.0.13/commands/M0001.json +66 -60
  100. data/schemas/tlc/1.0.13/commands/M0002.json +48 -42
  101. data/schemas/tlc/1.0.13/commands/M0003.json +48 -42
  102. data/schemas/tlc/1.0.13/commands/M0004.json +35 -29
  103. data/schemas/tlc/1.0.13/commands/M0005.json +48 -42
  104. data/schemas/tlc/1.0.13/commands/M0006.json +48 -42
  105. data/schemas/tlc/1.0.13/commands/M0007.json +35 -29
  106. data/schemas/tlc/1.0.13/commands/M0008.json +48 -42
  107. data/schemas/tlc/1.0.13/commands/M0010.json +35 -29
  108. data/schemas/tlc/1.0.13/commands/M0011.json +35 -29
  109. data/schemas/tlc/1.0.13/commands/M0012.json +35 -29
  110. data/schemas/tlc/1.0.13/commands/M0013.json +35 -29
  111. data/schemas/tlc/1.0.13/commands/M0014.json +48 -42
  112. data/schemas/tlc/1.0.13/commands/M0015.json +48 -42
  113. data/schemas/tlc/1.0.13/commands/M0016.json +35 -29
  114. data/schemas/tlc/1.0.13/commands/M0017.json +35 -29
  115. data/schemas/tlc/1.0.13/commands/M0018.json +48 -42
  116. data/schemas/tlc/1.0.13/commands/M0019.json +61 -55
  117. data/schemas/tlc/1.0.13/commands/M0103.json +51 -45
  118. data/schemas/tlc/1.0.13/commands/M0104.json +100 -94
  119. data/schemas/tlc/1.0.13/commands/command_requests.json +1293 -1
  120. data/schemas/tlc/1.0.13/defs/guards.json +19 -0
  121. data/schemas/tlc/1.0.13/sxl.yaml +63 -63
  122. data/schemas/tlc/1.0.13/sxl_index.json +280 -298
  123. data/schemas/tlc/1.0.14/alarms/A0008.json +21 -15
  124. data/schemas/tlc/1.0.14/alarms/A0201.json +26 -20
  125. data/schemas/tlc/1.0.14/alarms/A0202.json +26 -20
  126. data/schemas/tlc/1.0.14/alarms/A0301.json +69 -63
  127. data/schemas/tlc/1.0.14/alarms/A0302.json +87 -81
  128. data/schemas/tlc/1.0.14/commands/M0001.json +66 -60
  129. data/schemas/tlc/1.0.14/commands/M0002.json +48 -42
  130. data/schemas/tlc/1.0.14/commands/M0003.json +48 -42
  131. data/schemas/tlc/1.0.14/commands/M0004.json +35 -29
  132. data/schemas/tlc/1.0.14/commands/M0005.json +48 -42
  133. data/schemas/tlc/1.0.14/commands/M0006.json +48 -42
  134. data/schemas/tlc/1.0.14/commands/M0007.json +35 -29
  135. data/schemas/tlc/1.0.14/commands/M0008.json +48 -42
  136. data/schemas/tlc/1.0.14/commands/M0010.json +35 -29
  137. data/schemas/tlc/1.0.14/commands/M0011.json +35 -29
  138. data/schemas/tlc/1.0.14/commands/M0012.json +35 -29
  139. data/schemas/tlc/1.0.14/commands/M0013.json +35 -29
  140. data/schemas/tlc/1.0.14/commands/M0014.json +48 -42
  141. data/schemas/tlc/1.0.14/commands/M0015.json +48 -42
  142. data/schemas/tlc/1.0.14/commands/M0016.json +35 -29
  143. data/schemas/tlc/1.0.14/commands/M0017.json +35 -29
  144. data/schemas/tlc/1.0.14/commands/M0018.json +48 -42
  145. data/schemas/tlc/1.0.14/commands/M0019.json +61 -55
  146. data/schemas/tlc/1.0.14/commands/M0103.json +51 -45
  147. data/schemas/tlc/1.0.14/commands/M0104.json +100 -94
  148. data/schemas/tlc/1.0.14/commands/command_requests.json +1293 -1
  149. data/schemas/tlc/1.0.14/defs/guards.json +19 -0
  150. data/schemas/tlc/1.0.14/sxl.yaml +63 -63
  151. data/schemas/tlc/1.0.14/sxl_index.json +304 -322
  152. data/schemas/tlc/1.0.15/alarms/A0008.json +21 -15
  153. data/schemas/tlc/1.0.15/alarms/A0201.json +26 -20
  154. data/schemas/tlc/1.0.15/alarms/A0202.json +26 -20
  155. data/schemas/tlc/1.0.15/alarms/A0301.json +69 -63
  156. data/schemas/tlc/1.0.15/alarms/A0302.json +87 -81
  157. data/schemas/tlc/1.0.15/commands/M0001.json +66 -60
  158. data/schemas/tlc/1.0.15/commands/M0002.json +48 -42
  159. data/schemas/tlc/1.0.15/commands/M0003.json +48 -42
  160. data/schemas/tlc/1.0.15/commands/M0004.json +35 -29
  161. data/schemas/tlc/1.0.15/commands/M0005.json +48 -42
  162. data/schemas/tlc/1.0.15/commands/M0006.json +48 -42
  163. data/schemas/tlc/1.0.15/commands/M0007.json +35 -29
  164. data/schemas/tlc/1.0.15/commands/M0008.json +48 -42
  165. data/schemas/tlc/1.0.15/commands/M0010.json +35 -29
  166. data/schemas/tlc/1.0.15/commands/M0011.json +35 -29
  167. data/schemas/tlc/1.0.15/commands/M0012.json +35 -29
  168. data/schemas/tlc/1.0.15/commands/M0013.json +35 -29
  169. data/schemas/tlc/1.0.15/commands/M0014.json +48 -42
  170. data/schemas/tlc/1.0.15/commands/M0015.json +48 -42
  171. data/schemas/tlc/1.0.15/commands/M0016.json +35 -29
  172. data/schemas/tlc/1.0.15/commands/M0017.json +35 -29
  173. data/schemas/tlc/1.0.15/commands/M0018.json +48 -42
  174. data/schemas/tlc/1.0.15/commands/M0019.json +61 -55
  175. data/schemas/tlc/1.0.15/commands/M0020.json +61 -55
  176. data/schemas/tlc/1.0.15/commands/M0021.json +35 -29
  177. data/schemas/tlc/1.0.15/commands/M0103.json +51 -45
  178. data/schemas/tlc/1.0.15/commands/M0104.json +100 -94
  179. data/schemas/tlc/1.0.15/commands/command_requests.json +1425 -1
  180. data/schemas/tlc/1.0.15/defs/guards.json +19 -0
  181. data/schemas/tlc/1.0.15/sxl.yaml +66 -66
  182. data/schemas/tlc/1.0.15/sxl_index.json +330 -350
  183. data/schemas/tlc/1.0.7/alarms/A0008.json +21 -15
  184. data/schemas/tlc/1.0.7/alarms/A0201.json +26 -20
  185. data/schemas/tlc/1.0.7/alarms/A0202.json +26 -20
  186. data/schemas/tlc/1.0.7/alarms/A0301.json +69 -63
  187. data/schemas/tlc/1.0.7/alarms/A0302.json +87 -81
  188. data/schemas/tlc/1.0.7/commands/M0001.json +66 -60
  189. data/schemas/tlc/1.0.7/commands/M0002.json +48 -42
  190. data/schemas/tlc/1.0.7/commands/M0003.json +48 -42
  191. data/schemas/tlc/1.0.7/commands/M0004.json +35 -29
  192. data/schemas/tlc/1.0.7/commands/M0005.json +48 -42
  193. data/schemas/tlc/1.0.7/commands/M0006.json +48 -42
  194. data/schemas/tlc/1.0.7/commands/M0007.json +35 -29
  195. data/schemas/tlc/1.0.7/commands/M0008.json +48 -42
  196. data/schemas/tlc/1.0.7/commands/M0010.json +35 -29
  197. data/schemas/tlc/1.0.7/commands/M0011.json +35 -29
  198. data/schemas/tlc/1.0.7/commands/M0019.json +61 -55
  199. data/schemas/tlc/1.0.7/commands/M0103.json +51 -45
  200. data/schemas/tlc/1.0.7/commands/M0104.json +100 -94
  201. data/schemas/tlc/1.0.7/commands/command_requests.json +895 -1
  202. data/schemas/tlc/1.0.7/defs/guards.json +19 -0
  203. data/schemas/tlc/1.0.7/sxl.yaml +58 -58
  204. data/schemas/tlc/1.0.7/sxl_index.json +228 -246
  205. data/schemas/tlc/1.0.8/alarms/A0008.json +21 -15
  206. data/schemas/tlc/1.0.8/alarms/A0201.json +26 -20
  207. data/schemas/tlc/1.0.8/alarms/A0202.json +26 -20
  208. data/schemas/tlc/1.0.8/alarms/A0301.json +69 -63
  209. data/schemas/tlc/1.0.8/alarms/A0302.json +87 -81
  210. data/schemas/tlc/1.0.8/commands/M0001.json +66 -60
  211. data/schemas/tlc/1.0.8/commands/M0002.json +48 -42
  212. data/schemas/tlc/1.0.8/commands/M0003.json +48 -42
  213. data/schemas/tlc/1.0.8/commands/M0004.json +35 -29
  214. data/schemas/tlc/1.0.8/commands/M0005.json +48 -42
  215. data/schemas/tlc/1.0.8/commands/M0006.json +48 -42
  216. data/schemas/tlc/1.0.8/commands/M0007.json +35 -29
  217. data/schemas/tlc/1.0.8/commands/M0008.json +48 -42
  218. data/schemas/tlc/1.0.8/commands/M0010.json +35 -29
  219. data/schemas/tlc/1.0.8/commands/M0011.json +35 -29
  220. data/schemas/tlc/1.0.8/commands/M0012.json +35 -29
  221. data/schemas/tlc/1.0.8/commands/M0013.json +35 -29
  222. data/schemas/tlc/1.0.8/commands/M0019.json +61 -55
  223. data/schemas/tlc/1.0.8/commands/M0103.json +51 -45
  224. data/schemas/tlc/1.0.8/commands/M0104.json +100 -94
  225. data/schemas/tlc/1.0.8/commands/command_requests.json +995 -1
  226. data/schemas/tlc/1.0.8/defs/guards.json +19 -0
  227. data/schemas/tlc/1.0.8/sxl.yaml +56 -56
  228. data/schemas/tlc/1.0.8/sxl_index.json +226 -244
  229. data/schemas/tlc/1.0.9/alarms/A0008.json +21 -15
  230. data/schemas/tlc/1.0.9/alarms/A0201.json +26 -20
  231. data/schemas/tlc/1.0.9/alarms/A0202.json +26 -20
  232. data/schemas/tlc/1.0.9/alarms/A0301.json +69 -63
  233. data/schemas/tlc/1.0.9/alarms/A0302.json +87 -81
  234. data/schemas/tlc/1.0.9/commands/M0001.json +66 -60
  235. data/schemas/tlc/1.0.9/commands/M0002.json +48 -42
  236. data/schemas/tlc/1.0.9/commands/M0003.json +48 -42
  237. data/schemas/tlc/1.0.9/commands/M0004.json +35 -29
  238. data/schemas/tlc/1.0.9/commands/M0005.json +48 -42
  239. data/schemas/tlc/1.0.9/commands/M0006.json +48 -42
  240. data/schemas/tlc/1.0.9/commands/M0007.json +35 -29
  241. data/schemas/tlc/1.0.9/commands/M0008.json +48 -42
  242. data/schemas/tlc/1.0.9/commands/M0010.json +35 -29
  243. data/schemas/tlc/1.0.9/commands/M0011.json +35 -29
  244. data/schemas/tlc/1.0.9/commands/M0012.json +35 -29
  245. data/schemas/tlc/1.0.9/commands/M0013.json +35 -29
  246. data/schemas/tlc/1.0.9/commands/M0019.json +61 -55
  247. data/schemas/tlc/1.0.9/commands/M0103.json +51 -45
  248. data/schemas/tlc/1.0.9/commands/M0104.json +100 -94
  249. data/schemas/tlc/1.0.9/commands/command_requests.json +995 -1
  250. data/schemas/tlc/1.0.9/defs/guards.json +19 -0
  251. data/schemas/tlc/1.0.9/sxl.yaml +56 -56
  252. data/schemas/tlc/1.0.9/sxl_index.json +226 -244
  253. data/schemas/tlc/1.1.0/alarms/A0007.json +25 -19
  254. data/schemas/tlc/1.1.0/alarms/A0008.json +21 -15
  255. data/schemas/tlc/1.1.0/alarms/A0201.json +26 -20
  256. data/schemas/tlc/1.1.0/alarms/A0202.json +26 -20
  257. data/schemas/tlc/1.1.0/alarms/A0301.json +69 -63
  258. data/schemas/tlc/1.1.0/alarms/A0302.json +87 -81
  259. data/schemas/tlc/1.1.0/alarms/A0303.json +69 -63
  260. data/schemas/tlc/1.1.0/alarms/A0304.json +87 -81
  261. data/schemas/tlc/1.1.0/commands/M0001.json +66 -60
  262. data/schemas/tlc/1.1.0/commands/M0002.json +48 -42
  263. data/schemas/tlc/1.1.0/commands/M0003.json +48 -42
  264. data/schemas/tlc/1.1.0/commands/M0004.json +35 -29
  265. data/schemas/tlc/1.1.0/commands/M0005.json +48 -42
  266. data/schemas/tlc/1.1.0/commands/M0006.json +48 -42
  267. data/schemas/tlc/1.1.0/commands/M0007.json +35 -29
  268. data/schemas/tlc/1.1.0/commands/M0008.json +48 -42
  269. data/schemas/tlc/1.1.0/commands/M0010.json +35 -29
  270. data/schemas/tlc/1.1.0/commands/M0011.json +35 -29
  271. data/schemas/tlc/1.1.0/commands/M0012.json +35 -29
  272. data/schemas/tlc/1.1.0/commands/M0013.json +35 -29
  273. data/schemas/tlc/1.1.0/commands/M0014.json +48 -42
  274. data/schemas/tlc/1.1.0/commands/M0015.json +48 -42
  275. data/schemas/tlc/1.1.0/commands/M0016.json +35 -29
  276. data/schemas/tlc/1.1.0/commands/M0017.json +35 -29
  277. data/schemas/tlc/1.1.0/commands/M0018.json +48 -42
  278. data/schemas/tlc/1.1.0/commands/M0019.json +61 -55
  279. data/schemas/tlc/1.1.0/commands/M0020.json +61 -55
  280. data/schemas/tlc/1.1.0/commands/M0021.json +35 -29
  281. data/schemas/tlc/1.1.0/commands/M0022.json +194 -188
  282. data/schemas/tlc/1.1.0/commands/M0023.json +35 -29
  283. data/schemas/tlc/1.1.0/commands/M0103.json +51 -45
  284. data/schemas/tlc/1.1.0/commands/M0104.json +100 -94
  285. data/schemas/tlc/1.1.0/commands/command_requests.json +1525 -1
  286. data/schemas/tlc/1.1.0/defs/guards.json +19 -0
  287. data/schemas/tlc/1.1.0/statuses/S0033.json +2 -2
  288. data/schemas/tlc/1.1.0/sxl.yaml +75 -74
  289. data/schemas/tlc/1.1.0/sxl_index.json +391 -398
  290. data/schemas/tlc/1.2.0/alarms/A0007.json +25 -19
  291. data/schemas/tlc/1.2.0/alarms/A0008.json +21 -15
  292. data/schemas/tlc/1.2.0/alarms/A0201.json +26 -20
  293. data/schemas/tlc/1.2.0/alarms/A0202.json +26 -20
  294. data/schemas/tlc/1.2.0/alarms/A0301.json +69 -63
  295. data/schemas/tlc/1.2.0/alarms/A0302.json +87 -81
  296. data/schemas/tlc/1.2.0/alarms/A0303.json +69 -63
  297. data/schemas/tlc/1.2.0/alarms/A0304.json +87 -81
  298. data/schemas/tlc/1.2.0/commands/M0001.json +66 -60
  299. data/schemas/tlc/1.2.0/commands/M0002.json +48 -42
  300. data/schemas/tlc/1.2.0/commands/M0003.json +48 -42
  301. data/schemas/tlc/1.2.0/commands/M0004.json +35 -29
  302. data/schemas/tlc/1.2.0/commands/M0005.json +48 -42
  303. data/schemas/tlc/1.2.0/commands/M0006.json +48 -42
  304. data/schemas/tlc/1.2.0/commands/M0007.json +35 -29
  305. data/schemas/tlc/1.2.0/commands/M0008.json +48 -42
  306. data/schemas/tlc/1.2.0/commands/M0010.json +35 -29
  307. data/schemas/tlc/1.2.0/commands/M0011.json +35 -29
  308. data/schemas/tlc/1.2.0/commands/M0012.json +35 -29
  309. data/schemas/tlc/1.2.0/commands/M0013.json +35 -29
  310. data/schemas/tlc/1.2.0/commands/M0014.json +48 -42
  311. data/schemas/tlc/1.2.0/commands/M0015.json +48 -42
  312. data/schemas/tlc/1.2.0/commands/M0016.json +35 -29
  313. data/schemas/tlc/1.2.0/commands/M0017.json +35 -29
  314. data/schemas/tlc/1.2.0/commands/M0018.json +48 -42
  315. data/schemas/tlc/1.2.0/commands/M0019.json +61 -55
  316. data/schemas/tlc/1.2.0/commands/M0020.json +61 -55
  317. data/schemas/tlc/1.2.0/commands/M0021.json +35 -29
  318. data/schemas/tlc/1.2.0/commands/M0022.json +194 -188
  319. data/schemas/tlc/1.2.0/commands/M0023.json +35 -29
  320. data/schemas/tlc/1.2.0/commands/M0103.json +51 -45
  321. data/schemas/tlc/1.2.0/commands/M0104.json +100 -94
  322. data/schemas/tlc/1.2.0/commands/command_requests.json +1525 -1
  323. data/schemas/tlc/1.2.0/defs/guards.json +19 -0
  324. data/schemas/tlc/1.2.0/statuses/S0033.json +2 -2
  325. data/schemas/tlc/1.2.0/sxl.yaml +77 -76
  326. data/schemas/tlc/1.2.0/sxl_index.json +401 -397
  327. data/schemas/tlc/1.2.1/alarms/A0007.json +25 -19
  328. data/schemas/tlc/1.2.1/alarms/A0008.json +21 -15
  329. data/schemas/tlc/1.2.1/alarms/A0201.json +26 -20
  330. data/schemas/tlc/1.2.1/alarms/A0202.json +26 -20
  331. data/schemas/tlc/1.2.1/alarms/A0301.json +69 -63
  332. data/schemas/tlc/1.2.1/alarms/A0302.json +87 -81
  333. data/schemas/tlc/1.2.1/alarms/A0303.json +69 -63
  334. data/schemas/tlc/1.2.1/alarms/A0304.json +87 -81
  335. data/schemas/tlc/1.2.1/commands/M0001.json +66 -60
  336. data/schemas/tlc/1.2.1/commands/M0002.json +48 -42
  337. data/schemas/tlc/1.2.1/commands/M0003.json +48 -42
  338. data/schemas/tlc/1.2.1/commands/M0004.json +35 -29
  339. data/schemas/tlc/1.2.1/commands/M0005.json +48 -42
  340. data/schemas/tlc/1.2.1/commands/M0006.json +48 -42
  341. data/schemas/tlc/1.2.1/commands/M0007.json +35 -29
  342. data/schemas/tlc/1.2.1/commands/M0008.json +48 -42
  343. data/schemas/tlc/1.2.1/commands/M0010.json +35 -29
  344. data/schemas/tlc/1.2.1/commands/M0011.json +35 -29
  345. data/schemas/tlc/1.2.1/commands/M0012.json +35 -29
  346. data/schemas/tlc/1.2.1/commands/M0013.json +35 -29
  347. data/schemas/tlc/1.2.1/commands/M0014.json +48 -42
  348. data/schemas/tlc/1.2.1/commands/M0015.json +48 -42
  349. data/schemas/tlc/1.2.1/commands/M0016.json +35 -29
  350. data/schemas/tlc/1.2.1/commands/M0017.json +35 -29
  351. data/schemas/tlc/1.2.1/commands/M0018.json +48 -42
  352. data/schemas/tlc/1.2.1/commands/M0019.json +61 -55
  353. data/schemas/tlc/1.2.1/commands/M0020.json +61 -55
  354. data/schemas/tlc/1.2.1/commands/M0021.json +35 -29
  355. data/schemas/tlc/1.2.1/commands/M0022.json +194 -188
  356. data/schemas/tlc/1.2.1/commands/M0023.json +35 -29
  357. data/schemas/tlc/1.2.1/commands/M0103.json +51 -45
  358. data/schemas/tlc/1.2.1/commands/M0104.json +100 -94
  359. data/schemas/tlc/1.2.1/commands/command_requests.json +1525 -1
  360. data/schemas/tlc/1.2.1/defs/guards.json +19 -0
  361. data/schemas/tlc/1.2.1/statuses/S0033.json +2 -2
  362. data/schemas/tlc/1.2.1/sxl.yaml +77 -76
  363. data/schemas/tlc/1.2.1/sxl_index.json +401 -397
  364. data/schemas/tlc/1.3.0/alarms/A0007.json +25 -19
  365. data/schemas/tlc/1.3.0/alarms/A0008.json +21 -15
  366. data/schemas/tlc/1.3.0/alarms/A0201.json +26 -20
  367. data/schemas/tlc/1.3.0/alarms/A0202.json +26 -20
  368. data/schemas/tlc/1.3.0/alarms/A0301.json +69 -63
  369. data/schemas/tlc/1.3.0/alarms/A0302.json +87 -81
  370. data/schemas/tlc/1.3.0/alarms/A0303.json +70 -64
  371. data/schemas/tlc/1.3.0/alarms/A0304.json +88 -82
  372. data/schemas/tlc/1.3.0/commands/M0001.json +66 -60
  373. data/schemas/tlc/1.3.0/commands/M0002.json +48 -42
  374. data/schemas/tlc/1.3.0/commands/M0003.json +48 -42
  375. data/schemas/tlc/1.3.0/commands/M0004.json +35 -29
  376. data/schemas/tlc/1.3.0/commands/M0005.json +48 -42
  377. data/schemas/tlc/1.3.0/commands/M0006.json +48 -42
  378. data/schemas/tlc/1.3.0/commands/M0007.json +35 -29
  379. data/schemas/tlc/1.3.0/commands/M0008.json +61 -55
  380. data/schemas/tlc/1.3.0/commands/M0010.json +35 -29
  381. data/schemas/tlc/1.3.0/commands/M0011.json +35 -29
  382. data/schemas/tlc/1.3.0/commands/M0012.json +35 -29
  383. data/schemas/tlc/1.3.0/commands/M0013.json +36 -30
  384. data/schemas/tlc/1.3.0/commands/M0014.json +48 -42
  385. data/schemas/tlc/1.3.0/commands/M0015.json +48 -42
  386. data/schemas/tlc/1.3.0/commands/M0016.json +35 -29
  387. data/schemas/tlc/1.3.0/commands/M0017.json +35 -29
  388. data/schemas/tlc/1.3.0/commands/M0018.json +48 -42
  389. data/schemas/tlc/1.3.0/commands/M0019.json +61 -55
  390. data/schemas/tlc/1.3.0/commands/M0020.json +61 -55
  391. data/schemas/tlc/1.3.0/commands/M0021.json +35 -29
  392. data/schemas/tlc/1.3.0/commands/M0022.json +195 -189
  393. data/schemas/tlc/1.3.0/commands/M0023.json +35 -29
  394. data/schemas/tlc/1.3.0/commands/M0024.json +21 -15
  395. data/schemas/tlc/1.3.0/commands/M0103.json +51 -45
  396. data/schemas/tlc/1.3.0/commands/M0104.json +100 -94
  397. data/schemas/tlc/1.3.0/commands/command_requests.json +1559 -1
  398. data/schemas/tlc/1.3.0/defs/guards.json +19 -0
  399. data/schemas/tlc/1.3.0/statuses/S0001.json +4 -4
  400. data/schemas/tlc/1.3.0/statuses/S0005.json +3 -3
  401. data/schemas/tlc/1.3.0/statuses/S0006.json +2 -2
  402. data/schemas/tlc/1.3.0/statuses/S0007.json +1 -1
  403. data/schemas/tlc/1.3.0/statuses/S0008.json +1 -1
  404. data/schemas/tlc/1.3.0/statuses/S0009.json +1 -1
  405. data/schemas/tlc/1.3.0/statuses/S0010.json +1 -1
  406. data/schemas/tlc/1.3.0/statuses/S0011.json +1 -1
  407. data/schemas/tlc/1.3.0/statuses/S0012.json +1 -1
  408. data/schemas/tlc/1.3.0/statuses/S0013.json +1 -1
  409. data/schemas/tlc/1.3.0/statuses/S0014.json +1 -1
  410. data/schemas/tlc/1.3.0/statuses/S0015.json +1 -1
  411. data/schemas/tlc/1.3.0/statuses/S0016.json +1 -1
  412. data/schemas/tlc/1.3.0/statuses/S0017.json +1 -1
  413. data/schemas/tlc/1.3.0/statuses/S0019.json +1 -1
  414. data/schemas/tlc/1.3.0/statuses/S0020.json +1 -1
  415. data/schemas/tlc/1.3.0/statuses/S0025.json +2 -2
  416. data/schemas/tlc/1.3.0/statuses/S0027.json +1 -1
  417. data/schemas/tlc/1.3.0/statuses/S0032.json +2 -2
  418. data/schemas/tlc/1.3.0/statuses/S0033.json +5 -5
  419. data/schemas/tlc/1.3.0/statuses/S0034.json +1 -1
  420. data/schemas/tlc/1.3.0/statuses/S0035.json +1 -1
  421. data/schemas/tlc/1.3.0/statuses/S0091.json +1 -1
  422. data/schemas/tlc/1.3.0/statuses/S0092.json +1 -1
  423. data/schemas/tlc/1.3.0/statuses/S0096.json +6 -6
  424. data/schemas/tlc/1.3.0/statuses/S0201.json +1 -1
  425. data/schemas/tlc/1.3.0/statuses/S0202.json +1 -1
  426. data/schemas/tlc/1.3.0/statuses/S0203.json +1 -1
  427. data/schemas/tlc/1.3.0/statuses/S0204.json +9 -9
  428. data/schemas/tlc/1.3.0/statuses/S0205.json +1 -1
  429. data/schemas/tlc/1.3.0/statuses/S0206.json +1 -1
  430. data/schemas/tlc/1.3.0/statuses/S0207.json +1 -1
  431. data/schemas/tlc/1.3.0/statuses/S0208.json +9 -9
  432. data/schemas/tlc/1.3.0/sxl_index.json +408 -402
  433. metadata +11 -1
@@ -11,15 +11,25 @@ module RSMP
11
11
 
12
12
  def match_value?(item)
13
13
  return false if @want['q'] && @want['q'] != item['q']
14
+ return true unless @want.key?('s')
14
15
 
15
- if @want['s'].is_a? Regexp
16
- return false if item['s'] !~ @want['s']
17
- elsif @want['s']
18
- return false if item['s'] != @want['s']
16
+ want = @want['s']
17
+ got = item['s']
18
+ if want.is_a? Regexp
19
+ return false unless regex_match?(got, want)
20
+ elsif got != want
21
+ return false
19
22
  end
20
23
  true
21
24
  end
22
25
 
26
+ def regex_match?(got, want)
27
+ return got =~ want if got.is_a?(String)
28
+ return got.any? { |item| item.is_a?(String) && item =~ want } if got.is_a?(Array)
29
+
30
+ false
31
+ end
32
+
23
33
  def match(item)
24
34
  return nil unless match_code(item)
25
35
 
@@ -18,7 +18,7 @@ module RSMP
18
18
  'cCI' => command_code_id.to_s,
19
19
  'cO' => command_name.to_s,
20
20
  'n' => n.to_s,
21
- 'v' => v.to_s
21
+ 'v' => v
22
22
  }
23
23
  end
24
24
  end
@@ -0,0 +1,63 @@
1
+ module RSMP
2
+ # Validation helpers for RSMP runtime configuration hashes and files.
3
+ module Config
4
+ class << self
5
+ def validate(settings = {}, type:, source: nil, log_settings: nil)
6
+ options_class_for(type).new(settings, source: source, log_settings: log_settings)
7
+ end
8
+
9
+ def load_file(path, type:)
10
+ raise RSMP::ConfigurationError, 'not found' unless File.exist?(path)
11
+ raise RSMP::ConfigurationError, 'is not a file' unless File.file?(path)
12
+ raise RSMP::ConfigurationError, 'must be a YAML file (.yml or .yaml)' unless yaml_file?(path)
13
+
14
+ raw = YAML.load_file(path)
15
+ raise RSMP::ConfigurationError, "Config #{path} must be a hash" unless raw.is_a?(Hash) || raw.nil?
16
+
17
+ raw ||= {}
18
+ settings = raw.dup
19
+ log_settings = settings.delete('log') || {}
20
+ validate(settings, type: resolve_type(type, settings), log_settings: log_settings)
21
+ rescue Psych::SyntaxError => e
22
+ raise RSMP::ConfigurationError, "Cannot read config file #{path}: #{e}"
23
+ end
24
+
25
+ def types
26
+ %w[site supervisor tlc]
27
+ end
28
+
29
+ private
30
+
31
+ def yaml_file?(path)
32
+ %w[.yml .yaml].include?(File.extname(path).downcase)
33
+ end
34
+
35
+ def resolve_type(type, settings)
36
+ type = type.to_s
37
+ return infer_type(settings) if type == 'auto'
38
+
39
+ type
40
+ end
41
+
42
+ def infer_type(settings)
43
+ return 'supervisor' if settings.key?('sites')
44
+ return 'site' if settings.key?('supervisors')
45
+
46
+ raise RSMP::ConfigurationError, 'Cannot infer config type; use --type site, --type tlc or --type supervisor'
47
+ end
48
+
49
+ def options_class_for(type)
50
+ case type.to_s
51
+ when 'site'
52
+ RSMP::Site::Options
53
+ when 'supervisor'
54
+ RSMP::Supervisor::Options
55
+ when 'tlc'
56
+ RSMP::TLC::TrafficControllerSite::Options
57
+ else
58
+ raise RSMP::ConfigurationError, "Unknown config type #{type.inspect}, expected one of #{types.join(', ')}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,64 @@
1
+ module RSMP
2
+ module Convert
3
+ module Export
4
+ # Converts SXL definitions to JSON Schema files.
5
+ module JSONSchema
6
+ def self.output_sxl_index(out, sxl)
7
+ out['sxl_index.json'] = output_json({
8
+ 'meta' => sxl[:meta],
9
+ 'statuses' => index_items(sxl[:statuses]),
10
+ 'commands' => index_items(sxl[:commands]),
11
+ 'alarms' => index_items(sxl[:alarms])
12
+ })
13
+ end
14
+
15
+ def self.index_items(items)
16
+ items.keys.sort.to_h do |key|
17
+ [key, index_item(items[key])]
18
+ end
19
+ end
20
+
21
+ def self.index_item(item)
22
+ arguments = item['arguments'] || {}
23
+ entry = {}
24
+ required = typed_arguments(arguments.reject { |_name, argument| argument['optional'] == true })
25
+ optional = typed_arguments(arguments.select { |_name, argument| argument['optional'] == true })
26
+ entry['required'] = required unless required.empty?
27
+ entry['optional'] = optional unless optional.empty?
28
+ entry
29
+ end
30
+
31
+ def self.typed_arguments(arguments)
32
+ arguments.keys.sort.to_h do |name|
33
+ [name, argument_type_descriptor(arguments[name])]
34
+ end
35
+ end
36
+
37
+ def self.argument_type_descriptor(argument)
38
+ type = argument['type']
39
+ case type
40
+ when 'array'
41
+ argument_array_descriptor(argument)
42
+ when 'object'
43
+ argument_object_descriptor(argument)
44
+ else
45
+ type
46
+ end
47
+ end
48
+
49
+ def self.argument_array_descriptor(argument)
50
+ descriptor = { 'type' => argument['type'] }
51
+ descriptor['items'] = typed_arguments(argument['items']) if argument['items'].is_a?(Hash)
52
+ descriptor
53
+ end
54
+
55
+ def self.argument_object_descriptor(argument)
56
+ descriptor = { 'type' => argument['type'] }
57
+ properties = argument['properties'] || argument['items']
58
+ descriptor['properties'] = typed_arguments(properties) if properties.is_a?(Hash)
59
+ descriptor
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -42,12 +42,20 @@ module RSMP
42
42
  'then' => { 'properties' => { property_key => build_value(argument) } }
43
43
  }
44
44
  end
45
- {
45
+ schema = {
46
46
  '$schema' => 'https://json-schema.org/draft/2020-12/schema',
47
47
  'description' => item['description'],
48
48
  'properties' => { 'n' => { 'enum' => arguments.keys.sort } },
49
49
  'allOf' => rules
50
50
  }
51
+ return schema unless property_key == 'v'
52
+
53
+ schema.delete 'allOf'
54
+ schema.merge(
55
+ 'if' => { '$ref' => '../defs/guards.json#/$defs/age_unknown_or_undefined' },
56
+ 'then' => {},
57
+ 'else' => { 'allOf' => rules }
58
+ )
51
59
  end
52
60
  end
53
61
  end
@@ -11,6 +11,12 @@ module RSMP
11
11
  { 'required' => ['q'] },
12
12
  { 'properties' => { 'q' => { 'enum' => %w[undefined unknown] } } }
13
13
  ]
14
+ },
15
+ 'age_unknown_or_undefined' => {
16
+ 'allOf' => [
17
+ { 'required' => ['age'] },
18
+ { 'properties' => { 'age' => { 'enum' => %w[undefined unknown] } } }
19
+ ]
14
20
  }
15
21
  }
16
22
  }.freeze
@@ -67,6 +73,15 @@ module RSMP
67
73
 
68
74
  # convert commands to json schema
69
75
  def self.output_commands(out, items)
76
+ out['defs/guards.json'] ||= output_json(GUARDS_JSON)
77
+ out['commands/commands.json'] = output_json commands_schema(items)
78
+ out['commands/command_requests.json'] = output_json command_requests_schema(items)
79
+ out['commands/command_responses.json'] = output_json command_responses_schema
80
+
81
+ items.each_pair { |key, item| output_command out, key, item }
82
+ end
83
+
84
+ def self.commands_schema(items)
70
85
  list = [{ 'properties' => { 'cCI' => { 'enum' => items.keys.sort } } }]
71
86
  items.keys.sort.each do |key|
72
87
  list << {
@@ -74,25 +89,67 @@ module RSMP
74
89
  'then' => { '$ref' => "#{key}.json" }
75
90
  }
76
91
  end
77
- json = {
92
+ {
78
93
  '$schema' => 'https://json-schema.org/draft/2020-12/schema',
79
94
  'items' => { 'allOf' => list }
80
95
  }
81
- out['commands/commands.json'] = output_json json
96
+ end
82
97
 
83
- json = {
98
+ def self.command_requests_schema(items)
99
+ {
84
100
  '$schema' => 'https://json-schema.org/draft/2020-12/schema',
85
- 'properties' => { 'arg' => { '$ref' => 'commands.json' } }
101
+ 'properties' => { 'arg' => command_request_arg_schema(items) }
86
102
  }
87
- out['commands/command_requests.json'] = output_json json
103
+ end
88
104
 
89
- json = {
105
+ def self.command_responses_schema
106
+ {
90
107
  '$schema' => 'https://json-schema.org/draft/2020-12/schema',
91
108
  'properties' => { 'rvs' => { '$ref' => 'commands.json' } }
92
109
  }
93
- out['commands/command_responses.json'] = output_json json
110
+ end
94
111
 
95
- items.each_pair { |key, item| output_command out, key, item }
112
+ def self.command_request_arg_schema(items)
113
+ schema = { '$ref' => 'commands.json' }
114
+ required_rules = command_required_argument_rules(items)
115
+ return schema if required_rules.empty?
116
+
117
+ { 'allOf' => [schema] + required_rules }
118
+ end
119
+
120
+ def self.command_required_argument_rules(items)
121
+ items.keys.sort.filter_map do |key|
122
+ required = required_argument_names(items[key])
123
+ next if required.empty?
124
+
125
+ {
126
+ 'if' => {
127
+ 'contains' => {
128
+ 'required' => ['cCI'],
129
+ 'properties' => { 'cCI' => { 'const' => key } }
130
+ }
131
+ },
132
+ 'then' => {
133
+ 'allOf' => required.map { |name| command_argument_contains_rule(key, name) }
134
+ }
135
+ }
136
+ end
137
+ end
138
+
139
+ def self.command_argument_contains_rule(command_code, name)
140
+ {
141
+ 'contains' => {
142
+ 'required' => %w[cCI n],
143
+ 'properties' => {
144
+ 'cCI' => { 'const' => command_code },
145
+ 'n' => { 'const' => name }
146
+ }
147
+ }
148
+ }
149
+ end
150
+
151
+ def self.required_argument_names(item)
152
+ (item['arguments'] || {}).reject { |_name, argument| argument['optional'] == true }.keys.sort
96
153
  end
97
154
 
98
155
  # convert a command to json schema
@@ -103,23 +160,6 @@ module RSMP
103
160
  out["commands/#{key}.json"] = output_json json
104
161
  end
105
162
 
106
- def self.output_sxl_index(out, sxl)
107
- out['sxl_index.json'] = output_json({
108
- 'meta' => sxl[:meta],
109
- 'statuses' => index_items(sxl[:statuses]),
110
- 'commands' => index_items(sxl[:commands]),
111
- 'alarms' => index_items(sxl[:alarms])
112
- })
113
- end
114
-
115
- def self.index_items(items)
116
- items.keys.sort.to_h do |key|
117
- [key, {
118
- 'arguments' => (items[key]['arguments'] || {}).keys.sort
119
- }]
120
- end
121
- end
122
-
123
163
  # output the json schema root
124
164
  def self.output_root(out, meta)
125
165
  json = {
@@ -3,6 +3,21 @@ module RSMP
3
3
  module Export
4
4
  # Converts SXL definitions to JSON Schema files.
5
5
  module JSONSchema
6
+ DIRECT_JSON_TYPES = {
7
+ 'boolean' => 'boolean',
8
+ 'integer' => 'integer',
9
+ 'number' => 'number',
10
+ 'object' => 'object',
11
+ 'null' => 'null'
12
+ }.freeze
13
+
14
+ DEFINITION_REFS = {
15
+ 'boolean_as_string' => '../defs/definitions.json#/boolean',
16
+ 'timestamp' => '../defs/definitions.json#/timestamp',
17
+ 'integer_as_string' => '../defs/definitions.json#/integer',
18
+ 'long_as_string' => '../defs/definitions.json#/integer'
19
+ }.freeze
20
+
6
21
  # convert a yaml item to json schema
7
22
  def self.build_value(item)
8
23
  out = {}
@@ -19,20 +34,27 @@ module RSMP
19
34
 
20
35
  # convert an item which is not a string-list, to json schema
21
36
  def self.handle_types(item, out)
22
- case item['type']
23
- when 'boolean'
24
- out['$ref'] = '../defs/definitions.json#/boolean'
25
- when 'timestamp'
26
- out['$ref'] = '../defs/definitions.json#/timestamp'
27
- when 'integer', 'ordinal', 'unit', 'scale', 'long'
28
- out['$ref'] = '../defs/definitions.json#/integer'
29
- when 'array' # a json array
37
+ type = item['type']
38
+ return build_json_array_type(item, out) if type == 'array'
39
+ return build_number_as_string_type(out) if type == 'number_as_string'
40
+ return out['$ref'] = DEFINITION_REFS[type] if DEFINITION_REFS.key?(type)
41
+
42
+ out['type'] = DIRECT_JSON_TYPES.fetch(type, 'string')
43
+ end
44
+
45
+ def self.build_json_array_type(item, out)
46
+ if item['items']
30
47
  build_json_array item['items'], out
31
- else # string, base64, and any unknown types
32
- out['type'] = 'string'
48
+ else
49
+ out['type'] = 'array'
33
50
  end
34
51
  end
35
52
 
53
+ def self.build_number_as_string_type(out)
54
+ out['type'] = 'string'
55
+ out['pattern'] = '^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?$'
56
+ end
57
+
36
58
  # convert an yaml item with type: array to json schema
37
59
  def self.build_json_array(item, out)
38
60
  required = item.reject { |_k, v| v['optional'] == true }.keys.sort
@@ -83,7 +105,14 @@ module RSMP
83
105
  def self.handle_enum(item, out)
84
106
  return unless item['values']
85
107
 
86
- out['enum'] = stringify_values(enum_keys(item))
108
+ values = enum_keys(item)
109
+ values = stringify_values(values) if string_type? item
110
+ out['enum'] = values
111
+ end
112
+
113
+ def self.string_type?(item)
114
+ type = item['type'].to_s
115
+ type == 'string' || type.end_with?('_as_string') || %w[base64 timestamp].include?(type)
87
116
  end
88
117
 
89
118
  def self.enum_keys(item)
@@ -7,6 +7,7 @@ require 'fileutils'
7
7
  require_relative 'json_schema/values'
8
8
  require_relative 'json_schema/items'
9
9
  require_relative 'json_schema/outputs'
10
+ require_relative 'json_schema/index'
10
11
 
11
12
  module RSMP
12
13
  module Convert
@@ -19,7 +20,8 @@ module RSMP
19
20
  object_nl: "\n",
20
21
  indent: ' ',
21
22
  space_before: ' ',
22
- space: ' '
23
+ space: ' ',
24
+ ascii_only: true
23
25
  }.freeze
24
26
 
25
27
  def self.output_json(item)
@@ -0,0 +1,194 @@
1
+ module RSMP
2
+ # Encodes and decodes SXL argument values for message payloads.
3
+ module MessageSxlCodec
4
+ SXL_ITEM_SHAPES = {
5
+ 'StatusResponse' => { kind: :statuses, list: 'sS', code: 'sCI', name: 'n', value: 's' },
6
+ 'StatusUpdate' => { kind: :statuses, list: 'sS', code: 'sCI', name: 'n', value: 's' },
7
+ 'CommandRequest' => { kind: :commands, list: 'arg', code: 'cCI', name: 'n', value: 'v' },
8
+ 'CommandResponse' => { kind: :commands, list: 'rvs', code: 'cCI', name: 'n', value: 'v' },
9
+ 'Alarm' => { kind: :alarms, list: 'rvs', code: nil, name: 'n', value: 'v' }
10
+ }.freeze
11
+
12
+ def self.included(base)
13
+ base.extend ClassMethods
14
+ end
15
+
16
+ def encode_for(schemas)
17
+ transform_sxl_items(schemas, :encode_sxl_value)
18
+ self
19
+ end
20
+
21
+ def decode_for(schemas)
22
+ transform_sxl_items(schemas, :decode_sxl_value)
23
+ self
24
+ end
25
+
26
+ private
27
+
28
+ def transform_sxl_items(schemas, transformer)
29
+ shape = SXL_ITEM_SHAPES[type]
30
+ return unless shape
31
+
32
+ resolved = RSMP::Schema.resolve_sxl(@attributes, schemas: schemas)
33
+ return unless resolved
34
+
35
+ sxl_type, version = resolved
36
+ Array(@attributes[shape[:list]]).each do |item|
37
+ transform_sxl_item(item, shape, sxl_type, version, transformer)
38
+ end
39
+ end
40
+
41
+ def transform_sxl_item(item, shape, sxl_type, version, transformer)
42
+ item_code = shape[:code] ? item[shape[:code]] : @attributes['aCId']
43
+ name = item[shape[:name]]
44
+ return unless item_code && name && item.key?(shape[:value])
45
+
46
+ descriptor = RSMP::Schema.sxl_argument_descriptor(sxl_type, version, shape[:kind], item_code, name)
47
+ return unless descriptor
48
+
49
+ item[shape[:value]] = self.class.public_send(transformer, item[shape[:value]], descriptor)
50
+ end
51
+
52
+ # Class-level value transforms used by Message and command collectors.
53
+ module ClassMethods
54
+ STRING_TYPES = %w[string base64 timestamp].freeze
55
+ INTEGER_AS_STRING_TYPES = %w[integer_as_string long_as_string].freeze
56
+
57
+ def encode_sxl_value(value, descriptor)
58
+ return nil if value.nil?
59
+
60
+ type = descriptor_type(descriptor)
61
+ return encode_sxl_boolean(value) if type == 'boolean_as_string'
62
+ return encode_sxl_list(value) if list_type?(type)
63
+ return value.is_a?(String) ? value : value.to_s if encode_string_type?(type)
64
+ return encode_sxl_array(value, descriptor) if type == 'array'
65
+ return encode_sxl_object(value, descriptor['properties']) if type == 'object'
66
+
67
+ value
68
+ end
69
+
70
+ def decode_sxl_value(value, descriptor)
71
+ return nil if value.nil?
72
+
73
+ type = descriptor_type(descriptor)
74
+ return decode_sxl_boolean(value) if type == 'boolean_as_string'
75
+ return decode_sxl_integer(value) if INTEGER_AS_STRING_TYPES.include?(type)
76
+ return decode_sxl_number(value) if type == 'number_as_string'
77
+ return decode_sxl_list(value, type) if list_type?(type)
78
+ return decode_sxl_array(value, descriptor) if type == 'array'
79
+ return decode_sxl_object(value, descriptor['properties']) if type == 'object'
80
+
81
+ value
82
+ end
83
+
84
+ def descriptor_type(descriptor)
85
+ descriptor.is_a?(Hash) ? descriptor['type'] : descriptor.to_s
86
+ end
87
+
88
+ def list_type?(type)
89
+ type.match?(/_list(_as_string)?\z/)
90
+ end
91
+
92
+ def encode_string_type?(type)
93
+ STRING_TYPES.include?(type) || INTEGER_AS_STRING_TYPES.include?(type) || type == 'number_as_string'
94
+ end
95
+
96
+ def encode_sxl_boolean(value)
97
+ case value
98
+ when true
99
+ 'True'
100
+ when false
101
+ 'False'
102
+ else
103
+ value
104
+ end
105
+ end
106
+
107
+ def encode_sxl_list(value)
108
+ return value if value.is_a?(String)
109
+ return encode_sxl_boolean(value).to_s unless value.is_a?(Array)
110
+
111
+ value.map { |item| encode_sxl_boolean(item) }.join(',')
112
+ end
113
+
114
+ def encode_sxl_array(value, descriptor)
115
+ return value unless value.is_a?(Array)
116
+
117
+ items = descriptor['items']
118
+ return value unless items.is_a?(Hash)
119
+
120
+ value.map { |item| encode_sxl_object(item, items) }
121
+ end
122
+
123
+ def encode_sxl_object(value, properties)
124
+ transform_sxl_object(value, properties, :encode_sxl_value)
125
+ end
126
+
127
+ def decode_sxl_boolean(value)
128
+ case value
129
+ when 'True'
130
+ true
131
+ when 'False'
132
+ false
133
+ else
134
+ value
135
+ end
136
+ end
137
+
138
+ def decode_sxl_integer(value)
139
+ return value unless value.is_a?(String) && value.match?(/\A[+-]?\d+\z/)
140
+
141
+ value.to_i
142
+ end
143
+
144
+ def decode_sxl_number(value)
145
+ return value unless value.is_a?(String)
146
+
147
+ Float(value)
148
+ rescue ArgumentError
149
+ value
150
+ end
151
+
152
+ def decode_sxl_list(value, type)
153
+ items = value.is_a?(String) ? value.split(',') : Array(value)
154
+ descriptor = list_item_descriptor(type)
155
+ items.map { |item| decode_sxl_value(item, descriptor) }
156
+ end
157
+
158
+ def list_item_descriptor(type)
159
+ case type
160
+ when /\Aboolean_list/ then 'boolean_as_string'
161
+ when /\Ainteger_list/ then 'integer_as_string'
162
+ when /\Anumber_list/ then 'number_as_string'
163
+ else 'string'
164
+ end
165
+ end
166
+
167
+ def decode_sxl_array(value, descriptor)
168
+ return value unless value.is_a?(Array)
169
+
170
+ items = descriptor['items']
171
+ return value unless items.is_a?(Hash)
172
+
173
+ value.map { |item| decode_sxl_array_item(item, items) }
174
+ end
175
+
176
+ def decode_sxl_array_item(item, descriptor)
177
+ descriptor['type'] ? decode_sxl_value(item, descriptor) : decode_sxl_object(item, descriptor)
178
+ end
179
+
180
+ def decode_sxl_object(value, properties)
181
+ transform_sxl_object(value, properties, :decode_sxl_value)
182
+ end
183
+
184
+ def transform_sxl_object(value, properties, transformer)
185
+ return value unless value.is_a?(Hash) && properties.is_a?(Hash)
186
+
187
+ value.each_with_object({}) do |(key, item_value), memo|
188
+ descriptor = properties[key] || properties[key.to_sym]
189
+ memo[key] = descriptor ? public_send(transformer, item_value, descriptor) : item_value
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
data/lib/rsmp/message.rb CHANGED
@@ -2,6 +2,8 @@
2
2
  module RSMP
3
3
  # Base RSMP message class used to represent parsed and built messages.
4
4
  class Message
5
+ include MessageSxlCodec
6
+
5
7
  attr_reader :now, :attributes, :out, :timestamp # this is an internal timestamp recording when we receive/send
6
8
  attr_accessor :json, :direction
7
9