Class: RSMP::Message

Inherits:
Object
  • Object
show all
Defined in:
lib/rsmp/message.rb

Overview

Base RSMP message class used to represent parsed and built messages.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes = {}) ⇒ Message

Returns a new instance of Message.



151
152
153
154
155
156
157
158
# File 'lib/rsmp/message.rb', line 151

def initialize(attributes = {})
  @timestamp = Time.now # this timestamp is for internal use, and does not use the clock
  # in the node, which can be set by an rsmp supervisor

  @attributes = { 'mType' => 'rSMsg' }.merge attributes

  ensure_message_id
end

Instance Attribute Details

#attributesObject (readonly)

this is an internal timestamp recording when we receive/send



7
8
9
# File 'lib/rsmp/message.rb', line 7

def attributes
  @attributes
end

#directionObject

Returns the value of attribute direction.



8
9
10
# File 'lib/rsmp/message.rb', line 8

def direction
  @direction
end

#jsonObject

Returns the value of attribute json.



8
9
10
# File 'lib/rsmp/message.rb', line 8

def json
  @json
end

#nowObject (readonly)

this is an internal timestamp recording when we receive/send



7
8
9
# File 'lib/rsmp/message.rb', line 7

def now
  @now
end

#outObject (readonly)

this is an internal timestamp recording when we receive/send



7
8
9
# File 'lib/rsmp/message.rb', line 7

def out
  @out
end

#timestampObject (readonly)

this is an internal timestamp recording when we receive/send



7
8
9
# File 'lib/rsmp/message.rb', line 7

def timestamp
  @timestamp
end

Class Method Details

.bin_to_chars(str) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
# File 'lib/rsmp/message.rb', line 116

def self.bin_to_chars(str)
  out = str.gsub(/[^[:print:]]/i, '.')
  max = 120
  if out.size <= max
    out
  else
    mid = ' ... '
    length = ((max - mid.size) / 2) - 1
    "#{out[0..length]} ... #{out[(-length - 1)..]}"
  end
end

.build(attributes, json) ⇒ Object



22
23
24
25
26
27
28
# File 'lib/rsmp/message.rb', line 22

def self.build(attributes, json)
  validate_message_type attributes
  message = create_message_instance(attributes)
  message.json = json
  message.direction = :in
  message
end

.build_alarm(attributes) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/rsmp/message.rb', line 56

def self.build_alarm(attributes)
  case attributes['aSp']
  when /^Issue$/i
    AlarmIssue.new attributes
  when /^Request$/i
    AlarmRequest.new attributes
  when /^Acknowledge$/i
    if attributes['ack'] =~ /^acknowledged$/i
      AlarmAcknowledged.new attributes
    else
      AlarmAcknowledge.new attributes
    end
  when /^Suspend$/i
    if attributes['sS'] =~ /^suspended$/i
      AlarmSuspended.new attributes
    elsif attributes['sS'] =~ /^notSuspended$/i
      AlarmResumed.new attributes
    else
      AlarmSuspend.new attributes
    end
  when /^Resume$/i
    AlarmResume.new attributes
  else
    Alarm.new attributes
  end
end

.create_message_instance(attributes) ⇒ Object



48
49
50
51
52
53
54
# File 'lib/rsmp/message.rb', line 48

def self.create_message_instance(attributes)
  type = attributes['type']
  return build_alarm(attributes) if type == 'Alarm'

  klass = message_types[type] || Unknown
  klass.new(attributes)
end

.make_m_idObject



10
11
12
# File 'lib/rsmp/message.rb', line 10

def self.make_m_id
  SecureRandom.uuid
end

.message_typesObject



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/rsmp/message.rb', line 30

def self.message_types
  {
    'MessageAck' => MessageAck,
    'MessageNotAck' => MessageNotAck,
    'Version' => Version,
    'AggregatedStatus' => AggregatedStatus,
    'AggregatedStatusRequest' => AggregatedStatusRequest,
    'Watchdog' => Watchdog,
    'CommandRequest' => CommandRequest,
    'CommandResponse' => CommandResponse,
    'StatusRequest' => StatusRequest,
    'StatusResponse' => StatusResponse,
    'StatusSubscribe' => StatusSubscribe,
    'StatusUnsubscribe' => StatusUnsubscribe,
    'StatusUpdate' => StatusUpdate
  }
end

.parse_attributes(json) ⇒ Object



14
15
16
17
18
19
20
# File 'lib/rsmp/message.rb', line 14

def self.parse_attributes(json)
  raise ArgumentError unless json

  JSON.parse json
rescue JSON::ParserError
  raise InvalidPacket, bin_to_chars(json)
end

.shorten_m_id(m_id, length = 4) ⇒ Object



95
96
97
98
99
# File 'lib/rsmp/message.rb', line 95

def self.shorten_m_id(m_id, length = 4)
  return nil unless m_id

  m_id[0..(length - 1)]
end

.validate_attributes_structure(attributes) ⇒ Object

Raises:



134
135
136
# File 'lib/rsmp/message.rb', line 134

def self.validate_attributes_structure(attributes)
  raise MalformedMessage, "JSON must be a Hash, got #{attributes.class} " unless attributes.is_a?(Hash)
end

.validate_message_type(attributes) ⇒ Object



128
129
130
131
132
# File 'lib/rsmp/message.rb', line 128

def self.validate_message_type(attributes)
  validate_attributes_structure(attributes)
  validate_mtype_field(attributes)
  validate_type_field(attributes)
end

.validate_mtype_field(attributes) ⇒ Object

Raises:



138
139
140
141
142
143
# File 'lib/rsmp/message.rb', line 138

def self.validate_mtype_field(attributes)
  mtype = attributes['mType']
  raise MalformedMessage, "'mType' is missing" unless mtype
  raise MalformedMessage, "'mType' must be a String, got #{mtype.class}" unless mtype.is_a?(String)
  raise MalformedMessage, "'mType' must be 'rSMsg', got '#{mtype}'" unless mtype == 'rSMsg'
end

.validate_type_field(attributes) ⇒ Object

Raises:



145
146
147
148
149
# File 'lib/rsmp/message.rb', line 145

def self.validate_type_field(attributes)
  type = attributes['type']
  raise MalformedMessage, "'type' is missing" unless type
  raise MalformedMessage, "'type' must be a String, got #{type.class}" unless type.is_a?(String)
end

Instance Method Details

#attribute(key) ⇒ Object



105
106
107
108
109
110
111
112
113
114
# File 'lib/rsmp/message.rb', line 105

def attribute(key)
  unless @attributes.key? key # NOTE: that this is not the same as @attributes[key] when
    maybe = @attributes.find { |k, _v| k.downcase == key.downcase }
    raise MissingAttribute, "attribute '#{maybe.first}' should be named '#{key}'" if maybe

    raise MissingAttribute, "missing attribute '#{key}'"

  end
  @attributes[key]
end

#ensure_message_idObject



160
161
162
163
# File 'lib/rsmp/message.rb', line 160

def ensure_message_id
  # if message id is empty, generate a new one
  @attributes['mId'] ||= Message.make_m_id
end

#generate_jsonObject



187
188
189
190
191
192
193
194
195
196
# File 'lib/rsmp/message.rb', line 187

def generate_json
  # ensure compact format on all platforms
  options = {
    array_nl: nil,
    object_nl: nil,
    space_before: nil,
    space: nil
  }
  @json = JSON.generate @attributes, options
end

#inspectObject



83
84
85
# File 'lib/rsmp/message.rb', line 83

def inspect
  "#<#{self.class.name}:#{object_id} m_id: #{m_id_short}>"
end

#m_idObject



91
92
93
# File 'lib/rsmp/message.rb', line 91

def m_id
  @attributes['mId']
end

#m_id_shortObject



101
102
103
# File 'lib/rsmp/message.rb', line 101

def m_id_short
  Message.shorten_m_id @attributes['mId']
end

#typeObject



87
88
89
# File 'lib/rsmp/message.rb', line 87

def type
  @attributes['type']
end

#valid?Boolean

Returns:

  • (Boolean)


183
184
185
# File 'lib/rsmp/message.rb', line 183

def valid?
  true
end

#validate(schemas) ⇒ Object



165
166
167
168
169
170
171
172
173
# File 'lib/rsmp/message.rb', line 165

def validate(schemas)
  errors = RSMP::Schema.validate attributes, schemas
  return unless errors

  error_string = errors.map { |item| item.reject { |e| e == '' } }.compact.join(', ').strip
  err = SchemaError.new error_string.to_s
  err.schemas = schemas
  raise err
end

#validate_id?Boolean

Returns:

  • (Boolean)


179
180
181
# File 'lib/rsmp/message.rb', line 179

def validate_id?
  !(@attributes['mId'] =~ /[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}/i).nil?
end

#validate_type?Boolean

Returns:

  • (Boolean)


175
176
177
# File 'lib/rsmp/message.rb', line 175

def validate_type?
  @attributes['mType'] == 'rSMsg'
end