분 추가 (스케쥴 new 추가 필요)
This commit is contained in:
parent
cd7620672f
commit
eb3bc33509
|
|
@ -4,6 +4,16 @@ class ModbusController < ApplicationController
|
||||||
@modbus_running = Modbus::PollingService.running?
|
@modbus_running = Modbus::PollingService.running?
|
||||||
end
|
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
|
def start
|
||||||
Modbus::PollingService.start
|
Modbus::PollingService.start
|
||||||
redirect_to modbus_index_path
|
redirect_to modbus_index_path
|
||||||
|
|
@ -19,17 +29,23 @@ class ModbusController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def schedule_edit_update
|
def schedule_edit_update
|
||||||
error_hours = []
|
error_time = []
|
||||||
|
|
||||||
params[:schedule].each do |id, attributes|
|
params[:schedule].each do |id, attributes|
|
||||||
schedule = Schedule.find_by(id: id)
|
schedule = Schedule.find_by(id: id)
|
||||||
unless schedule.update(temperature: attributes[:temperature])
|
unless schedule.update(
|
||||||
error_hours << "#{schedule.hour}시"
|
hour: attributes[:hour],
|
||||||
|
minute: attributes[:minute],
|
||||||
|
is_active: attributes[:is_active],
|
||||||
|
temperature: attributes[:temperature]
|
||||||
|
)
|
||||||
|
|
||||||
|
error_time << "#{schedule.hour}시 #{schedule.minute}분"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if error_hours.any?
|
if error_time.any?
|
||||||
redirect_to modbus_index_path, alert: "#{error_hours.join(', ')}의 온도 업데이트에 실패하였습니다."
|
redirect_to modbus_index_path, alert: "#{error_time.join(', ')}의 온도 업데이트에 실패하였습니다."
|
||||||
else
|
else
|
||||||
redirect_to modbus_index_path, notice: "스케줄이 성공적으로 업데이트되었습니다."
|
redirect_to modbus_index_path, notice: "스케줄이 성공적으로 업데이트되었습니다."
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
class Schedule < ApplicationRecord
|
class Schedule < ApplicationRecord
|
||||||
|
validates :hour, presence: true
|
||||||
|
validates :minute, presence: true
|
||||||
validates :temperature,
|
validates :temperature,
|
||||||
presence: true,
|
presence: true,
|
||||||
numericality: true,
|
numericality: true,
|
||||||
format: { with: /\A\d+(\.\d)?\z/ }
|
format: { with: /\A\d+(\.\d)?\z/ }
|
||||||
|
validates :minute, uniqueness: { scope: :hour }
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,45 +3,68 @@ module Modbus
|
||||||
class << self
|
class << self
|
||||||
def start
|
def start
|
||||||
return if $modbus_polling_threads.any?(&:alive?)
|
return if $modbus_polling_threads.any?(&:alive?)
|
||||||
|
$modbus_polling_threads << start_thread
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def stop
|
def stop
|
||||||
$modbus_polling_threads.each { |t| t.kill if t.alive? }
|
$modbus_polling_threads.each { |t| t.kill if t.alive? }
|
||||||
$modbus_polling_threads.clear
|
$modbus_polling_threads.clear
|
||||||
puts "[#{Time.now}] Modbus polling service 중지됨"
|
puts "Modbus polling service 중지됨"
|
||||||
end
|
end
|
||||||
|
|
||||||
def running?
|
def running?
|
||||||
$modbus_polling_threads.any?(&:alive?)
|
$modbus_polling_threads.any?(&:alive?)
|
||||||
end
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,21 @@
|
||||||
<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">
|
<body>
|
||||||
<%= turbo_frame_tag :modals %>
|
<%= turbo_frame_tag :modals %>
|
||||||
<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">
|
<div class="flex flex-row flex-1 w-full divide-x divide-border-table-border">
|
||||||
<div class="w-full h-full">
|
<div class="w-full h-full">
|
||||||
|
<% if flash[:notice] %>
|
||||||
|
<div class="m-4 rounded px-4 py-2 bg-accept text-white">
|
||||||
|
<%= flash[:notice] %>
|
||||||
|
</div>
|
||||||
|
<% elsif flash[:alert] %>
|
||||||
|
<div class="m-4 rounded px-4 py-2 bg-danger text-white">
|
||||||
|
<%= flash[:alert] %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
<%= yield %>
|
<%= yield %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,11 @@
|
||||||
<% if flash[:notice] %>
|
|
||||||
<div class="m-4 rounded px-4 py-2 bg-accept text-white">
|
|
||||||
<%= flash[:notice] %>
|
|
||||||
</div>
|
|
||||||
<% elsif flash[:alert] %>
|
|
||||||
<div class="m-4 rounded px-4 py-2 bg-danger text-white">
|
|
||||||
<%= flash[:alert] %>
|
|
||||||
</div>
|
|
||||||
<% 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 flex-col flex-1 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>
|
||||||
<th>시간</th>
|
<th>시간</th>
|
||||||
|
<th>분</th>
|
||||||
|
<th>사용여부</th>
|
||||||
<th>온도</th>
|
<th>온도</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
@ -20,6 +13,8 @@
|
||||||
<% @schedule.each do |s| %>
|
<% @schedule.each do |s| %>
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= s.hour %>시</td>
|
<td><%= s.hour %>시</td>
|
||||||
|
<td><%= s.minute %>분</td>
|
||||||
|
<td><%= s.is_active %></td>
|
||||||
<td><%= s.temperature %> °C</td>
|
<td><%= s.temperature %> °C</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,30 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>시간</th>
|
<th>시간</th>
|
||||||
|
<th>분</th>
|
||||||
|
<th>사용여부</th>
|
||||||
<th>온도</th>
|
<th>온도</th>
|
||||||
|
<th>삭제</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<% @schedule.each do |s| %>
|
<% @schedule.each do |s| %>
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= s.hour %>시</td>
|
<td>
|
||||||
<td><%= number_field_tag "schedule[#{s.id}][temperature]", s.temperature, step: "0.1", inputmode: "decimal", class: "border px-2 py-1 rounded" %>
|
<%= select_tag "schedule[#{s.id}][hour]",
|
||||||
°C
|
options_for_select((0..23).map { |h| [h.to_s.rjust(2, '0'), h] }, s.hour),
|
||||||
|
class: "input-style" %>
|
||||||
|
</td>
|
||||||
|
<td><%= number_field_tag "schedule[#{s.id}][minute]", s.minute, min: 0, max: 59, step: 1, inputmode: "decimal", class: "input-style" %></td>
|
||||||
|
<td><%= check_box_tag "schedule[#{s.id}][is_active]", "1", s.is_active == true || s.is_active == 1 %></td>
|
||||||
|
<td><%= number_field_tag "schedule[#{s.id}][temperature]", s.temperature, step: "0.1", inputmode: "decimal", class: "input-style" %></td>
|
||||||
|
<td>
|
||||||
|
<%= link_to "삭제", modbus_path(s),
|
||||||
|
data: {
|
||||||
|
turbo_method: :delete,
|
||||||
|
turbo_confirm: "정말 삭제하시겠습니까?"
|
||||||
|
},
|
||||||
|
class: "btn bg-danger text-sm" %>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,12 @@ class CreateSchedules < ActiveRecord::Migration[8.0]
|
||||||
def change
|
def change
|
||||||
create_table :schedules do |t|
|
create_table :schedules do |t|
|
||||||
t.integer :hour
|
t.integer :hour
|
||||||
|
t.integer :minute
|
||||||
|
t.boolean :is_active
|
||||||
t.float :temperature
|
t.float :temperature
|
||||||
|
|
||||||
t.timestamps
|
t.timestamps
|
||||||
end
|
end
|
||||||
|
add_index :schedules, [ :hour, :minute ], unique: true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,11 @@
|
||||||
ActiveRecord::Schema[8.0].define(version: 2025_04_16_131440) do
|
ActiveRecord::Schema[8.0].define(version: 2025_04_16_131440) do
|
||||||
create_table "schedules", force: :cascade do |t|
|
create_table "schedules", force: :cascade do |t|
|
||||||
t.integer "hour"
|
t.integer "hour"
|
||||||
|
t.integer "minute"
|
||||||
|
t.boolean "is_active"
|
||||||
t.float "temperature"
|
t.float "temperature"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["hour", "minute"], name: "index_schedules_on_hour_and_minute", unique: true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,5 @@
|
||||||
# end
|
# end
|
||||||
|
|
||||||
(0..23).each do | h |
|
(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
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue