Compare commits

..

3 Commits

Author SHA1 Message Date
ming 6209b926e2 noti 스타일 수정 2025-04-17 11:22:27 +09:00
RubyOn 09068d9f70 Merge remote-tracking branch 'origin/develop_mh' 2025-04-17 17:07:06 +09:00
RubyOn fed1b75fd2 serial.rb 파일 호출 방식으로 변경 2025-04-17 17:04:23 +09:00
21 changed files with 39 additions and 228 deletions

View File

@ -69,4 +69,5 @@ group :test do
gem "selenium-webdriver" gem "selenium-webdriver"
end end
gem "ccutrer-serialport"
gem "rmodbus" gem "rmodbus"

View File

@ -102,6 +102,8 @@ GEM
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0) regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2) xpath (~> 3.2)
ccutrer-serialport (1.1.0)
ffi (~> 1.9, >= 1.9.3)
concurrent-ruby (1.3.5) concurrent-ruby (1.3.5)
connection_pool (2.5.0) connection_pool (2.5.0)
crass (1.0.6) crass (1.0.6)
@ -126,6 +128,7 @@ GEM
erubi (1.13.1) erubi (1.13.1)
et-orbi (1.2.11) et-orbi (1.2.11)
tzinfo tzinfo
ffi (1.17.2-aarch64-linux-gnu)
foreman (0.88.1) foreman (0.88.1)
fugit (1.11.1) fugit (1.11.1)
et-orbi (~> 1, >= 1.2.11) et-orbi (~> 1, >= 1.2.11)
@ -387,6 +390,7 @@ DEPENDENCIES
bootsnap bootsnap
brakeman brakeman
capybara capybara
ccutrer-serialport
cssbundling-rails cssbundling-rails
debug debug
erb_lint erb_lint

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -15,17 +15,15 @@ module Modbus
if current_hour != last_logged_hour && now.min == 0 if current_hour != last_logged_hour && now.min == 0
schedule = Schedule.find_by(hour: current_hour) schedule = Schedule.find_by(hour: current_hour)
Modbus::Service.new( serial_path = Rails.root.join("serial.rb")
type: "write_int16", system("ruby", serial_path.to_s, "#{schedule.temperature * 10}")
start_address: 2,
end_address: 2,
value: schedule.temperature * 10
).execute
puts "[Schedule] #{current_hour}:00 -> Target temp: #{schedule.temperature}°C" puts "[Schedule] #{current_hour}:00 -> Target temp: #{schedule.temperature}°C"
last_logged_hour = current_hour last_logged_hour = current_hour
end end
rescue StandardError => e rescue StandardError => e
error_message = "[#{Time.current}] 오류: #{e.message}" error_message = "[#{Time.current}] 오류: #{e.message}"
puts error_message puts error_message
ensure ensure
sleep 0.1 sleep 0.1

View File

@ -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

View File

@ -23,11 +23,11 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
</head> </head>
<body class="h-screen overflow-hidden"> <body class="h-screen">
<main class="flex flex-col h-full divide-y divide-border-table-border"> <main class="flex flex-col h-full divide-y divide-border-table-border">
<%= render "partials/header" %> <%= render "partials/header" %>
<div class="flex flex-row flex-1 w-full divide-x divide-border-table-border overflow-hidden"> <div class="flex flex-row flex-1 w-full divide-x divide-border-table-border">
<%= render "partials/sidebar" %> <%#= render "partials/sidebar" %>
<div class="w-full h-full"> <div class="w-full h-full">
<%= yield %> <%= yield %>
</div> </div>

View File

@ -1,14 +1,14 @@
<% if flash[:notice] %> <% if flash[:notice] %>
<div class="mb-4 rounded bg-green-100 px-4 py-2 text-green-800 border border-green-300"> <div class="m-4 rounded px-4 py-2 bg-accept text-white">
<%= flash[:notice] %> <%= flash[:notice] %>
</div> </div>
<% elsif flash[:alert] %> <% elsif flash[:alert] %>
<div class="mb-4 rounded bg-red-100 px-4 py-2 text-red-800 border border-red-300"> <div class="m-4 rounded px-4 py-2 bg-danger text-white">
<%= flash[:alert] %> <%= flash[:alert] %>
</div> </div>
<% end %> <% end %>
<div class="flex flex-col h-full divide-y divide-border-table-border"> <div class="flex flex-col h-full divide-y divide-border-table-border">
<div class="flex-1 overflow-y-auto p-4 space-y-4"> <div class="flex flex-col flex-1 p-4 space-y-4">
<table class="base-table"> <table class="base-table">
<thead> <thead>
<tr> <tr>

View File

@ -1,5 +1,5 @@
<%= form_with url: schedule_edit_update_modbus_index_path, method: :post, class: 'flex flex-col h-full divide-y divide-border-table-border' do %> <%= form_with url: schedule_edit_update_modbus_index_path, method: :post, class: 'flex flex-col h-full divide-y divide-border-table-border' do %>
<div class="flex-1 overflow-y-auto p-4"> <div class="flex-1 p-4">
<table class="base-table"> <table class="base-table">
<thead> <thead>
<tr> <tr>

View File

@ -4,8 +4,8 @@
<div class="flex h-full items-center tracking-[0.5rem]">FARMITRY</div> <div class="flex h-full items-center tracking-[0.5rem]">FARMITRY</div>
</div> </div>
<div data-timer-target="output" class="text-gray-600"></div> <div data-timer-target="output" class="text-gray-600"></div>
<div class="flex flex-row items-center gap-x-4"> <!-- <div class="flex flex-row items-center gap-x-4">-->
<i class="fa-solid fa-circle-user text-4xl text-default-slate-dark"></i> <!-- <i class="fa-solid fa-circle-user text-4xl text-default-slate-dark leading-10"></i>-->
<%= link_to "로그아웃", '', class: "btn" %> <%#= link_to "로그아웃", '', class: "btn" %>
</div> <!-- </div>-->
</header> </header>

12
serial.rb Normal file
View File

@ -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

7
start.sh Executable file
View File

@ -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