Compare commits
No commits in common. "faaf8677e3b79e679074e993ecca48d7393e044f" and "ddb0d6a69991af28b227bf9abf543ddb31e2d11e" have entirely different histories.
faaf8677e3
...
ddb0d6a699
|
|
@ -1,7 +0,0 @@
|
|||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="rubyon">
|
||||
<words>
|
||||
<w>farmitry</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
--color-warning: rgb(255, 202, 40);
|
||||
--color-accept: rgb(121, 134, 203);
|
||||
--color-danger: rgb(239, 83, 80);
|
||||
--color-table-border: rgb(130, 144, 158);
|
||||
--color-table-border: rgb(203, 213, 225);
|
||||
|
||||
/* theme */
|
||||
--color-base-background: rgb(39, 44, 56);
|
||||
|
|
@ -179,15 +179,15 @@
|
|||
|
||||
/*Base Table*/
|
||||
.base-table {
|
||||
@apply min-w-full whitespace-nowrap border border-table-border
|
||||
@apply min-w-full divide-y divide-table-border whitespace-nowrap border border-table-border
|
||||
}
|
||||
|
||||
.base-table th {
|
||||
@apply bg-base-text px-2 py-2 text-left font-bold lg:table-cell
|
||||
@apply bg-default px-2 py-2 text-left font-semibold text-default-slate-dark lg:table-cell
|
||||
}
|
||||
|
||||
.base-table td {
|
||||
@apply border-t border-table-border px-2 py-1 lg:table-cell whitespace-nowrap
|
||||
@apply border-t border-table-border px-2 py-1 text-default-slate lg:table-cell whitespace-nowrap
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +205,6 @@
|
|||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,4 @@
|
|||
class ApplicationController < ActionController::Base
|
||||
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
|
||||
allow_browser versions: :modern
|
||||
|
||||
before_action :set_controllers
|
||||
|
||||
private
|
||||
def set_controllers
|
||||
@controllers = Controller.all
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
class ModalsController < ApplicationController
|
||||
def open
|
||||
@schedule = Schedule.new
|
||||
render partial: "partials/modals_open", locals: { type: params[:type], id: params[:id], close_button: params[:close_button] }
|
||||
render partial: "partials/modals_open", locals: { type: params[:type], close_button: params[:close_button] }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,2 +1,11 @@
|
|||
class ModbusController < ApplicationController
|
||||
def start
|
||||
Modbus::PollingService.start
|
||||
redirect_to schedules_path
|
||||
end
|
||||
|
||||
def stop
|
||||
Modbus::PollingService.stop
|
||||
redirect_to schedules_path
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
class SchedulesController < ApplicationController
|
||||
def index
|
||||
@controllers = Controller.all
|
||||
end
|
||||
|
||||
def view
|
||||
@controller = Controller.find(params[:id])
|
||||
@schedule = Schedule.where(controller_id: params[:id]).order(:hour, :minute)
|
||||
@schedule = Schedule.order(:hour, :minute)
|
||||
@modbus_running = Modbus::PollingService.running?
|
||||
end
|
||||
|
||||
def new
|
||||
|
|
@ -16,10 +12,10 @@ class SchedulesController < ApplicationController
|
|||
@schedule = Schedule.new(schedule_params)
|
||||
|
||||
if @schedule.save
|
||||
redirect_to schedule_edit_schedule_path(@schedule.controller_id), notice: "스케쥴이 추가 되었습니다."
|
||||
redirect_to schedule_edit_schedules_path, notice: "스케쥴이 추가 되었습니다."
|
||||
else
|
||||
error_messages = @schedule.errors.full_messages.join(", ")
|
||||
redirect_to schedule_edit_schedule_path(@schedule.controller_id), alert: "스케쥴 추가 실패: #{error_messages}"
|
||||
redirect_to schedule_edit_schedules_path, alert: "스케쥴 추가 실패: #{error_messages}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -27,23 +23,23 @@ class SchedulesController < ApplicationController
|
|||
@schedule = Schedule.find_by(id: params[:id])
|
||||
|
||||
if @schedule.destroy
|
||||
redirect_to schedule_edit_schedule_path(@schedule.controller_id), notice: "스케줄이 삭제되었습니다."
|
||||
redirect_to schedule_edit_schedules_path, notice: "스케줄이 삭제되었습니다."
|
||||
else
|
||||
error_messages = @schedule.errors.full_messages.join(", ")
|
||||
redirect_to schedule_edit_schedule_path(@schedule.controller_id), alert: "스케쥴 삭제 실패: #{error_messages}"
|
||||
redirect_to schedule_edit_schedules_path, alert: "스케쥴 삭제 실패: #{error_messages}"
|
||||
end
|
||||
end
|
||||
|
||||
def reset
|
||||
Schedule.where(controller_id: params[:id]).destroy_all
|
||||
Schedule.delete_all
|
||||
ActiveRecord::Base.connection.execute("DELETE FROM sqlite_sequence WHERE name='schedules'")
|
||||
Rails.application.load_seed
|
||||
|
||||
redirect_to schedule_edit_schedule_path(params[:id]), notice: "스케줄이 초기화되었습니다."
|
||||
redirect_to schedules_path, notice: "스케줄이 초기화되었습니다."
|
||||
end
|
||||
|
||||
def schedule_edit
|
||||
@controller = Controller.find(params[:id])
|
||||
@schedule = Schedule.where(controller_id: params[:id]).order(:hour, :minute)
|
||||
@schedule = Schedule.order(:hour, :minute)
|
||||
end
|
||||
|
||||
def schedule_edit_update
|
||||
|
|
@ -67,14 +63,14 @@ class SchedulesController < ApplicationController
|
|||
end
|
||||
|
||||
if error_messages.any?
|
||||
redirect_to schedule_edit_schedule_path(params[:controller_id]), alert: error_messages.join("<br>")
|
||||
redirect_to schedules_path, alert: error_messages.join("<br>")
|
||||
else
|
||||
redirect_to schedule_edit_schedule_path(params[:controller_id]), notice: "스케줄이 성공적으로 업데이트되었습니다."
|
||||
redirect_to schedules_path, notice: "스케줄이 성공적으로 업데이트되었습니다."
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def schedule_params
|
||||
params.require(:schedule).permit(:controller_id, :hour, :minute, :is_active, :temperature)
|
||||
params.require(:schedule).permit(:hour, :minute, :is_active, :temperature)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
class Controller < ApplicationRecord
|
||||
has_many :schedules
|
||||
end
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
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: [ :controller_id, :hour ], message: "같은 시:분의 스케쥴이 이미 존재합니다" }
|
||||
validates :minute, uniqueness: { scope: :hour, message: "같은 시:분의 스케쥴이 이미 존재합니다" }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,43 +14,47 @@ module Modbus
|
|||
now = Time.now
|
||||
current_time = format("%02d:%02d", now.hour, now.min)
|
||||
puts "# current time: #{current_time}.#{now.sec}"
|
||||
schedules = Schedule.where(hour: now.hour, minute: now.min)
|
||||
schedules.each { |schedule| apply_schedule(schedule) }
|
||||
schedule = Schedule.find_by(hour: now.hour, minute: now.min)
|
||||
apply_schedule(schedule) if 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(
|
||||
"power",
|
||||
"#{schedule.controller.station_id}",
|
||||
"0",
|
||||
"[Schedule] #{schedule.controller.name} ON"
|
||||
)
|
||||
run_script("on_off.rb", "0", "[Schedule] ON")
|
||||
sleep 2
|
||||
run_script(
|
||||
"temp",
|
||||
"#{schedule.controller.station_id}",
|
||||
"serial.rb",
|
||||
(schedule.temperature * 10).to_i.to_s,
|
||||
"[Schedule] #{schedule.controller.name} #{format('%02d:%02d', schedule.hour, schedule.minute)} → #{schedule.temperature}°C"
|
||||
"[Schedule] #{format('%02d:%02d', schedule.hour, schedule.minute)} → #{schedule.temperature}°C"
|
||||
)
|
||||
else
|
||||
run_script(
|
||||
"power",
|
||||
"#{schedule.controller.station_id}",
|
||||
"1",
|
||||
"[Schedule] #{schedule.controller.name} OFF"
|
||||
)
|
||||
run_script("on_off.rb", "1", "[Schedule] OFF")
|
||||
end
|
||||
end
|
||||
|
||||
def run_script(mode, controller_id, value, success_msg)
|
||||
if system("ruby", "modbus.rb", mode, controller_id, value)
|
||||
def run_script(file, arg, success_msg)
|
||||
path = Rails.root.join(file)
|
||||
if system("ruby", path.to_s, arg)
|
||||
puts success_msg
|
||||
Rails.logger.info success_msg
|
||||
else
|
||||
error_message = "[#{Time.now}] 실행 실패 (station_id: #{controller_id}, value: #{value})"
|
||||
error_message = "[#{Time.now}] #{file} 실행 실패 (args: #{arg})"
|
||||
puts error_message
|
||||
Rails.logger.error error_message
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,25 +23,22 @@
|
|||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
|
||||
</head>
|
||||
|
||||
<body class="h-screen overflow-hidden bg-base-background text-base-text min-w-[400px] overflow-x-auto">
|
||||
<body>
|
||||
<%= turbo_frame_tag :modals %>
|
||||
<main class="flex flex-col h-full">
|
||||
<main class="flex flex-col h-full divide-y divide-border-table-border">
|
||||
<%= render "partials/header" %>
|
||||
<div class="flex flex-row flex-1 w-full overflow-hidden">
|
||||
<%= render "partials/sidebar" %>
|
||||
<div class="flex-1 h-full pb-4 pr-4">
|
||||
<div class="flex-1 h-full content">
|
||||
<% if flash[:notice] %>
|
||||
<div class="m-4 rounded px-4 py-2 bg-accept text-white">
|
||||
<%= raw flash[:notice] %>
|
||||
</div>
|
||||
<% elsif flash[:alert] %>
|
||||
<div class="m-4 rounded px-4 py-2 bg-danger text-white">
|
||||
<%= raw flash[:alert] %>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= yield %>
|
||||
</div>
|
||||
<div class="flex flex-row flex-1 w-full divide-x divide-border-table-border">
|
||||
<div class="w-full h-full">
|
||||
<% if flash[:notice] %>
|
||||
<div class="m-4 rounded px-4 py-2 bg-accept text-white">
|
||||
<%= raw flash[:notice] %>
|
||||
</div>
|
||||
<% elsif flash[:alert] %>
|
||||
<div class="m-4 rounded px-4 py-2 bg-danger text-white">
|
||||
<%= raw flash[:alert] %>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= yield %>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -7,16 +7,30 @@
|
|||
HOME
|
||||
</div>
|
||||
</div>
|
||||
<% @controllers.each do |c| %>
|
||||
<%= link_to view_schedule_path(c.id), class: "menu-group" do %>
|
||||
<div class="menu-group-icon">
|
||||
<i class="fa-solid fa-temperature-high text-base-text/30 text-4xl"></i>
|
||||
</div>
|
||||
<div class="menu-group-name">
|
||||
<%= c.name %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<div class="menu-group">
|
||||
<div class="menu-group-icon">
|
||||
<i class="fa-solid fa-temperature-high text-base-text/30 text-4xl"></i>
|
||||
</div>
|
||||
<div class="menu-group-name">
|
||||
메뉴 2
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-group">
|
||||
<div class="menu-group-icon">
|
||||
<i class="fa-solid fa-fan text-base-text/30 text-4xl"></i>
|
||||
</div>
|
||||
<div class="menu-group-name">
|
||||
메뉴 3
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-group">
|
||||
<div class="menu-group-icon">
|
||||
<i class="fa-solid fa-arrows-spin text-base-text/30 text-4xl"></i>
|
||||
</div>
|
||||
<div class="menu-group-name">
|
||||
메뉴 4
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-group">
|
||||
<div class="menu-group-icon">
|
||||
<i class="fa-solid fa-gear text-base-text/30 text-4xl"></i>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
<%= 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] %>
|
||||
<div class="flex-1 p-4">
|
||||
<span class="text-2xl font-bold"><%= Controller.find(params[:id]).name %> 컨트롤러</span>
|
||||
<table class="base-table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
|
|||
|
|
@ -1 +1,42 @@
|
|||
<div>MAIN</div>
|
||||
<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">
|
||||
<table class="base-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>시간</th>
|
||||
<th>분</th>
|
||||
<th>사용여부</th>
|
||||
<th>온도</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @schedule.each do |s| %>
|
||||
<tr>
|
||||
<td><%= s.hour %>시</td>
|
||||
<td><%= s.minute %>분</td>
|
||||
<td><%= s.is_active %></td>
|
||||
<td><%= s.temperature %> °C</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<div>
|
||||
<div class="flex gap-x-2">
|
||||
<%= button_to "스케쥴러 시작", start_modbus_index_path, method: :post, class: "btn bg-accept" %>
|
||||
<%= button_to "스케쥴러 정지", stop_modbus_index_path, method: :post, class: "btn bg-danger" %>
|
||||
</div>
|
||||
|
||||
<div class="py-4">
|
||||
<span class="font-semibold">스케쥴러 상태:</span>
|
||||
<% if @modbus_running %>
|
||||
<span class="text-accept font-bold">실행 중</span>
|
||||
<% else %>
|
||||
<span class="text-danger font-bold">정지됨</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex p-4">
|
||||
<%= link_to "수정", schedule_edit_schedules_path, class: "btn bg-default-slate" %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,58 +1,55 @@
|
|||
<div class="flex flex-col h-full text-white overflow-y-auto ">
|
||||
<div class="flex flex-col flex-1 divide-y divide-base-border">
|
||||
<%= form_with url: schedule_edit_update_schedules_path, method: :post, class: 'flex flex-col h-full divide-y divide-border-base-border p-4' do %>
|
||||
<%= hidden_field_tag :controller_id, params[:id] %>
|
||||
<div class="space-y-4">
|
||||
<div class="flex justify-between">
|
||||
<div class="text-2xl font-bold"><%= @controller.name %> 컨트롤러</div>
|
||||
<%= submit_tag "업데이트", class: "btn bg-primary" %>
|
||||
</div>
|
||||
<table class="base-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>시간</th>
|
||||
<th>분</th>
|
||||
<th>사용여부</th>
|
||||
<th>온도</th>
|
||||
<th>삭제</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @schedule.each do |s| %>
|
||||
<tr>
|
||||
<td>
|
||||
<%= 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" %>
|
||||
</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 "삭제", schedule_path(s),
|
||||
data: {
|
||||
turbo_method: :delete,
|
||||
turbo_confirm: "정말 삭제하시겠습니까?"
|
||||
},
|
||||
class: "btn bg-danger text-sm" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="flex p-4 space-x-4">
|
||||
<%= button_to "초기화", reset_schedule_path(params[:id]),
|
||||
method: :post,
|
||||
data: { turbo_confirm: "정말 초기화하시겠습니까? 모든 스케줄 데이터가 삭제됩니다." },
|
||||
class: "btn bg-danger" %>
|
||||
<%= button_to "추가하기", open_modals_path(type: "add_schedule", id: params[:id]),
|
||||
class: "btn bg-default-slate",
|
||||
data: {
|
||||
turbo_method: :post,
|
||||
turbo_frame: "modals"
|
||||
} %>
|
||||
</div>
|
||||
<%= form_with url: schedule_edit_update_schedules_path, method: :post, class: 'flex flex-col h-full divide-y divide-border-table-border' do %>
|
||||
<div class="flex-1 p-4">
|
||||
<table class="base-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>시간</th>
|
||||
<th>분</th>
|
||||
<th>사용여부</th>
|
||||
<th>온도</th>
|
||||
<th>삭제</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @schedule.each do |s| %>
|
||||
<tr>
|
||||
<td>
|
||||
<%= 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" %>
|
||||
</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 "삭제", schedule_path(s),
|
||||
data: {
|
||||
turbo_method: :delete,
|
||||
turbo_confirm: "정말 삭제하시겠습니까?"
|
||||
},
|
||||
class: "btn bg-danger text-sm" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="flex p-4">
|
||||
<%= submit_tag "업데이트", class: "btn bg-primary" %>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="flex p-4">
|
||||
<%= button_to "초기화", reset_schedules_path,
|
||||
method: :post,
|
||||
data: { turbo_confirm: "정말 초기화하시겠습니까? 모든 스케줄 데이터가 삭제됩니다." },
|
||||
class: "btn bg-danger" %>
|
||||
</div>
|
||||
<div class="flex p-4">
|
||||
<%= button_to "추가하기", open_modals_path(type: "add_schedule"),
|
||||
class: "btn bg-default-slate",
|
||||
data: {
|
||||
turbo_method: :post,
|
||||
turbo_frame: "modals"
|
||||
} %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
<div class="flex flex-col h-full divide-y divide-base-border text-white">
|
||||
<div class="flex flex-col flex-1 p-4 space-y-4 overflow-y-hidden">
|
||||
<div class="overflow-y-auto space-y-4">
|
||||
<div class="flex justify-between">
|
||||
<div class="text-2xl font-bold"><%= @controller.name %> 컨트롤러</div>
|
||||
<%= link_to "수정", schedule_edit_schedule_path(@controller.id), class: "btn bg-default-slate" %>
|
||||
</div>
|
||||
<table class="base-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>시간</th>
|
||||
<th>분</th>
|
||||
<th>사용여부</th>
|
||||
<th>온도</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @schedule.each do |s| %>
|
||||
<tr>
|
||||
<td><%= s.hour %>시</td>
|
||||
<td><%= s.minute %>분</td>
|
||||
<td><%= s.is_active %></td>
|
||||
<td><%= s.temperature %> °C</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -14,16 +14,20 @@ Rails.application.routes.draw do
|
|||
root "schedules#index"
|
||||
|
||||
resources :schedules do
|
||||
member do
|
||||
get "view"
|
||||
get "schedule_edit"
|
||||
post "reset"
|
||||
end
|
||||
collection do
|
||||
post "reset"
|
||||
get "schedule_edit"
|
||||
post "schedule_edit_update"
|
||||
end
|
||||
end
|
||||
|
||||
resources :modbus do
|
||||
collection do
|
||||
post "start"
|
||||
post "stop"
|
||||
end
|
||||
end
|
||||
|
||||
resources :modals do
|
||||
collection do
|
||||
post :open
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
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
|
||||
|
|
@ -9,6 +8,6 @@ class CreateSchedules < ActiveRecord::Migration[8.0]
|
|||
|
||||
t.timestamps
|
||||
end
|
||||
add_index :schedules, [ :controller_id, :hour, :minute ], unique: true
|
||||
add_index :schedules, [ :hour, :minute ], unique: true
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,24 +11,13 @@
|
|||
# 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 ["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"
|
||||
t.index ["hour", "minute"], name: "index_schedules_on_hour_and_minute", unique: true
|
||||
end
|
||||
|
||||
add_foreign_key "schedules", "controllers"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,10 +8,6 @@
|
|||
# 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!(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)
|
||||
Schedule.create!(hour: h, minute: 0, is_active: true, temperature: 15.0)
|
||||
end
|
||||
|
|
|
|||
34
modbus.rb
34
modbus.rb
|
|
@ -1,34 +0,0 @@
|
|||
require "rmodbus"
|
||||
require "ccutrer-serialport"
|
||||
require './sms/sms'
|
||||
|
||||
mode = ARGV[0]&.strip
|
||||
controller_id = ARGV[1]&.to_i
|
||||
value = ARGV[2]&.to_i
|
||||
|
||||
begin
|
||||
ModBus::RTUClient.new("/dev/ttyUSB0", 9600) do |cl|
|
||||
cl.with_slave(controller_id) do |slave|
|
||||
regs = slave.holding_registers
|
||||
case mode
|
||||
when "temp"
|
||||
regs[2] = value
|
||||
when "power"
|
||||
regs[22] = value
|
||||
else
|
||||
# type code here
|
||||
end
|
||||
sleep 0.1
|
||||
end
|
||||
end
|
||||
rescue
|
||||
error_message = "[#{Time.now}] #{mode} 실행 실패 (station_id: #{controller_id}, value: #{value})"
|
||||
res = Sms.send_one(
|
||||
{
|
||||
to: '01062619801',
|
||||
from: '01062619801',
|
||||
text: error_message
|
||||
}
|
||||
)
|
||||
puts res
|
||||
end
|
||||
|
|
@ -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[22] = value
|
||||
sleep 0.1
|
||||
end
|
||||
end
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"api_key": "NCSK1QGPTBWMUKBO",
|
||||
"api_secret": "RSGJ20CGUOTRKK33RFONFUFHY2SWMU9X",
|
||||
"protocol": "https",
|
||||
"domain": "api.coolsms.co.kr",
|
||||
"prefix": ""
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
require 'openssl'
|
||||
require 'base64'
|
||||
require 'net/http'
|
||||
require 'json'
|
||||
require 'securerandom'
|
||||
|
||||
module Request
|
||||
@file = File.read File.join(File.dirname(__FILE__), './config.json')
|
||||
@config = JSON.parse(@file)
|
||||
|
||||
def self.get_uri(path)
|
||||
str = @config['protocol'] + '://' + @config['domain'] + @config['prefix']
|
||||
puts str + path
|
||||
uri = URI(str + path)
|
||||
uri
|
||||
end
|
||||
|
||||
def self.header_get
|
||||
api_key = @config['api_key']
|
||||
api_secret = @config['api_secret']
|
||||
date = Time.now.strftime('%Y-%m-%dT%H:%M:%S.%L%z')
|
||||
salt = SecureRandom.hex
|
||||
signature = OpenSSL::HMAC.hexdigest('SHA256', api_secret, date + salt)
|
||||
'HMAC-SHA256 apiKey=' + api_key + ', date=' + date + ', salt=' + salt + ', signature=' + signature
|
||||
end
|
||||
|
||||
def self.post(path, body)
|
||||
header = header_get
|
||||
uri = get_uri(path)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = true
|
||||
req = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json')
|
||||
req.add_field('Authorization', header)
|
||||
if body
|
||||
req.body = body.to_json
|
||||
end
|
||||
res = http.request(req)
|
||||
JSON.parse(res.body)
|
||||
end
|
||||
|
||||
def self.put(path, body, headers = nil)
|
||||
auth = header_get
|
||||
uri = get_uri(path)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = true
|
||||
req = Net::HTTP::Put.new(uri.path, 'Content-Type' => 'application/json')
|
||||
req.add_field('Authorization', auth)
|
||||
if headers
|
||||
headers.each do |k, v|
|
||||
req.add_field(k, v)
|
||||
end
|
||||
end
|
||||
if body
|
||||
req.body = body.to_json
|
||||
end
|
||||
res = http.request(req)
|
||||
JSON.parse(res.body)
|
||||
end
|
||||
|
||||
def self.get(path)
|
||||
header = header_get
|
||||
uri = get_uri(path)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = true
|
||||
req = Net::HTTP::Get.new(uri.path, 'Content-Type' => 'application/json')
|
||||
req.add_field('Authorization', header)
|
||||
res = http.request(req)
|
||||
JSON.parse(res.body)
|
||||
end
|
||||
|
||||
def self.delete(path, body)
|
||||
header = header_get
|
||||
uri = get_uri(path)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = true
|
||||
req = Net::HTTP::Delete.new(uri.path, 'Content-Type' => 'application/json')
|
||||
req.add_field('Authorization', header)
|
||||
res = http.request(req)
|
||||
if body
|
||||
req.body = body.to_json
|
||||
end
|
||||
JSON.parse(res.body)
|
||||
end
|
||||
end
|
||||
11
sms/sms.rb
11
sms/sms.rb
|
|
@ -1,11 +0,0 @@
|
|||
require_relative "request"
|
||||
|
||||
module Sms
|
||||
def self.send_one(message)
|
||||
Request.post("/messages/v4/send", { message: message })
|
||||
end
|
||||
|
||||
def self.send_many(messages)
|
||||
Request.post("/messages/v4/send-many", { messages: messages })
|
||||
end
|
||||
end
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
# 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
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
require "test_helper"
|
||||
|
||||
class ControllerTest < ActiveSupport::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
end
|
||||
Loading…
Reference in New Issue