Class: RSMP::Component

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

Overview

Class that represents an RMSP component. Currently this class is used by both SiteProxy and SupervisorProxy, and can therefore represent either a local or remote (proxy) component.

Constant Summary collapse

AGGREGATED_STATUS_KEYS =
[ :local_control,
:communication_distruption,
:high_priority_alarm,
:medium_priority_alarm,
:low_priority_alarm,
:normal,
:rest,
:not_connected ]

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Inspect

#inspect, #inspector

Constructor Details

#initialize(node:, id:, grouped: false) ⇒ Component

Returns a new instance of Component.



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

def initialize node:, id:, grouped: false
  @c_id = id
  @node = node
  @grouped = grouped
  @alarms = {}
  @statuses = {}
  @subscribes = {}
  clear_aggregated_status
end

Instance Attribute Details

#aggregated_statusObject (readonly)

Returns the value of attribute aggregated_status.



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

def aggregated_status
  @aggregated_status
end

#aggregated_status_boolsObject (readonly)

Returns the value of attribute aggregated_status_bools.



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

def aggregated_status_bools
  @aggregated_status_bools
end

#alarmsObject (readonly)

Returns the value of attribute alarms.



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

def alarms
  @alarms
end

#c_idObject (readonly)

Returns the value of attribute c_id.



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

def c_id
  @c_id
end

#groupedObject (readonly)

Returns the value of attribute grouped.



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

def grouped
  @grouped
end

#nodeObject (readonly)

Returns the value of attribute node.



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

def node
  @node
end

#statusesObject (readonly)

Returns the value of attribute statuses.



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

def statuses
  @statuses
end

Instance Method Details

#aggregated_status_changed(options = {}) ⇒ Object



63
64
65
# File 'lib/rsmp/component.rb', line 63

def aggregated_status_changed options={}
  @node.aggregated_status_changed self, options
end

#clear_aggregated_statusObject



31
32
33
34
35
# File 'lib/rsmp/component.rb', line 31

def clear_aggregated_status
  @aggregated_status = []
  @aggregated_status_bools = Array.new(8,false)
  @aggregated_status_bools[5] = true
end

#get_status(status_code, status_name = nil) ⇒ Object



76
77
78
# File 'lib/rsmp/component.rb', line 76

def get_status status_code, status_name=nil
  raise UnknownStatus.new "Status #{status_code}/#{status_name} not implemented by #{self.class}"
end

#handle_alarm(message) ⇒ Object

handle incoming alarm



81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/rsmp/component.rb', line 81

def handle_alarm message
  code = message.attribute('aCId')
  previous = @alarms[code]
  if previous
    unless message.differ?(previous)
      raise RepeatedAlarmError.new("no changes from previous alarm #{previous.m_id_short}")
    end
    if Time.parse(message.attribute('aTs')) < Time.parse(previous.attribute('aTs'))
      raise TimestampError.new("timestamp is earlier than previous alarm #{previous.m_id_short}")
    end
  end
ensure
  @alarms[code] = message
end

#handle_command(command_code, arg) ⇒ Object



72
73
74
# File 'lib/rsmp/component.rb', line 72

def handle_command command_code, arg
  raise UnknownCommand.new "Command #{command_code} not implemented by #{self.class}"
end

#handle_status_response(message) ⇒ Object

Handle an incoming status respone, by storing the values



118
119
120
# File 'lib/rsmp/component.rb', line 118

def handle_status_response message
  store_status message, check_repeated: false
end

#handle_status_subscribe(status_list) ⇒ Object

Our proxy subscribed to status updates Store update rates, so we can check for repeated alarm if we asked for updates only when there's a change, not on a regular interval. After subscribing, an update status us send regarless of whether values changes, and we store that.



132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/rsmp/component.rb', line 132

