Skip to content

Instantly share code, notes, and snippets.

@kyoma-takahashi
Created October 3, 2015 00:30
Show Gist options
  • Select an option

  • Save kyoma-takahashi/e41eacd927987bed1b3e to your computer and use it in GitHub Desktop.

Select an option

Save kyoma-takahashi/e41eacd927987bed1b3e to your computer and use it in GitHub Desktop.
#!/usr/bin/ruby
SERIAL_DEVICE = '/dev/tty.usbserial-FTXYGCM5'
# On Windows, like SERIAL_DEVICE = 'COM4'
require 'rubygems'
require 'rmodbus'
# does not support 23 (0x17) Read/Write Multiple registers
# according to https://github.com/flipback/rmodbus
require 'serialport'
# To prepare,
# $ gem environment
# $ gem install -V rmodbus serialport
# To clean-up,
# $ gem uninstall gserver rmodbus serialport
# Serial constants according to the master configuration
BAUD_RATE = 19_200
SERVER_ADDRESS = 50
SERIAL_OPTS = {
:data_bit => 8,
:stop_bits => 1,
:parity => SerialPort::EVEN}
# FYI, default :read_timeout is 100 in SerialPort lib
# Modbus constants
HOLDING_REGISTER_OFFSET = 40001
IDX_WATCHDOG_COUNTER_M2S = 40501 - HOLDING_REGISTER_OFFSET
IDX_WATCHDOG_COUNTER_S2M = 40001 - HOLDING_REGISTER_OFFSET
WATCHDOG_COUNTER_MAX = 2 ** 8
IDX_DATA = 40510 - HOLDING_REGISTER_OFFSET
DATA_COUNTS = 28
INVALID_LENGTH_M2S = 0
# Constants for this implementation
DUMMY_BYTE = 0x00
class DummySlave < ModBus::RTUServer
def initialize(*arvg)
super
@holding_registers = Array.new(1 + IDX_DATA + DATA_COUNTS * 2 + INVALID_LENGTH_M2S)
@holding_registers[0..IDX_DATA] = [DUMMY_BYTE] * IDX_DATA
# On 2015-09-11, the entire array was filled by DUMMY_BYTE and
# it was why we could not find the error of the master,
# which sends wrong 0x10 messages 2 bytes shorter than requite.
# On 2015-09-15, changed not to fill the part of data from the master.
@slave_watchdog_counter = 0
holding_registers[IDX_WATCHDOG_COUNTER_S2M] = @slave_watchdog_counter
@master_watchdog_counter = nil
end
def when_write
timestamp = Time.now.utc.to_s
if holding_registers[IDX_WATCHDOG_COUNTER_M2S] == @master_watchdog_counter
warn "#{timestamp} Received a request to write holding registers with the same watchdog counter: #{@master_watchdog_counter}"
else
@master_watchdog_counter = holding_registers[IDX_WATCHDOG_COUNTER_M2S]
@slave_watchdog_counter = (@slave_watchdog_counter + 1) % WATCHDOG_COUNTER_MAX
holding_registers[IDX_WATCHDOG_COUNTER_S2M] = @slave_watchdog_counter
$stderr.puts [timestamp,
# holding_registers[0, IDX_DATA],
holding_registers[IDX_WATCHDOG_COUNTER_S2M],
holding_registers[IDX_WATCHDOG_COUNTER_M2S],
holding_registers[IDX_DATA, DATA_COUNTS * 2].
each_slice(2).collect {|pair|
if pair.include? nil
'[' + pair.join(',') + ']'
else
sprintf('%.8e', pair.reverse.to_32f.first)
end
}#,
# holding_registers[IDX_DATA + DATA_COUNTS * 2, holding_registers.length]
].flatten.join(',')
# Array#to_32f is defined in rmodbus/ext.rb
# The decimal digits of 32 bit float is 7.22
# according to https://en.wikipedia.org/wiki/IEEE_floating_point
end
end
def parse_write_multiple_registers_func(req)
ret_val = super
# p ret_val
# p quant = req[3,2].unpack('n')[0]
# p addr = req[1,2].unpack('n')[0]
# p @holding_registers.size
when_write
ret_val
end
end
server = DummySlave.new(SERIAL_DEVICE, BAUD_RATE, SERVER_ADDRESS, SERIAL_OPTS)
server.debug = true
server.start
server.join
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment