diff --git a/Gemfile b/Gemfile index 67a82f2..d2e30ce 100644 --- a/Gemfile +++ b/Gemfile @@ -69,4 +69,5 @@ group :test do gem "selenium-webdriver" end +gem "ccutrer-serialport" gem "rmodbus" diff --git a/Gemfile.lock b/Gemfile.lock index 3d31fa1..22dd906 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -102,6 +102,8 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) + ccutrer-serialport (1.1.0) + ffi (~> 1.9, >= 1.9.3) concurrent-ruby (1.3.5) connection_pool (2.5.0) crass (1.0.6) @@ -126,6 +128,7 @@ GEM erubi (1.13.1) et-orbi (1.2.11) tzinfo + ffi (1.17.2-aarch64-linux-gnu) foreman (0.88.1) fugit (1.11.1) et-orbi (~> 1, >= 1.2.11) @@ -387,6 +390,7 @@ DEPENDENCIES bootsnap brakeman capybara + ccutrer-serialport cssbundling-rails debug erb_lint diff --git a/app/services/modbus/commands/bit_coil.rb b/app/services/modbus/commands/bit_coil.rb deleted file mode 100644 index d69dbab..0000000 --- a/app/services/modbus/commands/bit_coil.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Modbus::Service.new(plc_id: 1, type: "bit_coil", start_address: 0, end_address: 15).execute -module Modbus - module Commands - def bit_coil(slave) - read_bit(slave, :read_coils) - end - end -end diff --git a/app/services/modbus/commands/bit_input.rb b/app/services/modbus/commands/bit_input.rb deleted file mode 100644 index 7a18433..0000000 --- a/app/services/modbus/commands/bit_input.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Modbus::Service.new(plc_id: 1, type: "bit_input", start_address: 0, end_address: 15).execute -module Modbus - module Commands - def bit_input(slave) - read_bit(slave, :discrete_inputs) - end - end -end diff --git a/app/services/modbus/commands/word_holding_float32.rb b/app/services/modbus/commands/word_holding_float32.rb deleted file mode 100644 index 061f6ba..0000000 --- a/app/services/modbus/commands/word_holding_float32.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Modbus::Service.new(plc_id: 1, type: "word_holding_float32", start_address: 0, end_address: 15).execute -module Modbus - module Commands - def word_holding_float32(slave) - read_float(slave, :holding_registers) - end - end -end diff --git a/app/services/modbus/commands/word_holding_int16.rb b/app/services/modbus/commands/word_holding_int16.rb deleted file mode 100644 index 7408737..0000000 --- a/app/services/modbus/commands/word_holding_int16.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Modbus::Service.new(plc_id: 1, type: "word_holding_int16", start_address: 16, end_address: 31).execute -module Modbus - module Commands - def word_holding_int16(slave) - read_int(slave, 16, :holding_registers) - end - end -end diff --git a/app/services/modbus/commands/word_input_float32.rb b/app/services/modbus/commands/word_input_float32.rb deleted file mode 100644 index 4fb60c7..0000000 --- a/app/services/modbus/commands/word_input_float32.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Modbus::Service.new(plc_id: 1, type: "word_input_float32", start_address: 0, end_address: 15).execute -module Modbus - module Commands - def word_input_float32(slave) - read_float(slave, :input_registers) - end - end -end diff --git a/app/services/modbus/commands/word_input_int16.rb b/app/services/modbus/commands/word_input_int16.rb deleted file mode 100644 index 430deab..0000000 --- a/app/services/modbus/commands/word_input_int16.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Modbus::Service.new(plc_id: 1, type: "word_input_int16", start_address: 0, end_address: 15).execute -module Modbus - module Commands - def word_input_int16(slave) - read_int(slave, 16, :input_registers) - end - end -end diff --git a/app/services/modbus/commands/write_bit.rb b/app/services/modbus/commands/write_bit.rb deleted file mode 100644 index 2c9268c..0000000 --- a/app/services/modbus/commands/write_bit.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Modbus::Service.new(plc_id: 1, type: "write_bit", start_address: 0, value: 1).execute -module Modbus - module Commands - def write_bit(slave) - write_coil(slave) - end - end -end diff --git a/app/services/modbus/commands/write_bit_toggle.rb b/app/services/modbus/commands/write_bit_toggle.rb deleted file mode 100644 index da40323..0000000 --- a/app/services/modbus/commands/write_bit_toggle.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Modbus::Service.new(plc_id: 1, type: "write_bit_toggle", start_address: 0).execute -module Modbus - module Commands - def write_bit_toggle(slave) - write_toggle_coil(slave) - end - end -end diff --git a/app/services/modbus/commands/write_float32.rb b/app/services/modbus/commands/write_float32.rb deleted file mode 100644 index 260b038..0000000 --- a/app/services/modbus/commands/write_float32.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Modbus::Service.new(plc_id: 1, type: "float32", start_address: 0, value: 123.45).execute -module Modbus - module Commands - def write_float32(slave) - write_float(slave) - end - end -end diff --git a/app/services/modbus/commands/write_int16.rb b/app/services/modbus/commands/write_int16.rb deleted file mode 100644 index b94da14..0000000 --- a/app/services/modbus/commands/write_int16.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Modbus::Service.new(plc_id: 1, type: "int16", start_address: 0, value: 123).execute -module Modbus - module Commands - def write_int16(slave) - write_int(slave, 16) - end - end -end diff --git a/app/services/modbus/operations.rb b/app/services/modbus/operations.rb deleted file mode 100644 index 3fa9221..0000000 --- a/app/services/modbus/operations.rb +++ /dev/null @@ -1,93 +0,0 @@ -# app/lib/modbus/operations.rb -module Modbus - module Operations - # write method - def write_toggle_coil(slave) - with_timeout do - target_value = slave.read_coils(@start_address, 1).first == 0 ? 1 : 0 - slave.coils[@start_address] = (target_value == 1) - loop do - break if slave.read_coils(@start_address, 1).first == target_value - sleep 0.1 - end - end - end - - def write_coil(slave) - with_timeout do - slave.coils[@start_address] = @value - loop do - break if slave.read_coils(@start_address, 1).first == @value - sleep 0.1 - end - end - end - - def write_int(slave, bits) - with_timeout do - regs = slave.holding_registers - if bits == 16 - value = @value.present? ? @value.to_i : 123 - regs[@start_address] = value - end - end - end - - def write_float(slave) - with_timeout do - regs = slave.holding_registers - value = @value.present? ? @value.to_f : 123.45 - regs[@start_address], regs[@start_address + 1] = [ value ].pack("f").unpack("v*") - end - end - - # read method - def read_bit(slave, type) - with_timeout do - value = case type - when :read_coils - slave.read_coils(@start_address, @end_address - @start_address + 1) - when :discrete_inputs - slave.discrete_inputs[@start_address..@end_address] - else - # type code here - end - Rails.logger.info "[#{Time.current}] Read Bit (#{type}): #{value}" - value - end - end - - def read_int(slave, bits, type) - with_timeout do - result = slave.send(type) - values = (@start_address..@end_address).map { |i| result[i] } - Rails.logger.info "[#{Time.current}] Read Float#{bits} (#{type}): #{values}" - values - end - end - - def read_float(slave, type) - with_timeout do - result = slave.send(type) - values = [] - (@start_address..@end_address).step(2) do |i| - raw = [ result[i], result[i + 1] ] - packed = raw.pack("v*") - float = packed.unpack("f")[0].round(2) - values << float - end - Rails.logger.info "[#{Time.current}] Read Float32 (#{type}): #{values.inspect}" - values - end - end - - def with_timeout - seconds = ENV.fetch("OPERATION_TIME_OUT", 1).to_i - Timeout.timeout(seconds) do - yield - end - rescue Timeout::Error - Rails.logger.error "[#{Time.current}] TimeOut (#{seconds}s)" - end - end -end diff --git a/app/services/modbus/polling_service.rb b/app/services/modbus/polling_service.rb index bc38eb2..7da77b2 100644 --- a/app/services/modbus/polling_service.rb +++ b/app/services/modbus/polling_service.rb @@ -15,17 +15,15 @@ module Modbus if current_hour != last_logged_hour && now.min == 0 schedule = Schedule.find_by(hour: current_hour) - Modbus::Service.new( - type: "write_int16", - start_address: 2, - end_address: 2, - value: schedule.temperature * 10 - ).execute + serial_path = Rails.root.join("serial.rb") + system("ruby", serial_path.to_s, "#{schedule.temperature * 10}") + puts "[Schedule] #{current_hour}:00 -> Target temp: #{schedule.temperature}°C" last_logged_hour = current_hour end rescue StandardError => e error_message = "[#{Time.current}] 오류: #{e.message}" + puts error_message ensure sleep 0.1 diff --git a/app/services/modbus/service.rb b/app/services/modbus/service.rb deleted file mode 100644 index 96c7624..0000000 --- a/app/services/modbus/service.rb +++ /dev/null @@ -1,38 +0,0 @@ -require_relative "operations" - -Dir[File.join(__dir__.to_s, "commands", "**", "*.rb")].each do |file| - load file -end - -class Modbus::Service - include Modbus::Operations - include Modbus::Commands - - def initialize(type:, start_address:, end_address: nil, value: nil) - @type = type - @start_address = start_address.to_i - @end_address = end_address&.to_i - @value = value - @slave_id = 1 - @plc_host = "rubyon.co.kr" - @plc_port = 502 - end - - def execute - ModBus::TCPClient.new(@plc_host, @plc_port) do |client| - client.with_slave(@slave_id) do |slave| - method_name = @type.strip - # Commands 모듈에 정의된 메서드만 허용 - if Modbus::Commands.instance_methods(false).include?(method_name.to_sym) - return send(method_name, slave) - else - available_commands = Modbus::Commands.instance_methods(false).sort.map(&:to_s) - command_list = available_commands.join("\r\n") - raise ArgumentError, "지원되지 않는 type: #{@type}\r\n사용 가능한 명령어 목록:\r\n#{command_list}" - end - end - end - rescue => e - Rails.logger.error "[#{Time.current}] Service 오류: #{e.message}" - end -end diff --git a/serial.rb b/serial.rb new file mode 100644 index 0000000..5ea916a --- /dev/null +++ b/serial.rb @@ -0,0 +1,12 @@ +require "rmodbus" +require "ccutrer-serialport" + +value = ARGV[0]&.to_i + +ModBus::RTUClient.new("/dev/ttyUSB0", 9600) do |cl| + cl.with_slave(7) do |slave| + regs = slave.holding_registers + regs[2] = value + sleep 0.1 + end +end diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..ec638a8 --- /dev/null +++ b/start.sh @@ -0,0 +1,7 @@ +#!/bin/bash +cd /home/farmitry/farmitry_hvac +export HOME=/home/farmitry +export RBENV_ROOT="$HOME/.rbenv" +export PATH="$RBENV_ROOT/bin:$PATH" +eval "$(rbenv init - bash)" +exec bin/dev