diff --git a/app/controllers/modbus_controller.rb b/app/controllers/modbus_controller.rb
index 380dcda..cdaf8d3 100644
--- a/app/controllers/modbus_controller.rb
+++ b/app/controllers/modbus_controller.rb
@@ -4,6 +4,16 @@ class ModbusController < ApplicationController
@modbus_running = Modbus::PollingService.running?
end
+ def destroy
+ schedule = Schedule.find_by(id: params[:id])
+
+ if schedule.destroy
+ redirect_to schedule_edit_modbus_index_path, notice: "스케줄이 삭제되었습니다."
+ else
+ redirect_to schedule_edit_modbus_index_path, alert: "스케줄 삭제에 실패했습니다."
+ end
+ end
+
def start
Modbus::PollingService.start
redirect_to modbus_index_path
@@ -19,17 +29,23 @@ class ModbusController < ApplicationController
end
def schedule_edit_update
- error_hours = []
+ error_time = []
params[:schedule].each do |id, attributes|
schedule = Schedule.find_by(id: id)
- unless schedule.update(temperature: attributes[:temperature])
- error_hours << "#{schedule.hour}시"
+ unless schedule.update(
+ hour: attributes[:hour],
+ minute: attributes[:minute],
+ is_active: attributes[:is_active],
+ temperature: attributes[:temperature]
+ )
+
+ error_time << "#{schedule.hour}시 #{schedule.minute}분"
end
end
- if error_hours.any?
- redirect_to modbus_index_path, alert: "#{error_hours.join(', ')}의 온도 업데이트에 실패하였습니다."
+ if error_time.any?
+ redirect_to modbus_index_path, alert: "#{error_time.join(', ')}의 온도 업데이트에 실패하였습니다."
else
redirect_to modbus_index_path, notice: "스케줄이 성공적으로 업데이트되었습니다."
end
diff --git a/app/models/schedule.rb b/app/models/schedule.rb
index 741d2ff..1bc1090 100644
--- a/app/models/schedule.rb
+++ b/app/models/schedule.rb
@@ -1,6 +1,9 @@
class Schedule < ApplicationRecord
+ validates :hour, presence: true
+ validates :minute, presence: true
validates :temperature,
presence: true,
numericality: true,
format: { with: /\A\d+(\.\d)?\z/ }
+ validates :minute, uniqueness: { scope: :hour }
end
diff --git a/app/services/modbus/polling_service.rb b/app/services/modbus/polling_service.rb
index 7da77b2..9b5d5c0 100644
--- a/app/services/modbus/polling_service.rb
+++ b/app/services/modbus/polling_service.rb
@@ -3,45 +3,68 @@ module Modbus
class << self
def start
return if $modbus_polling_threads.any?(&:alive?)
-
- thread = Thread.new do
- puts "[#{Time.current}] Modbus polling service 시작됨"
- last_logged_hour = nil
- loop do
- begin
- now = Time.now
- current_hour = now.hour
-
- if current_hour != last_logged_hour && now.min == 0
- schedule = Schedule.find_by(hour: current_hour)
-
- 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
- end
- end
- end
- $modbus_polling_threads << thread
+ $modbus_polling_threads << start_thread
end
def stop
$modbus_polling_threads.each { |t| t.kill if t.alive? }
$modbus_polling_threads.clear
- puts "[#{Time.now}] Modbus polling service 중지됨"
+ puts "Modbus polling service 중지됨"
end
def running?
$modbus_polling_threads.any?(&:alive?)
end
+
+ private
+ def start_thread
+ Thread.new { run_polling_loop }
+ end
+
+ def run_polling_loop
+ puts "Modbus polling service 시작됨"
+ last_logged = nil
+
+ loop do
+ begin
+ now = Time.now
+ current = [ now.hour, now.min ]
+
+ if current != last_logged
+ puts "시간 변경 감지됨: #{current.join(':')}"
+ schedule = Schedule.find_by(hour: current[0], minute: current[1])
+ apply_schedule(schedule) if schedule
+ last_logged = current
+ end
+ rescue => e
+ puts "[#{Time.current}] #{file} 에러: #{e.message}"
+ ensure
+ sleep 1
+ end
+ end
+ end
+
+ def apply_schedule(schedule)
+ if schedule.is_active
+ run_script("on_off.rb", "0", "[Schedule] ON")
+ run_script(
+ "serial.rb",
+ (schedule.temperature * 10).to_i.to_s,
+ "[Schedule] #{schedule.hour}:#{schedule.minute} → #{schedule.temperature}°C"
+ )
+ else
+ run_script("on_off.rb", "1", "[Schedule] OFF")
+ end
+ end
+
+ def run_script(file, arg, success_msg)
+ path = Rails.root.join(file)
+ if system("ruby", path.to_s, arg)
+ puts success_msg
+ else
+ puts "[#{Time.current}] #{file} 실행 실패 (args: #{arg})"
+ end
+ end
end
end
end
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 0e9d66b..14ced63 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -23,12 +23,21 @@
-
+
<%= turbo_frame_tag :modals %>
<%= render "partials/header" %>
+ <% if flash[:notice] %>
+
+ <%= flash[:notice] %>
+
+ <% elsif flash[:alert] %>
+
+ <%= flash[:alert] %>
+
+ <% end %>
<%= yield %>
diff --git a/app/views/modbus/index.html.erb b/app/views/modbus/index.html.erb
index 6fc8d6b..9268ef3 100644
--- a/app/views/modbus/index.html.erb
+++ b/app/views/modbus/index.html.erb
@@ -1,18 +1,11 @@
-<% if flash[:notice] %>
-
- <%= flash[:notice] %>
-
-<% elsif flash[:alert] %>
-
- <%= flash[:alert] %>
-
-<% end %>
| 시간 |
+ 분 |
+ 사용여부 |
온도 |
@@ -20,6 +13,8 @@
<% @schedule.each do |s| %>
| <%= s.hour %>시 |
+ <%= s.minute %>분 |
+ <%= s.is_active %> |
<%= s.temperature %> °C |
<% end %>
diff --git a/app/views/modbus/schedule_edit.html.erb b/app/views/modbus/schedule_edit.html.erb
index 2bed1d4..ef26832 100644
--- a/app/views/modbus/schedule_edit.html.erb
+++ b/app/views/modbus/schedule_edit.html.erb
@@ -4,15 +4,30 @@
| 시간 |
+ 분 |
+ 사용여부 |
온도 |
+ 삭제 |
<% @schedule.each do |s| %>
- | <%= s.hour %>시 |
- <%= number_field_tag "schedule[#{s.id}][temperature]", s.temperature, step: "0.1", inputmode: "decimal", class: "border px-2 py-1 rounded" %>
- °C
+ |
+ <%= select_tag "schedule[#{s.id}][hour]",
+ options_for_select((0..23).map { |h| [h.to_s.rjust(2, '0'), h] }, s.hour),
+ class: "input-style" %>
+ |
+ <%= number_field_tag "schedule[#{s.id}][minute]", s.minute, min: 0, max: 59, step: 1, inputmode: "decimal", class: "input-style" %> |
+ <%= check_box_tag "schedule[#{s.id}][is_active]", "1", s.is_active == true || s.is_active == 1 %> |
+ <%= number_field_tag "schedule[#{s.id}][temperature]", s.temperature, step: "0.1", inputmode: "decimal", class: "input-style" %> |
+
+ <%= link_to "삭제", modbus_path(s),
+ data: {
+ turbo_method: :delete,
+ turbo_confirm: "정말 삭제하시겠습니까?"
+ },
+ class: "btn bg-danger text-sm" %>
|
<% end %>
diff --git a/db/migrate/20250416131440_create_schedules.rb b/db/migrate/20250416131440_create_schedules.rb
index 1ed0d45..51df18e 100644
--- a/db/migrate/20250416131440_create_schedules.rb
+++ b/db/migrate/20250416131440_create_schedules.rb
@@ -2,9 +2,12 @@ class CreateSchedules < ActiveRecord::Migration[8.0]
def change
create_table :schedules do |t|
t.integer :hour
+ t.integer :minute
+ t.boolean :is_active
t.float :temperature
t.timestamps
end
+ add_index :schedules, [ :hour, :minute ], unique: true
end
end
diff --git a/db/schema.rb b/db/schema.rb
index afe3d8e..cab8406 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -13,8 +13,11 @@
ActiveRecord::Schema[8.0].define(version: 2025_04_16_131440) do
create_table "schedules", force: :cascade do |t|
t.integer "hour"
+ t.integer "minute"
+ t.boolean "is_active"
t.float "temperature"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["hour", "minute"], name: "index_schedules_on_hour_and_minute", unique: true
end
end
diff --git a/db/seeds.rb b/db/seeds.rb
index b19f220..953e2e0 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -9,5 +9,5 @@
# end
(0..23).each do | h |
- Schedule.create!(hour: h, temperature: 15.0)
+ Schedule.create!(hour: h, minute: 0, is_active: true, temperature: 15.0)
end
diff --git a/on_off.rb b/on_off.rb
new file mode 100644
index 0000000..032e304
--- /dev/null
+++ b/on_off.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[16] = value
+ sleep 0.1
+ end
+end