rsmp 0.37.0 → 0.38.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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/devcontainer.json +22 -0
  3. data/.github/workflows/rubocop.yaml +17 -0
  4. data/.gitignore +5 -6
  5. data/.rubocop.yml +80 -0
  6. data/Gemfile +13 -1
  7. data/Gemfile.lock +34 -1
  8. data/Rakefile +3 -3
  9. data/lib/rsmp/cli.rb +147 -124
  10. data/lib/rsmp/collect/ack_collector.rb +8 -7
  11. data/lib/rsmp/collect/aggregated_status_collector.rb +4 -4
  12. data/lib/rsmp/collect/alarm_collector.rb +31 -23
  13. data/lib/rsmp/collect/alarm_matcher.rb +3 -3
  14. data/lib/rsmp/collect/collector/logging.rb +17 -0
  15. data/lib/rsmp/collect/collector/reporting.rb +44 -0
  16. data/lib/rsmp/collect/collector/status.rb +34 -0
  17. data/lib/rsmp/collect/collector.rb +69 -150
  18. data/lib/rsmp/collect/command_matcher.rb +19 -6
  19. data/lib/rsmp/collect/command_response_collector.rb +7 -7
  20. data/lib/rsmp/collect/distributor.rb +14 -11
  21. data/lib/rsmp/collect/filter.rb +31 -15
  22. data/lib/rsmp/collect/matcher.rb +7 -11
  23. data/lib/rsmp/collect/queue.rb +4 -4
  24. data/lib/rsmp/collect/receiver.rb +10 -12
  25. data/lib/rsmp/collect/state_collector.rb +116 -77
  26. data/lib/rsmp/collect/status_collector.rb +6 -6
  27. data/lib/rsmp/collect/status_matcher.rb +17 -7
  28. data/lib/rsmp/{alarm_state.rb → component/alarm_state.rb} +76 -37
  29. data/lib/rsmp/{component.rb → component/component.rb} +15 -15
  30. data/lib/rsmp/component/component_base.rb +89 -0
  31. data/lib/rsmp/component/component_proxy.rb +75 -0
  32. data/lib/rsmp/component/components.rb +63 -0
  33. data/lib/rsmp/convert/export/json_schema.rb +116 -110
  34. data/lib/rsmp/convert/import/yaml.rb +21 -18
  35. data/lib/rsmp/{rsmp.rb → helpers/clock.rb} +5 -6
  36. data/lib/rsmp/{deep_merge.rb → helpers/deep_merge.rb} +2 -1
  37. data/lib/rsmp/helpers/error.rb +71 -0
  38. data/lib/rsmp/{inspect.rb → helpers/inspect.rb} +6 -10
  39. data/lib/rsmp/log/archive.rb +98 -0
  40. data/lib/rsmp/log/colorization.rb +41 -0
  41. data/lib/rsmp/log/filtering.rb +54 -0
  42. data/lib/rsmp/log/logger.rb +206 -0
  43. data/lib/rsmp/{logging.rb → log/logging.rb} +5 -7
  44. data/lib/rsmp/message.rb +159 -148
  45. data/lib/rsmp/{node.rb → node/node.rb} +19 -17
  46. data/lib/rsmp/{protocol.rb → node/protocol.rb} +5 -3
  47. data/lib/rsmp/node/site/site.rb +195 -0
  48. data/lib/rsmp/node/supervisor/modules/configuration.rb +59 -0
  49. data/lib/rsmp/node/supervisor/modules/connection.rb +140 -0
  50. data/lib/rsmp/node/supervisor/modules/sites.rb +64 -0
  51. data/lib/rsmp/node/supervisor/supervisor.rb +72 -0
  52. data/lib/rsmp/{task.rb → node/task.rb} +12 -14
  53. data/lib/rsmp/proxy/modules/acknowledgements.rb +144 -0
  54. data/lib/rsmp/proxy/modules/receive.rb +119 -0
  55. data/lib/rsmp/proxy/modules/send.rb +76 -0
  56. data/lib/rsmp/proxy/modules/state.rb +25 -0
  57. data/lib/rsmp/proxy/modules/tasks.rb +105 -0
  58. data/lib/rsmp/proxy/modules/versions.rb +69 -0
  59. data/lib/rsmp/proxy/modules/watchdogs.rb +66 -0
  60. data/lib/rsmp/proxy/proxy.rb +199 -0
  61. data/lib/rsmp/proxy/site/modules/aggregated_status.rb +52 -0
  62. data/lib/rsmp/proxy/site/modules/alarms.rb +27 -0
  63. data/lib/rsmp/proxy/site/modules/commands.rb +31 -0
  64. data/lib/rsmp/proxy/site/modules/status.rb +110 -0
  65. data/lib/rsmp/proxy/site/site_proxy.rb +205 -0
  66. data/lib/rsmp/proxy/supervisor/modules/aggregated_status.rb +47 -0
  67. data/lib/rsmp/proxy/supervisor/modules/alarms.rb +73 -0
  68. data/lib/rsmp/proxy/supervisor/modules/commands.rb +53 -0
  69. data/lib/rsmp/proxy/supervisor/modules/status.rb +204 -0
  70. data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +178 -0
  71. data/lib/rsmp/tlc/detector_logic.rb +18 -34
  72. data/lib/rsmp/tlc/input_states.rb +126 -0
  73. data/lib/rsmp/tlc/modules/detector_logics.rb +50 -0
  74. data/lib/rsmp/tlc/modules/display.rb +78 -0
  75. data/lib/rsmp/tlc/modules/helpers.rb +41 -0
  76. data/lib/rsmp/tlc/modules/inputs.rb +173 -0
  77. data/lib/rsmp/tlc/modules/modes.rb +253 -0
  78. data/lib/rsmp/tlc/modules/outputs.rb +30 -0
  79. data/lib/rsmp/tlc/modules/plans.rb +218 -0
  80. data/lib/rsmp/tlc/modules/signal_groups.rb +109 -0
  81. data/lib/rsmp/tlc/modules/startup_sequence.rb +22 -0
  82. data/lib/rsmp/tlc/modules/system.rb +140 -0
  83. data/lib/rsmp/tlc/modules/traffic_data.rb +49 -0
  84. data/lib/rsmp/tlc/signal_group.rb +37 -41
  85. data/lib/rsmp/tlc/signal_plan.rb +14 -11
  86. data/lib/rsmp/tlc/signal_priority.rb +39 -35
  87. data/lib/rsmp/tlc/startup_sequence.rb +59 -0
  88. data/lib/rsmp/tlc/traffic_controller.rb +38 -1010
  89. data/lib/rsmp/tlc/traffic_controller_site.rb +58 -57
  90. data/lib/rsmp/version.rb +1 -1
  91. data/lib/rsmp.rb +82 -48
  92. data/rsmp.gemspec +24 -31
  93. metadata +79 -139
  94. data/lib/rsmp/archive.rb +0 -76
  95. data/lib/rsmp/collect/message_matchers.rb +0 -0
  96. data/lib/rsmp/component_base.rb +0 -87
  97. data/lib/rsmp/component_proxy.rb +0 -57
  98. data/lib/rsmp/components.rb +0 -65
  99. data/lib/rsmp/error.rb +0 -71
  100. data/lib/rsmp/logger.rb +0 -216
  101. data/lib/rsmp/proxy.rb +0 -693
  102. data/lib/rsmp/site.rb +0 -188
  103. data/lib/rsmp/site_proxy.rb +0 -389
  104. data/lib/rsmp/supervisor.rb +0 -302
  105. data/lib/rsmp/supervisor_proxy.rb +0 -510
  106. data/lib/rsmp/tlc/inputs.rb +0 -134
