Class: RSMP::Message

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

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.



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

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



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

def attributes
  @attributes
end

#directionObject

Returns the value of attribute direction.



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

def direction
  @direction
end

#jsonObject

Returns the value of attribute json.



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

def json
  @json
end

#nowObject (readonly)

this is an internal timestamp recording when we receive/send



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

def now
  @now
end

#outObject (readonly)

this is an internal timestamp recording when we receive/send



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

def out
  @out
end

#timestampObject (readonly)

this is an internal timestamp recording when we receive/send



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

def timestamp
  @timestamp
end

Class Method Details

.bin_to_chars(str) ⇒ Object



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

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



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

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



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

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



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

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



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

def self.make_m_id
  SecureRandom.uuid
end

.message_typesObject



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

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



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

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



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

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

.validate_attributes_structure(attributes) ⇒ Object

Raises:



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

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



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

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:



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

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:



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

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



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

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



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

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

#generate_jsonObject



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

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



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

def m_id
  @attributes['mId']
end

#m_id_shortObject



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

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

#typeObject



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

def type
  @attributes['type']
end

#valid?Boolean

Returns:

  • (Boolean)


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

def valid?
  true
end

#validate(schemas) ⇒ Object



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

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)


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

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)


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

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