diff --git a/app/controllers/modals_controller.rb b/app/controllers/modals_controller.rb index 7cdfe77..7ccb852 100644 --- a/app/controllers/modals_controller.rb +++ b/app/controllers/modals_controller.rb @@ -1,6 +1,6 @@ class ModalsController < ApplicationController def open @schedule = Schedule.new - render partial: "partials/modals_open", locals: { type: params[:type], close_button: params[:close_button] } + render partial: "partials/modals_open", locals: { type: params[:type], id: params[:id], close_button: params[:close_button] } end end diff --git a/app/controllers/modbus_controller.rb b/app/controllers/modbus_controller.rb index fb3f7a7..a943e5d 100644 --- a/app/controllers/modbus_controller.rb +++ b/app/controllers/modbus_controller.rb @@ -1,11 +1,2 @@ class ModbusController < ApplicationController - def start - Modbus::PollingService.start - redirect_to schedules_path - end - - def stop - Modbus::PollingService.stop - redirect_to schedules_path - end end diff --git a/app/controllers/schedules_controller.rb b/app/controllers/schedules_controller.rb index 4390cb1..db55185 100644 --- a/app/controllers/schedules_controller.rb +++ b/app/controllers/schedules_controller.rb @@ -1,7 +1,11 @@ class SchedulesController < ApplicationController def index - @schedule = Schedule.order(:hour, :minute) - @modbus_running = Modbus::PollingService.running? + @controllers = Controller.all + end + + def view + @controller = Controller.find(params[:id]) + @schedule = Schedule.where(controller_id: params[:id]).order(:hour, :minute) end def new @@ -12,10 +16,10 @@ class SchedulesController < ApplicationController @schedule = Schedule.new(schedule_params) if @schedule.save - redirect_to schedule_edit_schedules_path, notice: "스케쥴이 추가 되었습니다." + redirect_to schedule_edit_schedule_path(@schedule.controller_id), notice: "스케쥴이 추가 되었습니다." else error_messages = @schedule.errors.full_messages.join(", ") - redirect_to schedule_edit_schedules_path, alert: "스케쥴 추가 실패: #{error_messages}" + redirect_to schedule_edit_schedule_path(@schedule.controller_id), alert: "스케쥴 추가 실패: #{error_messages}" end end @@ -23,23 +27,23 @@ class SchedulesController < ApplicationController @schedule = Schedule.find_by(id: params[:id]) if @schedule.destroy - redirect_to schedule_edit_schedules_path, notice: "스케줄이 삭제되었습니다." + redirect_to schedule_edit_schedule_path(@schedule.controller_id), notice: "스케줄이 삭제되었습니다." else error_messages = @schedule.errors.full_messages.join(", ") - redirect_to schedule_edit_schedules_path, alert: "스케쥴 삭제 실패: #{error_messages}" + redirect_to schedule_edit_schedule_path(@schedule.controller_id), alert: "스케쥴 삭제 실패: #{error_messages}" end end def reset - Schedule.delete_all + Schedule.where(controller_id: params[:id]).destroy_all ActiveRecord::Base.connection.execute("DELETE FROM sqlite_sequence WHERE name='schedules'") - Rails.application.load_seed - redirect_to schedules_path, notice: "스케줄이 초기화되었습니다." + redirect_to schedule_edit_schedule_path(params[:id]), notice: "스케줄이 초기화되었습니다." end def schedule_edit - @schedule = Schedule.order(:hour, :minute) + @controller = Controller.find(params[:id]) + @schedule = Schedule.where(controller_id: params[:id]).order(:hour, :minute) end def schedule_edit_update @@ -63,14 +67,14 @@ class SchedulesController < ApplicationController end if error_messages.any? - redirect_to schedules_path, alert: error_messages.join("
") + redirect_to schedule_edit_schedule_path(params[:controller_id]), alert: error_messages.join("
") else - redirect_to schedules_path, notice: "스케줄이 성공적으로 업데이트되었습니다." + redirect_to schedule_edit_schedule_path(params[:controller_id]), notice: "스케줄이 성공적으로 업데이트되었습니다." end end private def schedule_params - params.require(:schedule).permit(:hour, :minute, :is_active, :temperature) + params.require(:schedule).permit(:controller_id, :hour, :minute, :is_active, :temperature) end end diff --git a/app/models/controller.rb b/app/models/controller.rb new file mode 100644 index 0000000..3d0f332 --- /dev/null +++ b/app/models/controller.rb @@ -0,0 +1,3 @@ +class Controller < ApplicationRecord + has_many :schedules +end diff --git a/app/models/schedule.rb b/app/models/schedule.rb index b8d23ca..1c1d7fd 100644 --- a/app/models/schedule.rb +++ b/app/models/schedule.rb @@ -1,9 +1,11 @@ class Schedule < ApplicationRecord + belongs_to :controller + validates :hour, presence: { message: "시간을 입력하세요" } validates :minute, presence: { message: "분을 입력하세요" } validates :temperature, presence: { message: "온도를 입력하세요" }, numericality: { message: "온도는 숫자여야 합니다" }, format: { with: /\A\d+(\.\d)?\z/, message: "온도는 소수점 한 자리까지 입력 가능합니다" } - validates :minute, uniqueness: { scope: :hour, message: "같은 시:분의 스케쥴이 이미 존재합니다" } + validates :minute, uniqueness: { scope: [ :controller_id, :hour ], message: "같은 시:분의 스케쥴이 이미 존재합니다" } end diff --git a/app/services/modbus/polling_service.rb b/app/services/modbus/polling_service.rb index b981e70..6a3d7db 100644 --- a/app/services/modbus/polling_service.rb +++ b/app/services/modbus/polling_service.rb @@ -14,47 +14,34 @@ module Modbus now = Time.now current_time = format("%02d:%02d", now.hour, now.min) puts "# current time: #{current_time}.#{now.sec}" - schedule = Schedule.find_by(hour: now.hour, minute: now.min) - apply_schedule(schedule) if schedule + schedules = Schedule.where(hour: now.hour, minute: now.min) + schedules.each { |schedule| apply_schedule(schedule) } end end - def stop - if defined?(@scheduler) - @scheduler.shutdown(:kill) - @scheduler = nil - puts "[#{Time.now}] Modbus polling service 중지됨" - else - puts "[#{Time.now}] Scheduler 인스턴스 없음" - end - end - - def running? - @scheduler - end - private def apply_schedule(schedule) if schedule.is_active - run_script("on_off.rb", "0", "[Schedule] ON") + run_script("on_off.rb", "#{schedule.controller.station_id}", "0", "[Schedule] #{schedule.controller.name} ON") sleep 2 run_script( "serial.rb", + "#{schedule.controller.station_id}", (schedule.temperature * 10).to_i.to_s, - "[Schedule] #{format('%02d:%02d', schedule.hour, schedule.minute)} → #{schedule.temperature}°C" + "[Schedule] #{schedule.controller.name} #{format('%02d:%02d', schedule.hour, schedule.minute)} → #{schedule.temperature}°C" ) else - run_script("on_off.rb", "1", "[Schedule] OFF") + run_script("on_off.rb", "#{schedule.controller.station_id}", "1", "[Schedule] #{schedule.controller.name} OFF") end end - def run_script(file, arg, success_msg) + def run_script(file, controller_id, value, success_msg) path = Rails.root.join(file) - if system("ruby", path.to_s, arg) + if system("ruby", path.to_s, controller_id, value) puts success_msg Rails.logger.info success_msg else - error_message = "[#{Time.now}] #{file} 실행 실패 (args: #{arg})" + error_message = "[#{Time.now}] #{file} 실행 실패 (station_id: #{controller_id}, value: #{value})" puts error_message Rails.logger.error error_message end diff --git a/app/views/partials/modals/_add_schedule.html.erb b/app/views/partials/modals/_add_schedule.html.erb index 7e7b284..db25ecd 100644 --- a/app/views/partials/modals/_add_schedule.html.erb +++ b/app/views/partials/modals/_add_schedule.html.erb @@ -1,5 +1,7 @@ <%= form_with model: @schedule, method: :post, data: { turbo: false }, class: 'flex flex-col h-full divide-y divide-border-table-border' do |form| %> + <%= form.hidden_field :controller_id, value: params[:id] %>
+ <%= Controller.find(params[:id]).name %> 컨트롤러 diff --git a/app/views/schedules/index.html.erb b/app/views/schedules/index.html.erb index 444599f..748fa05 100644 --- a/app/views/schedules/index.html.erb +++ b/app/views/schedules/index.html.erb @@ -1,42 +1,7 @@ -
-
-
- - - - - - - - - - <% @schedule.each do |s| %> - - - - - - - <% end %> - -
시간사용여부온도
<%= s.hour %>시<%= s.minute %>분<%= s.is_active %><%= s.temperature %> °C
-
-
- <%= button_to "스케쥴러 시작", start_modbus_index_path, method: :post, class: "btn bg-accept" %> - <%= button_to "스케쥴러 정지", stop_modbus_index_path, method: :post, class: "btn bg-danger" %> -
- -
- 스케쥴러 상태: - <% if @modbus_running %> - 실행 중 - <% else %> - 정지됨 - <% end %> -
-
-
-
- <%= link_to "수정", schedule_edit_schedules_path, class: "btn bg-default-slate" %> +
+
+ <% @controllers.each do |c| %> + <%= link_to c.name, view_schedule_path(c.id), class: "btn bg-primary" %> + <% end %>
diff --git a/app/views/schedules/schedule_edit.html.erb b/app/views/schedules/schedule_edit.html.erb index 8aebd07..abbf621 100644 --- a/app/views/schedules/schedule_edit.html.erb +++ b/app/views/schedules/schedule_edit.html.erb @@ -1,5 +1,7 @@ <%= form_with url: schedule_edit_update_schedules_path, method: :post, class: 'flex flex-col h-full divide-y divide-border-table-border' do %> + <%= hidden_field_tag :controller_id, params[:id] %>
+ <%= @controller.name %> 컨트롤러 @@ -40,13 +42,13 @@ <% end %>
- <%= button_to "초기화", reset_schedules_path, + <%= button_to "초기화", reset_schedule_path(params[:id]), method: :post, data: { turbo_confirm: "정말 초기화하시겠습니까? 모든 스케줄 데이터가 삭제됩니다." }, class: "btn bg-danger" %>
- <%= button_to "추가하기", open_modals_path(type: "add_schedule"), + <%= button_to "추가하기", open_modals_path(type: "add_schedule", id: params[:id]), class: "btn bg-default-slate", data: { turbo_method: :post, diff --git a/app/views/schedules/view.html.erb b/app/views/schedules/view.html.erb new file mode 100644 index 0000000..65675a8 --- /dev/null +++ b/app/views/schedules/view.html.erb @@ -0,0 +1,28 @@ +
+
+ <%= @controller.name %> 컨트롤러 +
+ + + + + + + + + + <% @schedule.each do |s| %> + + + + + + + <% end %> + +
시간사용여부온도
<%= s.hour %>시<%= s.minute %>분<%= s.is_active %><%= s.temperature %> °C
+
+
+ <%= link_to "수정", schedule_edit_schedule_path(@controller.id), class: "btn bg-default-slate" %> +
+
diff --git a/config/routes.rb b/config/routes.rb index 1df9955..dcc1dfd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -14,17 +14,13 @@ Rails.application.routes.draw do root "schedules#index" resources :schedules do - collection do - post "reset" + member do + get "view" get "schedule_edit" - post "schedule_edit_update" + post "reset" end - end - - resources :modbus do collection do - post "start" - post "stop" + post "schedule_edit_update" end end diff --git a/db/migrate/20250416131430_create_controllers.rb b/db/migrate/20250416131430_create_controllers.rb new file mode 100644 index 0000000..fe8652a --- /dev/null +++ b/db/migrate/20250416131430_create_controllers.rb @@ -0,0 +1,10 @@ +class CreateControllers < ActiveRecord::Migration[8.0] + def change + create_table :controllers do |t| + t.string :name + t.integer :station_id + + t.timestamps + end + end +end diff --git a/db/migrate/20250416131440_create_schedules.rb b/db/migrate/20250416131440_create_schedules.rb index 51df18e..97b1285 100644 --- a/db/migrate/20250416131440_create_schedules.rb +++ b/db/migrate/20250416131440_create_schedules.rb @@ -1,6 +1,7 @@ class CreateSchedules < ActiveRecord::Migration[8.0] def change create_table :schedules do |t| + t.references :controller, null: false, foreign_key: true t.integer :hour t.integer :minute t.boolean :is_active @@ -8,6 +9,6 @@ class CreateSchedules < ActiveRecord::Migration[8.0] t.timestamps end - add_index :schedules, [ :hour, :minute ], unique: true + add_index :schedules, [ :controller_id, :hour, :minute ], unique: true end end diff --git a/db/schema.rb b/db/schema.rb index cab8406..4dba5a7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,13 +11,24 @@ # It's strongly recommended that you check this file into your version control system. ActiveRecord::Schema[8.0].define(version: 2025_04_16_131440) do + create_table "controllers", force: :cascade do |t| + t.string "name" + t.integer "station_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "schedules", force: :cascade do |t| + t.integer "controller_id", null: false 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 + t.index ["controller_id", "hour", "minute"], name: "index_schedules_on_controller_id_and_hour_and_minute", unique: true + t.index ["controller_id"], name: "index_schedules_on_controller_id" end + + add_foreign_key "schedules", "controllers" end diff --git a/db/seeds.rb b/db/seeds.rb index 953e2e0..22289d3 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -8,6 +8,10 @@ # MovieGenre.find_or_create_by!(name: genre_name) # end +Controller.create(name: "3번방", station_id: 7) +Controller.create(name: "2번방", station_id: 8) + (0..23).each do | h | - Schedule.create!(hour: h, minute: 0, is_active: true, temperature: 15.0) + Schedule.create!(controller_id: 1, hour: h, minute: 0, is_active: true, temperature: 15.0) + Schedule.create!(controller_id: 2, hour: h, minute: 0, is_active: true, temperature: 15.0) end diff --git a/on_off.rb b/on_off.rb index 9dcf8e8..998757f 100644 --- a/on_off.rb +++ b/on_off.rb @@ -1,10 +1,11 @@ require "rmodbus" require "ccutrer-serialport" -value = ARGV[0]&.to_i +controller_id = ARGV[0]&.to_i +value = ARGV[1]&.to_i ModBus::RTUClient.new("/dev/ttyUSB0", 9600) do |cl| - cl.with_slave(7) do |slave| + cl.with_slave(controller_id) do |slave| regs = slave.holding_registers regs[22] = value sleep 0.1 diff --git a/serial.rb b/serial.rb index 5ea916a..dd9d4a9 100644 --- a/serial.rb +++ b/serial.rb @@ -1,10 +1,11 @@ require "rmodbus" require "ccutrer-serialport" -value = ARGV[0]&.to_i +controller_id = ARGV[0]&.to_i +value = ARGV[1]&.to_i ModBus::RTUClient.new("/dev/ttyUSB0", 9600) do |cl| - cl.with_slave(7) do |slave| + cl.with_slave(controller_id) do |slave| regs = slave.holding_registers regs[2] = value sleep 0.1 diff --git a/test.rb b/test.rb new file mode 100644 index 0000000..5ea916a --- /dev/null +++ b/test.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/test/fixtures/controllers.yml b/test/fixtures/controllers.yml new file mode 100644 index 0000000..d7a3329 --- /dev/null +++ b/test/fixtures/controllers.yml @@ -0,0 +1,11 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the "{}" from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/models/controller_test.rb b/test/models/controller_test.rb new file mode 100644 index 0000000..c355ca7 --- /dev/null +++ b/test/models/controller_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class ControllerTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end