@@ -8,179 +8,187 @@ module RSMP
8
8
  module Convert
9
9
  module Export
10
10
  module JSONSchema
11
-
12
- @@json_options = {
11
+ JSON_OPTIONS = {
13
12
  array_nl: "\n",
14
13
  object_nl: "\n",
15
14
  indent: ' ',
16
15
  space_before: ' ',
17
16
  space: ' '
18
- }
17
+ }.freeze
19
18
 
20
- def self.output_json item
21
- JSON.generate(item,@@json_options)
19
+ def self.output_json(item)
20
+ JSON.generate(item, JSON_OPTIONS)
22
21
  end
23
22
 
24
- def self.build_value item
23
+ def self.build_value(item)
25
24
  out = {}
26
-
27
- if item['description']
28
- out["description"] = item['description']
29
- end
25
+ out['description'] = item['description'] if item['description']
30
26
 
31
27
  if item['list']
32
- case item['type']
33
- when "boolean"
34
- out["$ref"] = "../../../core/3.1.1/definitions.json#/boolean_list"
35
- when "integer", "ordinal", "unit", "scale", "long"
36
- out["$ref"] = "../../../core/3.1.1/definitions.json#/integer_list"
37
- else
38
- raise "Error: List of #{item['type']} is not supported: #{item.inspect}"
39
- end
28
+ build_list_value(out, item)
29
+ else
30
+ build_single_value(out, item)
31
+ end
40
32
 
