Class: Phaserunner::Modbus

Inherits:
Object
  • Object
show all
Defined in:
lib/phaserunner/modbus.rb

Overview

Methods for communicating with the Modbus interface to the Phaserunner

Defined Under Namespace

Classes: RegistersRunLength

Constant Summary collapse

DEFAULTS =
{
  tty: '/dev/ttyUSB0',
  baudrate: 115200,
  slave_id: 1,
  dictionary_file: default_file_path,
  loop_count: :forever,
  quiet: false,
  register_list: register_list
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts) ⇒ Modbus

New Modbus

Converts the opts hash into Class Instance Variables (attr_readers)
Reads the JSON Grin Phaserunner Modbus Dictionary into a Hash


74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/phaserunner/modbus.rb', line 74

def initialize(opts)
  # Start with defaults and allow input args to override them
  final_opts = DEFAULTS.merge opts

  # Converts each key of the opts hash from the CLI into individual class attr_readers.
  # So they are now available to the rest of this Class as instance variables.
  # The key of the hash becomes the name of the instance variable.
  # They are available to all the methods of this class
  # See https://stackoverflow.com/a/7527916/38841
  final_opts.each_pair do |name, value|
    self.class.send(:attr_accessor, name)
    instance_variable_set("@#{name}", value)
  end

  # A few other Instance Variables
  @bod = AsiBod::Bod.new(bod_file: dictionary_file)
  @dict = @bod.hash_data
end

Instance Attribute Details

#baudrateObject (readonly)

Returns the value of attribute baudrate.



43
44
45
# File 'lib/phaserunner/modbus.rb', line 43

def baudrate
  @baudrate
end

#bodObject (readonly)

Returns the value of attribute bod.



68
69
70
# File 'lib/phaserunner/modbus.rb', line 68

def bod
  @bod
end

#dictObject (readonly)

Contains the Grin Phaesrunner Modbus Dictionary



66
67
68
# File 'lib/phaserunner/modbus.rb', line 66

def dict
  @dict
end

#dictionary_fileObject (readonly)

Returns the value of attribute dictionary_file.



45
46
47
# File 'lib/phaserunner/modbus.rb', line 45

def dictionary_file
  @dictionary_file
end

#loop_countObject (readonly)

Returns the value of attribute loop_count.



46
47
48
# File 'lib/phaserunner/modbus.rb', line 46

def loop_count
  @loop_count
end

#quietObject (readonly)

Returns the value of attribute quiet.



47
48
49
# File 'lib/phaserunner/modbus.rb', line 47

def quiet
  @quiet
end

#register_listObject (readonly)

The registers of interest for logging



53
54
55
# File 'lib/phaserunner/modbus.rb', line 53

def register_list
  @register_list
end

#slave_idObject (readonly)

Returns the value of attribute slave_id.



44
45
46
# File 'lib/phaserunner/modbus.rb', line 44

def slave_id
  @slave_id
end

#ttyObject (readonly)

Returns the value of attribute tty.



42
43
44
# File 'lib/phaserunner/modbus.rb', line 42

def tty
  @tty
end

Class Method Details

.default_file_pathObject

Returns the path to the default BODm.json file



21
22
23
# File 'lib/phaserunner/modbus.rb', line 21

def self.default_file_path
  AsiBod::Bod.default_file_path
end

Instance Method Details

#bulk_addresses_header(addresses) ⇒ Object



139
140
141
142
143
# File 'lib/phaserunner/modbus.rb', line 139

def bulk_addresses_header(addresses)
  addresses.map do |address|
    "#{dict[address][:name]} (#{dict[address][:units]})"
  end
end

#bulk_log_data(registers = register_list) ⇒ Array<Integer>

More optimized data fetch. Gets an array of address range structs

Parameters:

  • register_list (Array<RegistersRunLength] Register ranges to log. Optional, has a default)

    egister_list [Array<RegistersRunLength] Register ranges to log. Optional, has a default

Returns:

  • (Array<Integer>)

    List of the register values in the order requested



148
149
150
151
152
# File 'lib/phaserunner/modbus.rb', line 148

def bulk_log_data(registers = register_list)
  registers.map do |reg|
    read_scaled_range(reg.start, reg.count)
  end.flatten
end

#bulk_log_header(registers = register_list) ⇒ Array<String>

Get the headers for the bulk_log data

Parameters:

  • register_list (Array<RegistersRunLength] Register ranges to log. Optional, has a default)

    egister_list [Array<RegistersRunLength] Register ranges to log. Optional, has a default

Returns:

  • (Array<String>)

    Array of the headers



157
158
159
160
161
# File 'lib/phaserunner/modbus.rb', line 157

def bulk_log_header(registers = register_list)
  registers.map do |reg|
    range_address_header(reg.start, reg.count)
  end.flatten
end

#range_address_header(start_address, count) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/phaserunner/modbus.rb', line 116

def range_address_header(start_address, count)
  end_address = start_address + count 
  (start_address...end_address).map do |address|
    units = case dict[address][:type].downcase
            when "bit vector"
              "Flags"
            when "enum"
              "enum"
            when "dependent"
              "Unscaled #{dict[address][:units]}"
            else
              dict[address][:units]
            end
    "#{dict[address][:name]} (#{units})"
  end
end

#read_addresses(addresses) ⇒ Object



133
134
135
136
137
# File 'lib/phaserunner/modbus.rb', line 133

def read_addresses(addresses)
  addresses.map do |address|
    read_raw_range(address, 1)
  end
end

#read_raw_range(start_address, count) ⇒ Object



93
94
95
96
97
98
# File 'lib/phaserunner/modbus.rb', line 93

def read_raw_range(start_address, count)
  cl = ::ModBus::RTUClient.new(tty, baudrate)
  cl.with_slave(slave_id) do |slave|
    slave.read_holding_registers(start_address, count)
  end
end

#read_scaled_range(start_address, count) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/phaserunner/modbus.rb', line 100

def read_scaled_range(start_address, count)
  data = read_raw_range(start_address, count)
  data.map.with_index do |val, index| 
    address = index + start_address
    if dict[address][:type] == "independent"
      value = val.to_signed(16) / dict[address][:scale]
      value
    elsif dict[address][:type].downcase == "bit vector"
      value = val.to_s(2)
    else
      value = val
    end
    value
  end

end