Class: RSMP::Message

Inherits:
Object
  • Object
show all
Includes:
Inspect
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

Methods included from Inspect

#inspect, #inspector

Constructor Details

#initialize(attributes = {}) ⇒ Message

Returns a new instance of Message.



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

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



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

def attributes
  @attributes
end

#directionObject

Returns the value of attribute direction.



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

def direction
  @direction
end

#jsonObject

Returns the value of attribute json.



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

def json
  @json
end

#nowObject (readonly)

this is an internal timestamp recording when we receive/send



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

def now
  @now
end

#outObject (readonly)

this is an internal timestamp recording when we receive/send



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

def out
  @out
end

#timestampObject (readonly)

this is an internal timestamp recording when we receive/send



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

def timestamp
  @timestamp
end

Class Method Details

.bin_to_chars(str) ⇒ Object



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

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



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

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



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

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



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

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



12
13
14
# File 'lib/rsmp/message.rb', line 12

def self.make_m_id
  SecureRandom.uuid
end

.message_typesObject



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

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



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

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
# File 'lib/rsmp/message.rb', line 93

def self.shorten_m_id(m_id, length = 4)
  m_id[0..(length - 1)]
end

.validate_attributes_structure(attributes) ⇒ Object

Raises:



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

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



124
125
126
127
128
# File 'lib/rsmp/message.rb', line 124

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:



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

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:



141
142
143
144
145
# File 'lib/rsmp/message.rb', line 141

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



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

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



156
157
158
159
# File 'lib/rsmp/message.rb', line 156

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

#generate_jsonObject



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

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

#m_idObject



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

def m_id
  @attributes['mId']
end

#m_id_shortObject



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

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)


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

def valid?
  true
end

#validate(schemas) ⇒ Object



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

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)


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

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)


171
172
173
# File 'lib/rsmp/message.rb', line 171

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