41
- if item["values"]
42
- value_list = item["values"].keys.join('|')
43
- out['pattern'] = /(?-mix:^(#{value_list})(?:,(#{value_list}))*$)/
44
- end
33
+ out
34
+ end
45
35
 
46
- if item["pattern"]
47
- puts "Warning: Pattern not support for lists: #{item.inspect}"
48
- end
36
+ def self.build_list_value(out, item)
37
+ case item['type']
38
+ when 'boolean'
39
+ out['$ref'] = '../../../core/3.1.1/definitions.json#/boolean_list'
40
+ when 'integer', 'ordinal', 'unit', 'scale', 'long'
41
+ out['$ref'] = '../../../core/3.1.1/definitions.json#/integer_list'
49
42
  else
50
- case item['type']
51
- when "string", "base64"
52
- out["type"] = "string"
53
- when "boolean"
54
- out["$ref"] = "../../../core/3.1.1/definitions.json#/boolean"
55
- when "timestamp"
56
- out["$ref"] = "../../../core/3.1.1/definitions.json#/timestamp"
57
- when "integer", "ordinal", "unit", "scale", "long"
58
- out["$ref"] = "../../../core/3.1.1/definitions.json#/integer"
59
- else
60
- out["type"] = "string"
61
- end
62
-
63
- if item["values"]
64
- out["enum"] = item["values"].keys.sort
65
- end
43
+ raise "Error: List of #{item['type']} is not supported: #{item.inspect}"
44
+ end
66
45
 
67
- if item["pattern"]
68
- out["pattern"] = item["pattern"]
69
- end
46
+ if item['values']
47
+ value_list = item['values'].keys.join('|')
48
+ out['pattern'] = /(?-mix:^(#{value_list})(?:,(#{value_list}))*$)/
70
49
  end
71
50
 
51
+ puts "Warning: Pattern not support for lists: #{item.inspect}" if item['pattern']
52
+ end
53
+
54
+ def self.build_single_value(out, item)
55
+ case item['type']
56
+ when 'boolean'
57
+ out['$ref'] = '../../../core/3.1.1/definitions.json#/boolean'
58
+ when 'timestamp'
59
+ out['$ref'] = '../../../core/3.1.1/definitions.json#/timestamp'
60
+ when 'integer', 'ordinal', 'unit', 'scale', 'long'
61
+ out['$ref'] = '../../../core/3.1.1/definitions.json#/integer'
62
+ else # includes 'string', 'base64' and any other types
63
+ out['type'] = 'string'
64
+ end
72
65
 
73
- out
66
+ out['enum'] = item['values'].keys.sort if item['values']
67
+ out['pattern'] = item['pattern'] if item['pattern']
74
68
  end
75
69
 
76
- def self.build_item item, property_key: 'v'
77
- json = { "allOf" => [ { "description" => item['description'] } ] }
70
+ def self.build_item(item, property_key: 'v')
71
+ json = { 'allOf' => [{ 'description' => item['description'] }] }
78
72
  if item['arguments']
79
- json["allOf"].first["properties"] = { "n" => { "enum" => item['arguments'].keys.sort } }
80
- item['arguments'].each_pair do |key,argument|
81
- json["allOf"] << {
82
- "if" => { "required" => ["n"], "properties" => { "n" => { "const" => key }}},
83
- "then" => { "properties" => { property_key => build_value(argument) } }
73
+ json['allOf'].first['properties'] = { 'n' => { 'enum' => item['arguments'].keys.sort } }
74
+ item['arguments'].each_pair do |key, argument|
75
+ json['allOf'] << {
76
+ 'if' => { 'required' => ['n'], 'properties' => { 'n' => { 'const' => key } } },
77
+ 'then' => { 'properties' => { property_key => build_value(argument) } }
84
78
  }
85
79
  end
86
80
  end
87
81
  json
88
82
  end
89
83
 
90
- def self.output_alarms out, items
84
+ def self.output_alarms(out, items)
91
85
  list = items.keys.sort.map do |key|
92
86
  {
93
- "if" => { "required" => ["aCId"], "properties" => { "aCId" => { "const" => key }}},
94
- "then" => { "$ref" => "#{key}.json" }
87
+ 'if' => { 'required' => ['aCId'], 'properties' => { 'aCId' => { 'const' => key } } },
88
+ 'then' => { '$ref' => "#{key}.json" }
95
89
  }
96
90
  end
97
91
  json = {
98
- "properties" => {
99
- "aCId" => { "enum" => items.keys.sort },
100
- "rvs" => { "items" => { "allOf" => list } }
92
+ 'properties' => {
93
+ 'aCId' => { 'enum' => items.keys.sort },
94
+ 'rvs' => { 'items' => { 'allOf' => list } }
101
95
  }
102
96
  }
103
97
  out['alarms/alarms.json'] = output_json json
104
- items.each_pair { |key,item| output_alarm out, key, item }
98
+ items.each_pair { |key, item| output_alarm out, key, item }
105
99
  end
106
100
 
107
- def self.output_alarm out, key, item
101
+ def self.output_alarm(out, key, item)
108
102
  json = build_item item
109
103
  out["alarms/#{key}.json"] = output_json json
110
104
  end
111
105
 
112
- def self.output_statuses out, items
113
- list = [ { "properties" => { "sCI" => { "enum"=> items.keys.sort }}} ]
106
+ def self.output_statuses(out, items)
107
+ list = [{ 'properties' => { 'sCI' => { 'enum' => items.keys.sort } } }]
114
108
  items.keys.sort.each do |key|
115
109
  list << {
116
- "if"=> { "required" => ["sCI"], "properties" => { "sCI"=> { "const"=> key }}},
117
- "then" => { "$ref" => "#{key}.json" }
110
+ 'if' => { 'required' => ['sCI'], 'properties' => { 'sCI' => { 'const' => key } } },
111
+ 'then' => { '$ref' => "#{key}.json" }
118
112
  }
119
113
  end
120
- json = { "properties" => { "sS" => { "items" => { "allOf" => list }}}}
114
+ json = { 'properties' => { 'sS' => { 'items' => { 'allOf' => list } } } }
121
115
  out['statuses/statuses.json'] = output_json json
122
- items.each_pair { |key,item| output_status out, key, item }
116
+ items.each_pair { |key, item| output_status out, key, item }
123
117
  end
124
118
 
125
- def self.output_status out, key, item
126
- json = build_item item, property_key:'s'
119
+ def self.output_status(out, key, item)
120
+ json = build_item item, property_key: 's'
127
121
  out["statuses/#{key}.json"] = output_json json
128
122
  end
129
123
 
130
- def self.output_commands out, items
131
- list = [ { "properties" => { "cCI" => { "enum"=> items.keys.sort }}} ]
124
+ def self.output_commands(out, items)
125
+ list = [{ 'properties' => { 'cCI' => { 'enum' => items.keys.sort } } }]
132
126
  items.keys.sort.each do |key|
133
127
  list << {
134
- "if" => { "required" => ["cCI"], "properties" => { "cCI"=> { "const"=> key }}},
135
- "then" => { "$ref" => "#{key}.json" }
128
+ 'if' => { 'required' => ['cCI'], 'properties' => { 'cCI' => { 'const' => key } } },
129
+ 'then' => { '$ref' => "#{key}.json" }
136
130
  }
137
131
  end
138
- json = { "items" => { "allOf" => list }}
132
+ json = { 'items' => { 'allOf' => list } }
139
133
  out['commands/commands.json'] = output_json json
140
134
 
141
- json = { "properties" => { "arg" => { "$ref" => "commands.json" }}}
135
+ json = { 'properties' => { 'arg' => { '$ref' => 'commands.json' } } }
142
136
  out['commands/command_requests.json'] = output_json json
143
137
 
144
- json = { "properties" => { "rvs" => { "$ref" => "commands.json" }}}
138
+ json = { 'properties' => { 'rvs' => { '$ref' => 'commands.json' } } }
145
139
  out['commands/command_responses.json'] = output_json json
146
140
 
147
- items.each_pair { |key,item| output_command out, key, item }
141
+ items.each_pair { |key, item| output_command out, key, item }
148
142
  end
149
143
 
150
- def self.output_command out, key, item
144
+ def self.output_command(out, key, item)
151
145
  json = build_item item
152
- json["allOf"].first["properties"]['cO'] = { "const" => item['command'] }
146
+ json['allOf'].first['properties']['cO'] = { 'const' => item['command'] }
153
147
  out["commands/#{key}.json"] = output_json json
154
148
  end
155
149
 
156
- def self.output_root out, meta
157
- json = {
158
- "name"=> meta['name'],
159
- "description"=> meta['description'],
160
- "version"=> meta['version'],
161
- "allOf" => [
162
- {
163
- "if" => { "required" => ["type"], "properties" => { "type" => { "const" => "CommandRequest" }}},
164
- "then" => { "$ref" => "commands/command_requests.json" }
165
- },
166
- {
167
- "if" => { "required" => ["type"], "properties" => { "type" => { "const" => "CommandResponse" }}},
168
- "then" => { "$ref" => "commands/command_responses.json" }
169
- },
170
- {
171
- "if" => { "required" => ["type"], "properties" => { "type" => { "enum" => ["StatusRequest","StatusResponse","StatusSubscribe","StatusUnsubscribe","StatusUpdate"] }}},
172
- "then" => { "$ref" => "statuses/statuses.json" }
173
- },
174
- {
175
- "if" => { "required" => ["type"], "properties" => { "type" => { "const" => "Alarm" }}},
176
- "then" => { "$ref" => "alarms/alarms.json" }
177
- }
178
- ]
150
+ def self.build_root_schema(meta)
151
+ {
152
+ 'name' => meta['name'],
153
+ 'description' => meta['description'],
154
+ 'version' => meta['version'],
155
+ 'allOf' => build_root_refs
179
156
  }
180
- out["sxl.json"] = output_json json
181
157
  end
182
158
 
183
- def self.generate sxl
159
+ def self.build_root_refs
160
+ [
161
+ {
162
+ 'if' => { 'required' => ['type'], 'properties' => { 'type' => { 'const' => 'CommandRequest' } } },
163
+ 'then' => { '$ref' => 'commands/command_requests.json' }
164
+ },
165
+ {
166
+ 'if' => { 'required' => ['type'], 'properties' => { 'type' => { 'const' => 'CommandResponse' } } },
167
+ 'then' => { '$ref' => 'commands/command_responses.json' }
168
+ },
169
+ {
170
+ 'if' => { 'required' => ['type'],
171
+ 'properties' => {
172
+ 'type' => {
173
+ 'enum' => %w[StatusRequest StatusResponse StatusSubscribe
174
+ StatusUnsubscribe StatusUpdate]
175
+ }
176
+ } },
177
+ 'then' => { '$ref' => 'statuses/statuses.json' }
178
+ },
179
+ {
180
+ 'if' => { 'required' => ['type'], 'properties' => { 'type' => { 'const' => 'Alarm' } } },
181
+ 'then' => { '$ref' => 'alarms/alarms.json' }
182
+ }
183
+ ]
184
+ end
185
+
186
+ def self.output_root(out, meta)
187
+ json = build_root_schema(meta)
188
+ out['sxl.json'] = output_json json
189
+ end
190
+
191
+ def self.generate(sxl)
184
192
  out = {}
185
193
  output_root out, sxl[:meta]
186
194
  output_alarms out, sxl[:alarms]
@@ -189,18 +197,16 @@ module RSMP
189
197
  out
190
198
  end
191
199
 
192
- def self.write sxl, folder
200
+ def self.write(sxl, folder)
193
201
  out = generate sxl
194
- out.each_pair do |relative_path,str|
202
+ out.each_pair do |relative_path, str|
195
203
  path = File.join(folder, relative_path)
196
- FileUtils.mkdir_p File.dirname(path) # create folders if needed
197
- file = File.open(path, 'w+') # w+ means truncate or create new file
204
+ FileUtils.mkdir_p File.dirname(path) # create folders if needed
205
+ file = File.open(path, 'w+') # w+ means truncate or create new file
198
206
  file.puts str
199
207
  end
200
208
  end
201
-
202
209
  end
203
-
204
210
  end
205
211
  end
206
- end
212
+ end
@@ -8,34 +8,37 @@ module RSMP
8
8
  module Convert
9
9
  module Import
10
10
  module YAML
11
-
12
- def self.read path
11
+ def self.read(path)
13
12
  convert ::YAML.load_file(path)
14
13
  end
15
14
 
16
- def self.parse str
15
+ def self.parse(str)
17
16
  convert ::YAML.load(str)
18
17
  end
19
18
 
20
- def self.convert yaml
21
- sxl = {
22
- meta: {},
23
- alarms: {},
24
- statuses: {},
25
- commands: {}
26
- }
27
-
19
+ def self.convert(yaml)
20
+ sxl = build_empty_sxl
28
21
  sxl[:meta] = yaml['meta']
22
+ merge_objects(sxl, yaml['objects'])
23
+ sxl
24
+ end
25
+
26
+ def self.build_empty_sxl
27
+ { meta: {}, alarms: {}, statuses: {}, commands: {} }
28
+ end
29
29
 
30
- yaml['objects'].each_pair do |type,object|
31
- object["alarms"].each { |id,item| sxl[:alarms][id] = item } if object["alarms"]
32
- object["statuses"].each { |id,item| sxl[:statuses][id] = item } if object["statuses"]
33
- object["commands"].each { |id,item| sxl[:commands][id] = item } if object["commands"]
30
+ def self.merge_objects(sxl, objects)
31
+ objects.each_pair do |_type, object|
32
+ merge_object_items(sxl, object)
34
33
  end
35
- sxl
36
34
  end
37
- end
38
35
 
36
+ def self.merge_object_items(sxl, object)
37
+ object['alarms']&.each { |id, item| sxl[:alarms][id] = item }
38
+ object['statuses']&.each { |id, item| sxl[:statuses][id] = item }
39
+ object['commands']&.each { |id, item| sxl[:commands][id] = item }
40
+ end
41
+ end
39
42
  end
40
43
  end
41
- end
44
+ end
@@ -6,7 +6,6 @@
6
6
  require 'time'
7
7
 
8
8
  module RSMP
9
-
10
9
  class Clock
11
10
  attr_reader :adjustment
12
11
 
@@ -14,7 +13,7 @@ module RSMP
14
13
  @adjustment = 0
15
14
  end
16
15
 
17
- def set target
16
+ def set(target)
18
17
  @adjustment = target - Time.now
19
18
  end
20
19
 
@@ -34,12 +33,12 @@ module RSMP
34
33
  Time.now.utc
35
34
  end
36
35
 
37
- def self.to_s time=nil
38
- (time || now).strftime("%FT%T.%3NZ")
36
+ def self.to_s(time = nil)
37
+ (time || now).strftime('%FT%T.%3NZ')
39
38
  end
40
39
 
41
- def self.parse str
40
+ def self.parse(str)
42
41
  Time.parse(str)
43
42
  end
44
43
  end
45
- end
44
+ end
@@ -1,7 +1,8 @@
1
1
  class Hash
2
2
  def deep_merge(other_hash)
3
3
  return self unless other_hash
4
- self.merge(other_hash) do |key, old, fresh|
4
+
5
+ merge(other_hash) do |_key, old, fresh|
5
6
  if old.is_a?(Hash) && fresh.is_a?(Hash)
6
7
  old.deep_merge(fresh)
7
8
  else
@@ -0,0 +1,71 @@
1
+ module RSMP
2
+ class Error < StandardError
3
+ end
4
+
5
+ class InvalidPacket < Error
6
+ end
7
+
8
+ class MalformedMessage < Error
9
+ end
10
+
11
+ class SchemaError < Error
12
+ attr_accessor :schemas
13
+ end
14
+
15
+ class InvalidMessage < Error
16
+ end
17
+
18
+ class UnknownMessage < Error
19
+ end
20
+
21
+ class MissingAcknowledgment < Error
22
+ end
23
+
24
+ class MissingWatchdog < Error
25
+ end
26
+
27
+ class MessageRejected < Error
28
+ end
29
+
30
+ class MissingAttribute < InvalidMessage
31
+ end
32
+
33
+ class FatalError < Error
34
+ end
35
+
36
+ class HandshakeError < FatalError
37
+ end
38
+
39
+ class NotReady < Error
40
+ end
41
+
42
+ class TimeoutError < Error
43
+ end
44
+
45
+ class DisconnectError < Error
46
+ end
47
+
48
+ class ConnectionError < Error
49
+ end
50
+
51
+ class UnknownComponent < Error
52
+ end
53
+
54
+ class UnknownCommand < Error
55
+ end
56
+
57
+ class UnknownStatus < Error
58
+ end
59
+
60
+ class ConfigurationError < Error
61
+ end
62
+
63
+ class RepeatedAlarmError < Error
64
+ end
65
+
66
+ class RepeatedStatusError < Error
67
+ end
68
+
69
+ class TimestampError < Error
70
+ end
71
+ end
@@ -12,23 +12,20 @@
12
12
 
13
13
  module RSMP
14
14
  module Inspect
15
-
16
15
  def inspector *short_items
17
16
  instance_variables.map do |var_name|
18
17
  var = instance_variable_get(var_name)
19
18
  class_name = var.class.name
20
19
 
21
20
  short = short_items.include?(var_name) ||
22
- class_name.start_with?('Async') ||
23
- class_name.start_with?('RSMP')
21
+ class_name.start_with?('Async') ||
22
+ class_name.start_with?('RSMP')
24
23
 
25
24
  if short
26
- if var.is_a? Array
27
- "#{var_name}: #<#{class_name}:#{class_name.object_id}, #{var.size} items>"
28
- elsif var.is_a? Hash
25
+ if var.is_a?(Array) || var.is_a?(Hash)
29
26
  "#{var_name}: #<#{class_name}:#{class_name.object_id}, #{var.size} items>"
30
27
  else
31
- "#{var_name}: #{var.to_s}"
28
+ "#{var_name}: #{var}"
32
29
  end
33
30
  else
34
31
  "#{var_name}: #{var.inspect}"
@@ -39,8 +36,7 @@ module RSMP
39
36
  # override this if you want additional variable to be shown in the short format,
40
37
  # or ottherwise change the inspect format
41
38
  def inspect
42
- "#<#{self.class.name}:#{self.object_id}, #{inspector}>"
39
+ "#<#{self.class.name}:#{object_id}, #{inspector}>"
43
40
  end
44
-
45
41
  end
46
- end
42
+ end
@@ -0,0 +1,98 @@
1
+ # Archive of log items, which can be messages or info items.
2
+ # All items are timestamped, and stored chronologically.
3
+
4
+ module RSMP
5
+ class Archive
6
+ include Inspect
7
+
8
+ attr_reader :items
9
+
10
+ @index = 0
11
+
12
+ class << self
13
+ attr_accessor :index
14
+ end
15
+
16
+ def initialize(max = 100)
17
+ @items = []
18
+ @max = max
19
+ end
20
+
21
+ def inspect
22
+ "#<#{self.class.name}:#{object_id}, #{inspector(:@items)}>"
23
+ end
24
+
25
+ def self.prepare_item(item)
26
+ raise ArgumentError unless item.is_a? Hash
27
+
28
+ cleaned = item.slice(:author, :level, :ip, :port, :site_id, :component, :text, :message, :exception)
29
+ cleaned[:timestamp] = Clock.now
30
+ if item[:message]
31
+ cleaned[:direction] = item[:message].direction
32
+ cleaned[:component] = item[:message].attributes['cId']
33
+ end
34
+
35
+ cleaned
36
+ end
37
+
38
+ def self.increase_index
39
+ self.index += 1
40
+ end
41
+
42
+ def self.current_index
43
+ index
44
+ end
45
+
46
+ def by_level(levels)
47
+ items.select { |item| levels.include? item[:level] }
48
+ end
49
+
50
+ def strings
51
+ items.map { |item| item[:str] }
52
+ end
53
+
54
+ def add(item)
55
+ item[:index] = RSMP::Archive.increase_index
56
+ @items << item
57
+ return unless @items.size > @max
58
+
59
+ @items.shift
60
+ end
61
+
62
+ private
63
+
64
+ def find(options, &)
65
+ # search backwards from newest to older, stopping once messages
66
+ # are older that options[:earliest]
67
+ out = []
68
+ @items.reverse_each do |item|
69
+ break if too_old?(item, options[:earliest])
70
+ next unless matches_filters?(item, options, &)
71
+
72
+ out.unshift item
73
+ end
74
+ out
75
+ end
76
+
77
+ def too_old?(item, earliest)
78
+ earliest && item[:timestamp] < earliest
79
+ end
80
+
81
+ def matches_filters?(item, options, &block)
82
+ return false if options[:level] && item[:level] != options[:level]
83
+ return false if options[:type] && !matches_type?(item, options[:type])
84
+ return false if options[:with_message] && !message?(item)
85
+ return false if block_given? && block.call != true
86
+
87
+ true
88
+ end
89
+
90
+ def matches_type?(item, type)
91
+ item[:message] && item[:message].type == type
92
+ end
93
+
94
+ def message?(item)
95
+ item[:direction] && item[:message]
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,41 @@
1
+ module RSMP
2
+ class Logger
3
+ # Handles colorization of log output
4
+ module Colorization
5
+ def default_colors
6
+ {
7
+ 'info' => 'white',
8
+ 'log' => 'light_blue',
9
+ 'statistics' => 'light_black',
10
+ 'warning' => 'light_yellow',
11
+ 'error' => 'red',
12
+ 'debug' => 'light_black',
13
+ 'collect' => 'light_black'
14
+ }
15
+ end
16
+
17
+ def colorize_with_map(level, str, colors)
18
+ color = colors[level.to_s]
19
+ color ? str.colorize(color.to_sym) : str
20
+ end
21
+
22
+ def apply_hash_colors(level, str)
23
+ colors = default_colors
24
+ colors.merge! @settings['color'] if @settings['color'].is_a?(Hash)
25
+ colorize_with_map(level, str, colors)
26
+ end
27
+
28
+ def colorize(level, str)
29
+ return str if @settings['color'] == false || @settings['color'].nil?
30
+
31
+ if @settings['color'] == true || @settings['color'].is_a?(Hash)
32
+ apply_hash_colors(level, str)
33
+ elsif %i[nack warning error].include?(level)
34
+ str.colorize(@settings['color']).bold
35
+ else
36
+ str.colorize @settings['color']
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end