### This is free software
### Placed in the public domain by Joachim Wuttke 2007
### auxiliary functions:
class String
def to_int16
return self[0]*256 + self[1]
end
end
class Integer
def to_bytes
return (self >> 8).chr + (self & 0xFF).chr
end
end
### class for communication with one modbus slave:
class Modbus
def initialize ipaddr, port, slaveaddr
@sock = TCPSocket.open ipaddr, port
@@transaction_no = 0 # a unique number (on class level ?)
@slave = slaveaddr.chr
end
### modbus TCP encapsulation:
def send_pdu pdu ## encapsulate protocol_data_unit and send it to slave
@@transaction_no += 1
msg = @@transaction_no.to_bytes # transaction number
msg += "\000\000" # protocol identifier (always 0)
msg += (pdu.size+1).to_bytes # so many bytes will follow
msg += @slave # slave address
msg += pdu # protocol data unit
@sock.write msg
end
def read_pdu ## receive message from slave and extract pdu
header = @sock.read 7 # read TCP header
tin = header[0,2].to_int16
raise "transaction number mismatch" unless tin == @@transaction_no
len = header[4,2].to_int16 # length of remaining message
pdu = @sock.read len-1 # read remaining message
return pdu
end
def query pdu ## send pdu to slave and receive response
send_pdu pdu
return read_pdu
end
### modbus functions:
def query_holding( addr, nreg ) ## query contents of holding registers
pdu = query "\003" + addr.to_bytes + nreg.to_bytes
return pdu[2..-1]
end
# ... and so on ...
end
### usage in main program:
# connect once:
modbus = Modbus.new "129.187.1.1", 502, 1
# interact with slave:
puts modbus.query_holding 12, 4 # print contents of registers 12..15