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.



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

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



5
6
7
# File 'lib/rsmp/message.rb', line 5

def attributes
  @attributes
end

#directionObject

Returns the value of attribute direction.



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

def direction
  @direction
end

#jsonObject

Returns the value of attribute json.



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

def json
  @json
end

#nowObject (readonly)

this is an internal timestamp recording when we receive/send



5
6
7
# File 'lib/rsmp/message.rb', line 5

def now
  @now
end

#outObject (readonly)

this is an internal timestamp recording when we receive/send



5
6
7
# File 'lib/rsmp/message.rb', line 5

def out
  @out
end

#timestampObject (readonly)

this is an internal timestamp recording when we receive/send



5
6
7
# File 'lib/rsmp/message.rb', line 5

def timestamp
  @timestamp
end

Class Method Details

.bin_to_chars(str) ⇒ Object



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

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



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

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



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

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



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

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



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

def self.make_m_id
  SecureRandom.uuid
end

.message_typesObject



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

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



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

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



93
94
95
96
97
# File 'lib/rsmp/message.rb', line 93

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:



132
133
134
# File 'lib/rsmp/message.rb', line 132

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



126
127
128
129
130
# File 'lib/rsmp/message.rb', line 126

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:



136
137
138
139
140
141
# File 'lib/rsmp/message.rb', line 136

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:



143
144
145
146
147
# File 'lib/rsmp/message.rb', line 143

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



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

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



158
159
160
161
# File 'lib/rsmp/message.rb', line 158

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

#generate_jsonObject



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

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



81
82
83
# File 'lib/rsmp/message.rb', line 81

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

#m_idObject



89
90
91
# File 'lib/rsmp/message.rb', line 89

def m_id
  @attributes['mId']
end

#m_id_shortObject



99
100
101
# File 'lib/rsmp/message.rb', line 99

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

#typeObject



85
86
87
# File 'lib/rsmp/message.rb', line 85

def type
  @attributes['type']
end

#valid?Boolean

Returns:

  • (Boolean)


181
182
183
# File 'lib/rsmp/message.rb', line 181

def valid?
  true
end

#validate(schemas) ⇒ Object



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

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)


177
178
179
# File 'lib/rsmp/message.rb', line 177

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)


173
174
175
# File 'lib/rsmp/message.rb', line 173

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