def handle_status_subscribe status_list
  status_list.each do |item|
    sCI, n, uRt = item['sCI'], item['n'], item['uRt']

    # record the update rate, so we can check for repeated status values if rate is zero
    @subscribes[sCI] ||= {}
    @subscribes[sCI][n] = {'uRt'=>uRt}

    # record that we expect an upeate, even though the value might not change
    @statuses[sCI] ||= {}
    @statuses[sCI][n] ||= {}
    @statuses[sCI][n][:initial] = true
  end
end

#handle_status_unsubscribe(status_list) ⇒ Object

Our proxy unsubscribed to status updates. Update our list of update rates.



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/rsmp/component.rb', line 149

def handle_status_unsubscribe status_list
  status_list.each do |item|
    sCI, n = item['sCI'], item['n']
    if @subscribes[sCI]
      @subscribes[sCI].delete n
    end
    if @subscribes[sCI].empty?
      @subscribes.delete sCI
    end

    # remove any mark that would allow the next update to be a repeat
    item = @statuses.dig sCI, n
    item.delete(:initial) if item
  end
end

#handle_status_update(message) ⇒ Object

Handle an incoming status update, by storing the values



123
124
125
# File 'lib/rsmp/component.rb', line 123

def handle_status_update message
  store_status message, check_repeated: true
end

#log(str, options) ⇒ Object



67
68
69
70
# File 'lib/rsmp/component.rb', line 67

def log str, options
  default = { component: c_id}
  @node.log str, default.merge(options)
end

#send_alarm(code:, status:) ⇒ Object

set alarm



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/rsmp/component.rb', line 97

def send_alarm code:, status:
  # TODO
  # we need to manage the state of alarms internally (an ALarm class probably),
  # and handle request from the supervisor to suspend and resume alarms etc.
  # when this state changes, we then send an alarm message
  alarm = Alarm.new(
    'cId' => c_id,
    'aTs' => @node.clock.to_s,
    'aCId' => code,
    'aSp' => 'Issue',
    'ack' => 'Acknowledged',
    'sS' => 'notSuspended',
    'aS' => status,
    'cat' => 'D',
    'pri' => '2',
    'rvs' => []
  )
  @node.alarm_changed self, alarm
end

#set_aggregated_status(status, options = {}) ⇒ Object

Raises:

  • (InvalidArgument)


37
38
39
40
41
42
43
44
45
46
47
# File 'lib/rsmp/component.rb', line 37

def set_aggregated_status status, options={}
  status = [status] if status.is_a? Symbol
  raise InvalidArgument unless status.is_a? Array
  input = status & AGGREGATED_STATUS_KEYS
  if input != @aggregated_status
    AGGREGATED_STATUS_KEYS.each_with_index do |key,index|
      @aggregated_status_bools[index] = status.include?(key)
    end
    aggregated_status_changed options
  end
end

#set_aggregated_status_bools(status) ⇒ Object

Raises:

  • (InvalidArgument)


49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/rsmp/component.rb', line 49

def set_aggregated_status_bools status
  raise InvalidArgument unless status.is_a? Array
  raise InvalidArgument unless status.size == 8
  if status != @aggregated_status_bools
    @aggregated_status = []
    AGGREGATED_STATUS_KEYS.each_with_index do |key,index|
      on = status[index] == true
      @aggregated_status_bools[index] = on
      @aggregated_status << key if on
    end
    aggregated_status_changed
  end
end

#store_status(message, check_repeated:) ⇒ Object

Store the latest status update values, optionally checking that we're not receiving unchanged values if we're subscribed wit updates only on change



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/rsmp/component.rb', line 168

def store_status message, check_repeated:
  message.attribute('sS').each do |item|
    sCI, n, s, q = item['sCI'], item['n'], item['s'], item['q']
    uRt = @subscribes.dig(sCI,n,'uRt')
    new_values = {'s'=>s,'q'=>q}
    old_values = @statuses.dig(sCI,n)
    if check_repeated && uRt.to_i == 0
      if new_values == old_values
        raise RSMP::RepeatedStatusError.new "no change for #{sCI} '#{n}'"
      end
    end
    @statuses[sCI] ||= {}
    @statuses[sCI][n] = new_values
  end
end