Compare commits
64 Commits
a479f5da7c
...
develop_mh
| Author | SHA1 | Date | |
|---|---|---|---|
| 883254e05f | |||
| f4d12c87eb | |||
| d3c6deaf96 | |||
| 15299b3de5 | |||
| 4a2c3714ba | |||
| 2d174d4fd0 | |||
| a117d417f4 | |||
| 32842d4b6e | |||
| 703a279ffa | |||
| 0d388b8c42 | |||
| ba25fada8e | |||
| ac06cd281f | |||
| a98aab4f45 | |||
| 36bea152fd | |||
| 90f89e08b8 | |||
| 7a99cd1dda | |||
| 9badb4dcd1 | |||
| 05f610bd86 | |||
| 91aefdba24 | |||
| fc61bc87f6 | |||
| 02932e8b79 | |||
| f7cedfd799 | |||
| 07c5d0fa2a | |||
| 69c98034d6 | |||
| 2f237acaec | |||
| 55715a20eb | |||
| 29841e2d15 | |||
| faaf8677e3 | |||
| ec259c981c | |||
| d4d30f1982 | |||
| 60138964cf | |||
| 9ce0efdb8c | |||
| 0047ed9587 | |||
| d9ad314b62 | |||
| 40f51173aa | |||
| e53449e001 | |||
| bdd562f0dd | |||
| cda965cb81 | |||
| 77c840064c | |||
| 31663fb9df | |||
| cbf28071b0 | |||
| 760561b10e | |||
| 2434c5b7aa | |||
| 9291a5d1db | |||
| ddb0d6a699 | |||
| a9b0fe80e4 | |||
| fbf93e80ae | |||
| 95a126953b | |||
| 30aa429c76 | |||
| 423460df66 | |||
| cb19bc990d | |||
| a958915d41 | |||
| 9ca6e10ac1 | |||
| def8d28c3b | |||
| cf3dae63f3 | |||
| f7131c827f | |||
| 32d0ac69c9 | |||
| eb3bc33509 | |||
| cd7620672f | |||
| 4a3d0313a2 | |||
| 8041453c5e | |||
| 6209b926e2 | |||
| 09068d9f70 | |||
| fed1b75fd2 |
12
.idea/dataSources.xml
generated
Normal file
12
.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
|
<data-source source="LOCAL" name="development.sqlite3" uuid="d3ab71a5-ebb0-4573-9e48-bc4bd013b015">
|
||||||
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/storage/development.sqlite3</jdbc-url>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
7
.idea/dictionaries/rubyon.xml
generated
Normal file
7
.idea/dictionaries/rubyon.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="rubyon">
|
||||||
|
<words>
|
||||||
|
<w>farmitry</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
||||||
3
.idea/farmitry_hvac.iml
generated
3
.idea/farmitry_hvac.iml
generated
@@ -53,6 +53,7 @@
|
|||||||
<orderEntry type="library" scope="PROVIDED" name="builder (v3.3.0, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="builder (v3.3.0, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="bundler (v2.6.6, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="bundler (v2.6.6, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="capybara (v3.40.0, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="capybara (v3.40.0, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
|
<orderEntry type="library" scope="PROVIDED" name="ccutrer-serialport (v1.1.0, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.3.5, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.3.5, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="connection_pool (v2.5.0, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="connection_pool (v2.5.0, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="crass (v1.0.6, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="crass (v1.0.6, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
@@ -66,6 +67,7 @@
|
|||||||
<orderEntry type="library" scope="PROVIDED" name="erb_lint (v0.9.0, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="erb_lint (v0.9.0, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="erubi (v1.13.1, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="erubi (v1.13.1, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="et-orbi (v1.2.11, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="et-orbi (v1.2.11, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
|
<orderEntry type="library" scope="PROVIDED" name="ffi (v1.17.2, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="foreman (v0.88.1, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="foreman (v0.88.1, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="fugit (v1.11.1, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="fugit (v1.11.1, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="globalid (v1.2.1, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="globalid (v1.2.1, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
@@ -129,6 +131,7 @@
|
|||||||
<orderEntry type="library" scope="PROVIDED" name="rubocop-rails-omakase (v1.1.0, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="rubocop-rails-omakase (v1.1.0, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.13.0, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.13.0, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="rubyzip (v2.4.1, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="rubyzip (v2.4.1, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
|
<orderEntry type="library" scope="PROVIDED" name="rufus-scheduler (v3.9.2, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="securerandom (v0.4.1, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="securerandom (v0.4.1, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="selenium-webdriver (v4.31.0, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="selenium-webdriver (v4.31.0, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="smart_properties (v1.17.0, rbenv: 3.4.2) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="smart_properties (v1.17.0, rbenv: 3.4.2) [gem]" level="application" />
|
||||||
|
|||||||
6
.idea/sqldialects.xml
generated
Normal file
6
.idea/sqldialects.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="SqlDialectMappings">
|
||||||
|
<file url="file://$PROJECT_DIR$/app/controllers/schedules_controller.rb" dialect="GenericSQL" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
2
Gemfile
2
Gemfile
@@ -69,4 +69,6 @@ group :test do
|
|||||||
gem "selenium-webdriver"
|
gem "selenium-webdriver"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
gem "ccutrer-serialport"
|
||||||
gem "rmodbus"
|
gem "rmodbus"
|
||||||
|
gem "rufus-scheduler"
|
||||||
|
|||||||
26
Gemfile.lock
26
Gemfile.lock
@@ -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,9 @@ 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)
|
||||||
|
ffi (1.17.2-arm64-darwin)
|
||||||
|
ffi (1.17.2-x86_64-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)
|
||||||
@@ -189,18 +194,10 @@ GEM
|
|||||||
nio4r (2.7.4)
|
nio4r (2.7.4)
|
||||||
nokogiri (1.18.7-aarch64-linux-gnu)
|
nokogiri (1.18.7-aarch64-linux-gnu)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.7-aarch64-linux-musl)
|
|
||||||
racc (~> 1.4)
|
|
||||||
nokogiri (1.18.7-arm-linux-gnu)
|
|
||||||
racc (~> 1.4)
|
|
||||||
nokogiri (1.18.7-arm-linux-musl)
|
|
||||||
racc (~> 1.4)
|
|
||||||
nokogiri (1.18.7-arm64-darwin)
|
nokogiri (1.18.7-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.7-x86_64-linux-gnu)
|
nokogiri (1.18.7-x86_64-linux-gnu)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.7-x86_64-linux-musl)
|
|
||||||
racc (~> 1.4)
|
|
||||||
ostruct (0.6.1)
|
ostruct (0.6.1)
|
||||||
parallel (1.27.0)
|
parallel (1.27.0)
|
||||||
parser (3.3.8.0)
|
parser (3.3.8.0)
|
||||||
@@ -300,6 +297,8 @@ GEM
|
|||||||
rubocop-rails (>= 2.30)
|
rubocop-rails (>= 2.30)
|
||||||
ruby-progressbar (1.13.0)
|
ruby-progressbar (1.13.0)
|
||||||
rubyzip (2.4.1)
|
rubyzip (2.4.1)
|
||||||
|
rufus-scheduler (3.9.2)
|
||||||
|
fugit (~> 1.1, >= 1.11.1)
|
||||||
securerandom (0.4.1)
|
securerandom (0.4.1)
|
||||||
selenium-webdriver (4.31.0)
|
selenium-webdriver (4.31.0)
|
||||||
base64 (~> 0.2)
|
base64 (~> 0.2)
|
||||||
@@ -325,12 +324,8 @@ GEM
|
|||||||
railties (>= 7.1)
|
railties (>= 7.1)
|
||||||
thor (~> 1.3.1)
|
thor (~> 1.3.1)
|
||||||
sqlite3 (2.6.0-aarch64-linux-gnu)
|
sqlite3 (2.6.0-aarch64-linux-gnu)
|
||||||
sqlite3 (2.6.0-aarch64-linux-musl)
|
|
||||||
sqlite3 (2.6.0-arm-linux-gnu)
|
|
||||||
sqlite3 (2.6.0-arm-linux-musl)
|
|
||||||
sqlite3 (2.6.0-arm64-darwin)
|
sqlite3 (2.6.0-arm64-darwin)
|
||||||
sqlite3 (2.6.0-x86_64-linux-gnu)
|
sqlite3 (2.6.0-x86_64-linux-gnu)
|
||||||
sqlite3 (2.6.0-x86_64-linux-musl)
|
|
||||||
sshkit (1.24.0)
|
sshkit (1.24.0)
|
||||||
base64
|
base64
|
||||||
logger
|
logger
|
||||||
@@ -374,19 +369,15 @@ GEM
|
|||||||
PLATFORMS
|
PLATFORMS
|
||||||
aarch64-linux
|
aarch64-linux
|
||||||
aarch64-linux-gnu
|
aarch64-linux-gnu
|
||||||
aarch64-linux-musl
|
|
||||||
arm-linux-gnu
|
|
||||||
arm-linux-musl
|
|
||||||
arm64-darwin-24
|
arm64-darwin-24
|
||||||
x86_64-linux
|
x86_64-linux
|
||||||
x86_64-linux-gnu
|
|
||||||
x86_64-linux-musl
|
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
amazing_print
|
amazing_print
|
||||||
bootsnap
|
bootsnap
|
||||||
brakeman
|
brakeman
|
||||||
capybara
|
capybara
|
||||||
|
ccutrer-serialport
|
||||||
cssbundling-rails
|
cssbundling-rails
|
||||||
debug
|
debug
|
||||||
erb_lint
|
erb_lint
|
||||||
@@ -399,6 +390,7 @@ DEPENDENCIES
|
|||||||
rails (~> 8.0.2)
|
rails (~> 8.0.2)
|
||||||
rmodbus
|
rmodbus
|
||||||
rubocop-rails-omakase
|
rubocop-rails-omakase
|
||||||
|
rufus-scheduler
|
||||||
selenium-webdriver
|
selenium-webdriver
|
||||||
solid_cable
|
solid_cable
|
||||||
solid_cache
|
solid_cache
|
||||||
|
|||||||
5
app/assets/images/svg/svg_pump.svg
Normal file
5
app/assets/images/svg/svg_pump.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="286" height="164" viewBox="0 0 286 164" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M259 153L179.015 153C197.205 142.983 211.133 126.18 217.385 106L259 106L259 94L286 94L286 164L259 164L259 153Z" fill="black"/>
|
||||||
|
<path d="M6.11959e-06 -1.21518e-05L27 -9.79135e-06L27 11L139 11L139 11.0078C108.644 11.4269 82.8939 30.8954 73.1602 58L27 58L27 70L0 70L6.11959e-06 -1.21518e-05Z" fill="black"/>
|
||||||
|
<path d="M140 11C179.212 11 211 42.7878 211 82C211 121.212 179.212 153 140 153C100.788 153 69 121.212 69 82C69 42.7878 100.788 11 140 11ZM155.737 102.695C151.368 106.023 145.916 108 140 108C134.331 108 129.087 106.185 124.814 103.105L102.157 116.188C111.49 126.512 124.987 133 140 133C155.245 133 168.925 126.308 178.271 115.705L155.737 102.695ZM131 31.792C107.126 36.0428 89 56.9045 89 82C89 88.3231 90.1514 94.3772 92.2549 99.9648L114.505 87.1191C114.174 85.464 114 83.7523 114 82C114 70.8045 121.076 61.2623 131 57.6006V31.792ZM150 57.9932C159.396 61.9114 166 71.1843 166 82C166 83.5208 165.868 85.0108 165.617 86.46L187.967 99.3633C189.929 93.9436 191 88.0969 191 82C191 57.2561 173.378 36.6283 150 31.9805V57.9932ZM140 72C134.477 72 130 76.4772 130 82C130 87.5228 134.477 92 140 92C145.523 92 150 87.5228 150 82C150 76.4772 145.523 72 140 72Z" fill="black"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
4
app/assets/images/svg/svg_valve.svg
Normal file
4
app/assets/images/svg/svg_valve.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 117.35" style="enable-background:new 0 0 122.88 117.35" xml:space="preserve">
|
||||||
|
<g><path fill="currentColor" d="M79.22,54.41c5.16,3.03,9.06,6.88,11.81,11.19h5.36v36.21h-5.73c-5.72,9.03-15.74,15.53-28.98,15.53 c-13.23,0-23.34-6.5-29.09-15.53h-5.96V65.6h5.91c2.98-4.48,7.25-8.48,12.96-11.58c3.04-1.64,4.88-3.69,4.9-5.53 c0.03-2.33-2.52-4.31-5.26-4.31h-1.36c-3.32,0-6.04-2.72-6.04-6.04l0,0c0-3.32,2.72-6.04,6.04-6.04h7.76l6.1-7.57 c-0.02-0.28-0.04-0.57-0.04-0.87v-9.99H32.87c-3.77,0-6.85-3.08-6.85-6.85l0,0c0-3.76,3.08-6.85,6.85-6.85h58.18 c3.76,0,6.85,3.08,6.85,6.85l0,0c0,3.77-3.08,6.85-6.85,6.85H67.51v9.99c0,0.41-0.03,0.82-0.07,1.21l5.48,7.23h6.67 c3.32,0,6.04,2.72,6.04,6.04l0,0c0,3.32-2.72,5.97-6.04,6.04c-4.57,0.1-6.25,2.06-6.04,4.34C73.91,52.73,76.83,53.01,79.22,54.41 L79.22,54.41z M101.34,65.6h4.2v-8.65c0-1.54,1.26-2.79,2.79-2.79h11.76c1.54,0,2.79,1.26,2.79,2.79v53.51 c0,1.54-1.26,2.79-2.79,2.79h-11.76c-1.54,0-2.79-1.26-2.79-2.79v-8.65h-4.2V65.6L101.34,65.6z M21.67,101.81h-4.33v8.65 c0,1.54-1.26,2.79-2.79,2.79H2.79C1.26,113.25,0,112,0,110.46V56.95c0-1.54,1.26-2.79,2.79-2.79h11.76c1.54,0,2.79,1.26,2.79,2.79 v8.65h4.33V101.81L21.67,101.81z"/></g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -9,7 +9,23 @@
|
|||||||
--color-warning: rgb(255, 202, 40);
|
--color-warning: rgb(255, 202, 40);
|
||||||
--color-accept: rgb(121, 134, 203);
|
--color-accept: rgb(121, 134, 203);
|
||||||
--color-danger: rgb(239, 83, 80);
|
--color-danger: rgb(239, 83, 80);
|
||||||
--color-table-border: rgb(203, 213, 225);
|
--color-table-border: rgb(181, 188, 199);
|
||||||
|
--color-table-background: rgb(226, 228, 234);
|
||||||
|
|
||||||
|
/* theme */
|
||||||
|
--color-logo-color: rgb(21, 128, 120);
|
||||||
|
--color-env-color: rgb(86, 145, 172);
|
||||||
|
--color-nut-color: rgb(39, 156, 26);
|
||||||
|
--color-etc-color: rgb(140, 152, 154);
|
||||||
|
--color-base-primary: rgb(44, 137, 255);
|
||||||
|
--color-base-background: rgb(242, 244, 248);
|
||||||
|
--color-base-sidebar: rgb(235, 238, 244);
|
||||||
|
--color-base-content: rgb(253, 253, 253);
|
||||||
|
--color-on-content: rgb(192, 216, 245);
|
||||||
|
--color-off-content: rgb(187, 187, 191);
|
||||||
|
--color-base-third: rgb(201, 208, 210);
|
||||||
|
--color-base-text: rgb(39, 41, 49);
|
||||||
|
--color-base-border: rgb(223, 226, 234);
|
||||||
|
|
||||||
--base-font-size: 0.85rem;
|
--base-font-size: 0.85rem;
|
||||||
--text-xs: calc(var(--base-font-size) * 0.75);
|
--text-xs: calc(var(--base-font-size) * 0.75);
|
||||||
@@ -42,11 +58,21 @@
|
|||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
body {
|
body {
|
||||||
@apply text-sm text-default-slate-dark
|
@apply bg-base-background text-base-text font-['Godo']
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
@apply border-table-border
|
@apply border-base-border
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-pattern {
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
rgba(255, 255, 255, 0.01) 0,
|
||||||
|
rgba(255, 255, 255, 0.01) 24px,
|
||||||
|
transparent 1px,
|
||||||
|
transparent 36px
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
a.disabled {
|
a.disabled {
|
||||||
@@ -58,7 +84,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
@apply justify-center rounded-md bg-default-slate px-4 py-2 text-sm font-semibold leading-6 text-white
|
@apply justify-center rounded-md bg-default-slate px-4 py-2 font-semibold leading-6 text-white
|
||||||
shadow-xs hover:opacity-80 cursor-pointer
|
shadow-xs hover:opacity-80 cursor-pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,41 +94,59 @@
|
|||||||
|
|
||||||
/*Form*/
|
/*Form*/
|
||||||
.label-style {
|
.label-style {
|
||||||
@apply block my-1 font-bold text-default-slate-dark
|
@apply block my-1 text-default-slate-dark
|
||||||
}
|
}
|
||||||
|
|
||||||
.select-style {
|
.select-style {
|
||||||
@apply block w-full h-8 px-3 py-1.5 border border-default-slate/50 rounded-xs
|
@apply block w-full px-2 py-1.5 border border-default-slate/50 rounded-xs appearance-none
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-style option {
|
||||||
|
color: theme('colors.base-background');
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-style {
|
.input-style {
|
||||||
@apply block w-full h-8 px-3 py-1.5 border border-default-slate/50 rounded-xs
|
@apply block w-full px-2 py-1.5 border border-default-slate/50 rounded-xs
|
||||||
}
|
}
|
||||||
|
|
||||||
.textarea-style {
|
.textarea-style {
|
||||||
@apply block w-full pl-4 flex-1 py-1.5 ring-1 ring-inset ring-default-slate/50 rounded-xs
|
@apply block w-full pl-4 flex-1 py-1.5 ring-1 ring-inset ring-default-slate/50 rounded-xs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*Toggle Button*/
|
||||||
|
.toggle_style {
|
||||||
|
@apply w-14 h-8 bg-danger rounded-full peer-checked:bg-notice transition-all duration-300
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-toggle-round {
|
||||||
|
@apply absolute left-1 top-1 w-6 h-6 bg-white rounded-full transition-all duration-300 peer-checked:translate-x-full shadow-sm
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
.header {
|
.header {
|
||||||
@apply p-4 text-lg font-bold h-16 flex justify-between items-center;
|
@apply p-4 text-lg h-16 flex justify-between items-center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-bar {
|
.side-bar {
|
||||||
@apply w-64 px-4 text-lg font-bold divide-y divide-default-slate-dark;
|
/*@apply w-[16rem] pl-4 text-lg divide-y divide-base-border;*/
|
||||||
|
@apply w-[16rem] p-4 text-lg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
@apply lg:rounded-lg bg-base-background
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-title {
|
.page-title {
|
||||||
@apply text-xl font-bold;
|
@apply text-xl;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-title {
|
.search-title {
|
||||||
@apply text-base font-bold;
|
@apply text-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-title {
|
.form-title {
|
||||||
@apply text-center text-3xl font-bold tracking-tight text-default-slate-dark
|
@apply text-center text-3xl tracking-tight text-default-slate-dark
|
||||||
}
|
}
|
||||||
|
|
||||||
.field {
|
.field {
|
||||||
@@ -113,13 +157,25 @@
|
|||||||
@apply inline-flex items-center rounded-xs px-2.5 py-1.5 text-sm font-semibold hover:opacity-75 disabled:cursor-not-allowed disabled:opacity-30 disabled:hover:bg-white
|
@apply inline-flex items-center rounded-xs px-2.5 py-1.5 text-sm font-semibold hover:opacity-75 disabled:cursor-not-allowed disabled:opacity-30 disabled:hover:bg-white
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Menu*/
|
.btn-toggle-text {
|
||||||
.menu-group {
|
@apply bg-logo-color rounded-lg text-white text-center
|
||||||
@apply flex flex-row py-6 px-4 gap-x-4 justify-between items-center w-full
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*Menu*/
|
||||||
|
.menu-group {
|
||||||
|
@apply flex flex-row py-1 px-4 my-2 gap-x-4 justify-between items-center w-full
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-group-active {
|
||||||
|
@apply bg-base-primary rounded-full text-base-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*.menu-group:hover {*/
|
||||||
|
/* @apply bg-base-primary/30 transition-colors rounded-full;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
.menu-group-icon {
|
.menu-group-icon {
|
||||||
@apply py-1 text-sm text-default-slate-dark font-medium justify-center
|
@apply flex py-1 justify-center items-center
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-group-name {
|
.menu-group-name {
|
||||||
@@ -127,13 +183,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-group-items-title {
|
.menu-group-items-title {
|
||||||
@apply text-base font-bold
|
@apply text-base
|
||||||
}
|
}
|
||||||
|
|
||||||
.copyright {
|
.copyright {
|
||||||
@apply text-default-slate-dark text-center
|
@apply text-default-slate-dark text-center
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*Dashboard*/
|
||||||
|
.dashboard-group {
|
||||||
|
@apply rounded-md min-h-32 p-4 flex flex-col justify-between cursor-pointer shadow-xs
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-title {
|
||||||
|
@apply text-2xl
|
||||||
|
}
|
||||||
|
|
||||||
/*Pagy*/
|
/*Pagy*/
|
||||||
.pagy {
|
.pagy {
|
||||||
@apply flex items-center justify-center space-x-1 font-semibold text-sm text-default-slate;
|
@apply flex items-center justify-center space-x-1 font-semibold text-sm text-default-slate;
|
||||||
@@ -169,24 +234,92 @@
|
|||||||
|
|
||||||
/*Base Table*/
|
/*Base Table*/
|
||||||
.base-table {
|
.base-table {
|
||||||
@apply min-w-full divide-y divide-table-border whitespace-nowrap border border-table-border
|
@apply min-w-full whitespace-nowrap
|
||||||
}
|
}
|
||||||
|
|
||||||
.base-table th {
|
.base-table th {
|
||||||
@apply bg-default px-2 py-2 text-left font-semibold text-default-slate-dark lg:table-cell
|
@apply p-3 text-left lg:table-cell
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-table tr {
|
||||||
|
@apply divide-x divide-base-border
|
||||||
}
|
}
|
||||||
|
|
||||||
.base-table td {
|
.base-table td {
|
||||||
@apply border-t border-table-border px-2 py-1 text-default-slate lg:table-cell whitespace-nowrap
|
@apply border-t border-base-border p-3 lg:table-cell whitespace-nowrap
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Base List*/
|
||||||
|
.base-list {
|
||||||
|
@apply overflow-y-auto divide-y divide-base-border lg:border lg:border-base-border lg:rounded-lg
|
||||||
|
}
|
||||||
|
.base-list ul {
|
||||||
|
@apply divide-y divide-base-border lg:rounded-b-lg
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-list li{
|
||||||
|
@apply flex justify-between items-center px-4 py-2 space-x-4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
.page-title {
|
.page-title {
|
||||||
@apply text-3xl font-bold;
|
@apply text-3xl;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-title {
|
.search-title {
|
||||||
@apply text-lg font-bold;
|
@apply text-lg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 기본 스크롤바 스타일 */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: rgba(21, 21, 21, 0.4);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: rgba(100, 100, 100, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-style::-webkit-outer-spin-button,
|
||||||
|
.input-style::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-style[type="number"] {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Fonts*/
|
||||||
|
@font-face {
|
||||||
|
font-family: "Chakra_Petch", sans-serif;
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Pretendard-Regular';
|
||||||
|
src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Regular.woff') format('woff');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Godo';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('https://fastly.jsdelivr.net/korean-webfonts/1/corps/godo/Godo/GodoM.woff2') format('woff2'), url('https://fastly.jsdelivr.net/korean-webfonts/1/corps/godo/Godo/GodoM.woff') format('woff');
|
||||||
|
}
|
||||||
@@ -1,4 +1,11 @@
|
|||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
|
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
|
||||||
allow_browser versions: :modern
|
allow_browser versions: :modern
|
||||||
|
|
||||||
|
before_action :set_controllers
|
||||||
|
|
||||||
|
private
|
||||||
|
def set_controllers
|
||||||
|
@controllers = Controller.all
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
106
app/controllers/environment_controller.rb
Normal file
106
app/controllers/environment_controller.rb
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
class EnvironmentController < ApplicationController
|
||||||
|
def index
|
||||||
|
@controllers = Controller.all
|
||||||
|
end
|
||||||
|
|
||||||
|
# def view_temp
|
||||||
|
# @controller = Controller.find(params[:id])
|
||||||
|
# @schedule = Schedule.where(controller_id: params[:id]).order(:hour, :minute)
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def view_humidity
|
||||||
|
# @controller = Controller.find(params[:id])
|
||||||
|
# @schedule = Schedule.where(controller_id: params[:id]).order(:hour, :minute)
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def view_co2
|
||||||
|
# @controller = Controller.find(params[:id])
|
||||||
|
# @schedule = Schedule.where(controller_id: params[:id]).order(:hour, :minute)
|
||||||
|
# end
|
||||||
|
|
||||||
|
def view_area
|
||||||
|
@controller = Controller.find(params[:id])
|
||||||
|
@schedule = Schedule.where(controller_id: params[:id]).order(:hour, :minute)
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit_humidity
|
||||||
|
@controller = Controller.find(params[:id])
|
||||||
|
@schedule = Schedule.where(controller_id: params[:id]).order(:hour, :minute)
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit_temp
|
||||||
|
@controller = Controller.find(params[:id])
|
||||||
|
@schedule = Schedule.where(controller_id: params[:id]).order(:hour, :minute)
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit_co2
|
||||||
|
@controller = Controller.find(params[:id])
|
||||||
|
@schedule = Schedule.where(controller_id: params[:id]).order(:hour, :minute)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@schedule = Schedule.new(schedule_params)
|
||||||
|
|
||||||
|
if @schedule.save
|
||||||
|
redirect_to schedule_edit_schedule_path(@schedule.controller_id), notice: "스케쥴이 추가 되었습니다."
|
||||||
|
else
|
||||||
|
error_messages = @schedule.errors.full_messages.join(", ")
|
||||||
|
redirect_to schedule_edit_schedule_path(@schedule.controller_id), alert: "스케쥴 추가 실패: #{error_messages}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@schedule = Schedule.find_by(id: params[:id])
|
||||||
|
|
||||||
|
if @schedule.destroy
|
||||||
|
redirect_to schedule_edit_schedule_path(@schedule.controller_id), notice: "스케줄이 삭제되었습니다."
|
||||||
|
else
|
||||||
|
error_messages = @schedule.errors.full_messages.join(", ")
|
||||||
|
redirect_to schedule_edit_schedule_path(@schedule.controller_id), alert: "스케쥴 삭제 실패: #{error_messages}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset
|
||||||
|
Schedule.where(controller_id: params[:id]).destroy_all
|
||||||
|
ActiveRecord::Base.connection.execute("DELETE FROM sqlite_sequence WHERE name='schedules'")
|
||||||
|
|
||||||
|
redirect_to schedule_edit_schedule_path(params[:id]), notice: "스케줄이 초기화되었습니다."
|
||||||
|
end
|
||||||
|
|
||||||
|
def schedule_edit
|
||||||
|
@controller = Controller.find(params[:id])
|
||||||
|
@schedule = Schedule.where(controller_id: params[:id]).order(:hour, :minute)
|
||||||
|
end
|
||||||
|
|
||||||
|
def schedule_edit_update
|
||||||
|
error_messages = []
|
||||||
|
|
||||||
|
params[:schedule]&.each do |id, attributes|
|
||||||
|
schedule = Schedule.find_by(id: id)
|
||||||
|
next unless schedule
|
||||||
|
|
||||||
|
unless schedule.update(
|
||||||
|
hour: attributes[:hour],
|
||||||
|
minute: attributes[:minute],
|
||||||
|
is_active: attributes[:is_active],
|
||||||
|
temperature: attributes[:temperature]
|
||||||
|
)
|
||||||
|
|
||||||
|
error_detail = schedule.errors.full_messages.join(", ")
|
||||||
|
time_label = "#{attributes[:hour]}시 #{attributes[:minute]}분"
|
||||||
|
error_messages << "#{time_label} - #{error_detail}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if error_messages.any?
|
||||||
|
redirect_to schedule_edit_schedule_path(params[:controller_id]), alert: error_messages.join("<br>")
|
||||||
|
else
|
||||||
|
redirect_to schedule_edit_schedule_path(params[:controller_id]), notice: "스케줄이 성공적으로 업데이트되었습니다."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def schedule_params
|
||||||
|
params.require(:schedule).permit(:controller_id, :hour, :minute, :is_active, :temperature)
|
||||||
|
end
|
||||||
|
end
|
||||||
6
app/controllers/modals_controller.rb
Normal file
6
app/controllers/modals_controller.rb
Normal file
@@ -0,0 +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] }
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,37 +1,2 @@
|
|||||||
class ModbusController < ApplicationController
|
class ModbusController < ApplicationController
|
||||||
def index
|
|
||||||
@schedule = Schedule.all
|
|
||||||
@modbus_running = Modbus::PollingService.running?
|
|
||||||
end
|
|
||||||
|
|
||||||
def start
|
|
||||||
Modbus::PollingService.start
|
|
||||||
redirect_to modbus_index_path
|
|
||||||
end
|
|
||||||
|
|
||||||
def stop
|
|
||||||
Modbus::PollingService.stop
|
|
||||||
redirect_to modbus_index_path
|
|
||||||
end
|
|
||||||
|
|
||||||
def schedule_edit
|
|
||||||
@schedule = Schedule.all
|
|
||||||
end
|
|
||||||
|
|
||||||
def schedule_edit_update
|
|
||||||
error_hours = []
|
|
||||||
|
|
||||||
params[:schedule].each do |id, attributes|
|
|
||||||
schedule = Schedule.find_by(id: id)
|
|
||||||
unless schedule.update(temperature: attributes[:temperature])
|
|
||||||
error_hours << "#{schedule.hour}시"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if error_hours.any?
|
|
||||||
redirect_to modbus_index_path, alert: "#{error_hours.join(', ')}의 온도 업데이트에 실패하였습니다."
|
|
||||||
else
|
|
||||||
redirect_to modbus_index_path, notice: "스케줄이 성공적으로 업데이트되었습니다."
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
2
app/controllers/nutrient_controller.rb
Normal file
2
app/controllers/nutrient_controller.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
class NutrientController < ApplicationController
|
||||||
|
end
|
||||||
2
app/controllers/rack_controller.rb
Normal file
2
app/controllers/rack_controller.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
class RackController < ApplicationController
|
||||||
|
end
|
||||||
80
app/controllers/schedules_controller.rb
Normal file
80
app/controllers/schedules_controller.rb
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
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)
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@schedule = Schedule.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@schedule = Schedule.new(schedule_params)
|
||||||
|
|
||||||
|
if @schedule.save
|
||||||
|
redirect_to schedule_edit_schedule_path(@schedule.controller_id), notice: "스케쥴이 추가 되었습니다."
|
||||||
|
else
|
||||||
|
error_messages = @schedule.errors.full_messages.join(", ")
|
||||||
|
redirect_to schedule_edit_schedule_path(@schedule.controller_id), alert: "스케쥴 추가 실패: #{error_messages}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@schedule = Schedule.find_by(id: params[:id])
|
||||||
|
|
||||||
|
if @schedule.destroy
|
||||||
|
redirect_to schedule_edit_schedule_path(@schedule.controller_id), notice: "스케줄이 삭제되었습니다."
|
||||||
|
else
|
||||||
|
error_messages = @schedule.errors.full_messages.join(", ")
|
||||||
|
redirect_to schedule_edit_schedule_path(@schedule.controller_id), alert: "스케쥴 삭제 실패: #{error_messages}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset
|
||||||
|
Schedule.where(controller_id: params[:id]).destroy_all
|
||||||
|
ActiveRecord::Base.connection.execute("DELETE FROM sqlite_sequence WHERE name='schedules'")
|
||||||
|
|
||||||
|
redirect_to schedule_edit_schedule_path(params[:id]), notice: "스케줄이 초기화되었습니다."
|
||||||
|
end
|
||||||
|
|
||||||
|
def schedule_edit
|
||||||
|
@controller = Controller.find(params[:id])
|
||||||
|
@schedule = Schedule.where(controller_id: params[:id]).order(:hour, :minute)
|
||||||
|
end
|
||||||
|
|
||||||
|
def schedule_edit_update
|
||||||
|
error_messages = []
|
||||||
|
|
||||||
|
params[:schedule]&.each do |id, attributes|
|
||||||
|
schedule = Schedule.find_by(id: id)
|
||||||
|
next unless schedule
|
||||||
|
|
||||||
|
unless schedule.update(
|
||||||
|
hour: attributes[:hour],
|
||||||
|
minute: attributes[:minute],
|
||||||
|
is_active: attributes[:is_active],
|
||||||
|
temperature: attributes[:temperature]
|
||||||
|
)
|
||||||
|
|
||||||
|
error_detail = schedule.errors.full_messages.join(", ")
|
||||||
|
time_label = "#{attributes[:hour]}시 #{attributes[:minute]}분"
|
||||||
|
error_messages << "#{time_label} - #{error_detail}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if error_messages.any?
|
||||||
|
redirect_to schedule_edit_schedule_path(params[:controller_id]), alert: error_messages.join("<br>")
|
||||||
|
else
|
||||||
|
redirect_to schedule_edit_schedule_path(params[:controller_id]), notice: "스케줄이 성공적으로 업데이트되었습니다."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def schedule_params
|
||||||
|
params.require(:schedule).permit(:controller_id, :hour, :minute, :is_active, :temperature)
|
||||||
|
end
|
||||||
|
end
|
||||||
2
app/helpers/environment_helper.rb
Normal file
2
app/helpers/environment_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module EnvironmentHelper
|
||||||
|
end
|
||||||
2
app/helpers/modals_helper.rb
Normal file
2
app/helpers/modals_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module ModalsHelper
|
||||||
|
end
|
||||||
2
app/helpers/nutrient_helper.rb
Normal file
2
app/helpers/nutrient_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module NutrientHelper
|
||||||
|
end
|
||||||
2
app/helpers/rack_helper.rb
Normal file
2
app/helpers/rack_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module RackHelper
|
||||||
|
end
|
||||||
2
app/helpers/schedules_helper.rb
Normal file
2
app/helpers/schedules_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module SchedulesHelper
|
||||||
|
end
|
||||||
@@ -7,5 +7,14 @@ import { application } from "./application"
|
|||||||
import HelloController from "./hello_controller"
|
import HelloController from "./hello_controller"
|
||||||
application.register("hello", HelloController)
|
application.register("hello", HelloController)
|
||||||
|
|
||||||
|
import ModalsController from "./modals_controller"
|
||||||
|
application.register("modals", ModalsController)
|
||||||
|
|
||||||
|
import SidebarController from "./sidebar_controller"
|
||||||
|
application.register("sidebar", SidebarController)
|
||||||
|
|
||||||
import TimerController from "./timer_controller"
|
import TimerController from "./timer_controller"
|
||||||
application.register("timer", TimerController)
|
application.register("timer", TimerController)
|
||||||
|
|
||||||
|
import ToggleController from "./toggle_controller"
|
||||||
|
application.register("toggle", ToggleController)
|
||||||
|
|||||||
53
app/javascript/controllers/modals_controller.js
Normal file
53
app/javascript/controllers/modals_controller.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
import { useClickOutside } from 'stimulus-use'
|
||||||
|
|
||||||
|
// Connects to data-controller="modals"
|
||||||
|
export default class extends Controller {
|
||||||
|
connect() {
|
||||||
|
// useClickOutside(this)
|
||||||
|
console.log("modal stimulus")
|
||||||
|
this.boundHandleKeydown = this.handleKeydown.bind(this)
|
||||||
|
document.addEventListener("keydown", this.boundHandleKeydown)
|
||||||
|
window.addEventListener('modals:close', this.close.bind(this))
|
||||||
|
|
||||||
|
const panelElement = document.getElementById("modals-panel")
|
||||||
|
const backdropElement = document.getElementById("modals-backdrop")
|
||||||
|
|
||||||
|
if (panelElement && backdropElement) {
|
||||||
|
setTimeout(() => {
|
||||||
|
panelElement.classList.remove('opacity-0', 'translate-y-4', 'sm:scale-95')
|
||||||
|
panelElement.classList.add('opacity-100', 'translate-y-0', 'sm:scale-100')
|
||||||
|
backdropElement.classList.remove('opacity-0')
|
||||||
|
backdropElement.classList.add('opacity-75')
|
||||||
|
}, 10) // 애니메이션이 바로 시작되도록 약간의 지연 추가
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
document.removeEventListener('keydown', this.boundHandleKeydown)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeydown(event) {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(event) {
|
||||||
|
const panelElement = document.getElementById("modals-panel")
|
||||||
|
const backdropElement = document.getElementById("modals-backdrop")
|
||||||
|
|
||||||
|
if (panelElement && backdropElement) {
|
||||||
|
panelElement.classList.remove("opacity-100", "translate-y-0", "sm:scale-100")
|
||||||
|
panelElement.classList.add("opacity-0", "translate-y-4", "sm:scale-95")
|
||||||
|
|
||||||
|
backdropElement.classList.remove("opacity-75")
|
||||||
|
backdropElement.classList.add("opacity-0")
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const modalsFrame = document.querySelector("turbo-frame#modals")
|
||||||
|
modalsFrame.innerHTML = ""
|
||||||
|
}, 300) // 닫힘 애니메이션 시간과 동일하게 300ms 지연 후 모달 제거
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
app/javascript/controllers/sidebar_controller.js
Normal file
15
app/javascript/controllers/sidebar_controller.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
static targets = ["menu", "overlay"]
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
this.menuTarget.classList.toggle("-translate-x-full")
|
||||||
|
this.overlayTarget.classList.toggle("hidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.menuTarget.classList.add("-translate-x-full")
|
||||||
|
this.overlayTarget.classList.add("hidden")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,8 +15,7 @@ export default class extends Controller {
|
|||||||
|
|
||||||
updateTime() {
|
updateTime() {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const formatted = `${now.getFullYear()}년 ${String(now.getMonth() + 1).padStart(2, '0')}월 ${String(now.getDate()).padStart(2, '0')}일 ` +
|
this.outputTarget.textContent = `${now.getFullYear()}년 ${String(now.getMonth() + 1).padStart(2, '0')}월 ${String(now.getDate()).padStart(2, '0')}일 ` +
|
||||||
`${String(now.getHours()).padStart(2, '0')}시 ${String(now.getMinutes()).padStart(2, '0')}분 ${String(now.getSeconds()).padStart(2, '0')}초`
|
`${String(now.getHours()).padStart(2, '0')}시 ${String(now.getMinutes()).padStart(2, '0')}분 ${String(now.getSeconds()).padStart(2, '0')}초`
|
||||||
this.outputTarget.textContent = formatted
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
app/javascript/controllers/toggle_controller.js
Normal file
40
app/javascript/controllers/toggle_controller.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// controllers/toggle_controller.js
|
||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
static targets = ["label", "dot", "background"]
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
this.state = true
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
this.state = !this.state
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
if (this.state) {
|
||||||
|
this.labelTarget.textContent = "ON"
|
||||||
|
this.labelTarget.classList.add("text-notice")
|
||||||
|
this.labelTarget.classList.remove("text-gray-400")
|
||||||
|
|
||||||
|
this.dotTarget.classList.add("bg-notice")
|
||||||
|
this.dotTarget.classList.remove("bg-gray-400")
|
||||||
|
|
||||||
|
// this.backgroundTarget.classList.add("bg-on-content")
|
||||||
|
// this.backgroundTarget.classList.remove("bg-off-content")
|
||||||
|
} else {
|
||||||
|
this.labelTarget.textContent = "OFF"
|
||||||
|
this.labelTarget.classList.add("text-gray-400")
|
||||||
|
this.labelTarget.classList.remove("text-notice")
|
||||||
|
|
||||||
|
this.dotTarget.classList.add("bg-gray-400")
|
||||||
|
this.dotTarget.classList.remove("bg-notice")
|
||||||
|
|
||||||
|
// this.backgroundTarget.classList.add("bg-off-content")
|
||||||
|
// this.backgroundTarget.classList.remove("bg-on-content")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
app/models/controller.rb
Normal file
3
app/models/controller.rb
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
class Controller < ApplicationRecord
|
||||||
|
has_many :schedules
|
||||||
|
end
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
class Schedule < ApplicationRecord
|
class Schedule < ApplicationRecord
|
||||||
|
belongs_to :controller
|
||||||
|
|
||||||
|
validates :hour, presence: { message: "시간을 입력하세요" }
|
||||||
|
validates :minute, presence: { message: "분을 입력하세요" }
|
||||||
validates :temperature,
|
validates :temperature,
|
||||||
presence: true,
|
presence: { message: "온도를 입력하세요" },
|
||||||
numericality: true,
|
numericality: { message: "온도는 숫자여야 합니다" },
|
||||||
format: { with: /\A\d+(\.\d)?\z/ }
|
format: { with: /\A\d+(\.\d)?\z/, message: "온도는 소수점 한 자리까지 입력 가능합니다" }
|
||||||
|
validates :minute, uniqueness: { scope: [ :controller_id, :hour ], message: "같은 시:분의 스케쥴이 이미 존재합니다" }
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -1,48 +1,59 @@
|
|||||||
|
# polling_service.rb
|
||||||
|
require "rufus-scheduler"
|
||||||
|
|
||||||
module Modbus
|
module Modbus
|
||||||
class PollingService
|
class PollingService
|
||||||
class << self
|
class << self
|
||||||
def start
|
def start
|
||||||
return if $modbus_polling_threads.any?(&:alive?)
|
return if @scheduler
|
||||||
|
|
||||||
thread = Thread.new do
|
puts "[#{Time.now}] Modbus polling service 시작됨 (Rufus)"
|
||||||
puts "[#{Time.current}] Modbus polling service 시작됨"
|
@scheduler ||= Rufus::Scheduler.new
|
||||||
last_logged_hour = nil
|
|
||||||
loop do
|
@scheduler.cron "0 * * * * *" do
|
||||||
begin
|
|
||||||
now = Time.now
|
now = Time.now
|
||||||
current_hour = now.hour
|
current_time = format("%02d:%02d", now.hour, now.min)
|
||||||
|
puts "# current time: #{current_time}.#{now.sec}"
|
||||||
if current_hour != last_logged_hour && now.min == 0
|
schedules = Schedule.where(hour: now.hour, minute: now.min)
|
||||||
schedule = Schedule.find_by(hour: current_hour)
|
schedules.each { |schedule| apply_schedule(schedule) }
|
||||||
|
|
||||||
Modbus::Service.new(
|
|
||||||
type: "write_int16",
|
|
||||||
start_address: 2,
|
|
||||||
end_address: 2,
|
|
||||||
value: schedule.temperature * 10
|
|
||||||
).execute
|
|
||||||
puts "[Schedule] #{current_hour}:00 -> Target temp: #{schedule.temperature}°C"
|
|
||||||
last_logged_hour = current_hour
|
|
||||||
end
|
end
|
||||||
rescue StandardError => e
|
end
|
||||||
error_message = "[#{Time.current}] 오류: #{e.message}"
|
|
||||||
|
private
|
||||||
|
def apply_schedule(schedule)
|
||||||
|
if schedule.is_active
|
||||||
|
run_script(
|
||||||
|
"power",
|
||||||
|
"#{schedule.controller.station_id}",
|
||||||
|
"0",
|
||||||
|
"[Schedule] #{schedule.controller.name} ON"
|
||||||
|
)
|
||||||
|
sleep 2
|
||||||
|
run_script(
|
||||||
|
"temp",
|
||||||
|
"#{schedule.controller.station_id}",
|
||||||
|
(schedule.temperature * 10).to_i.to_s,
|
||||||
|
"[Schedule] #{schedule.controller.name} #{format('%02d:%02d', schedule.hour, schedule.minute)} → #{schedule.temperature}°C"
|
||||||
|
)
|
||||||
|
else
|
||||||
|
run_script(
|
||||||
|
"power",
|
||||||
|
"#{schedule.controller.station_id}",
|
||||||
|
"1",
|
||||||
|
"[Schedule] #{schedule.controller.name} OFF"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_script(mode, controller_id, value, success_msg)
|
||||||
|
if system("ruby", "modbus.rb", mode, controller_id, value)
|
||||||
|
puts success_msg
|
||||||
|
Rails.logger.info success_msg
|
||||||
|
else
|
||||||
|
error_message = "[#{Time.now}] 실행 실패 (station_id: #{controller_id}, value: #{value})"
|
||||||
puts error_message
|
puts error_message
|
||||||
ensure
|
Rails.logger.error error_message
|
||||||
sleep 0.1
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
$modbus_polling_threads << thread
|
|
||||||
end
|
|
||||||
|
|
||||||
def stop
|
|
||||||
$modbus_polling_threads.each { |t| t.kill if t.alive? }
|
|
||||||
$modbus_polling_threads.clear
|
|
||||||
puts "[#{Time.now}] Modbus polling service 중지됨"
|
|
||||||
end
|
|
||||||
|
|
||||||
def running?
|
|
||||||
$modbus_polling_threads.any?(&:alive?)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -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
|
|
||||||
86
app/views/environment/edit_co2.html.erb
Normal file
86
app/views/environment/edit_co2.html.erb
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<div class="flex flex-col h-full lg:rounded-lg">
|
||||||
|
<div class="flex flex-row items-center space-x-4 p-2">
|
||||||
|
<%= link_to environment_index_path(id: params[:id]), class: "flex items-center" do %>
|
||||||
|
<i class="fa-solid fa-angle-left text-2xl"></i>
|
||||||
|
<% end %>
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= @controller.id %>동 CO</span><span class="text-sm">2</span> 스케줄러</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4 overflow-y-hidden divide-y divide-base-border bg-base-content rounded-lg p-4">
|
||||||
|
<%= form_with url: edit_temp_environment_path, method: :post, class: 'flex flex-col h-full' do %>
|
||||||
|
<%= hidden_field_tag :controller_id, params[:id] %>
|
||||||
|
<div class="flex flex-col divide-y divide-base-border overflow-y-hidden flex-1 space-y-4">
|
||||||
|
<%= submit_tag "업데이트", class: "btn bg-primary w-fit" %>
|
||||||
|
<div class="overflow-y-auto flex-1 min-h-0 border border-base-border rounded-lg">
|
||||||
|
<ul class="divide-y divide-base-border whitespace-nowrap relative">
|
||||||
|
<% if @schedule.present? %>
|
||||||
|
<% @schedule.each do |s| %>
|
||||||
|
<li class="flex justify-between items-center px-4 py-2 space-x-4 h-full relative">
|
||||||
|
<div class="flex items-center space-x-4 basis-3/5 h-full">
|
||||||
|
<!-- 시간 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">시간</div>
|
||||||
|
<div class="text-xl flex items-center space-x-1 w-full h-full">
|
||||||
|
<%= select_tag "schedule[#{s.id}][hour]",
|
||||||
|
options_for_select((0..23).map { |h| [h.to_s.rjust(2, '0'), h] }, s.hour),
|
||||||
|
class: "select-style min-w-[60px]" %>
|
||||||
|
<div>:</div>
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][minute]",
|
||||||
|
s.minute, min: 0, max: 59, step: 1, inputmode: "decimal",
|
||||||
|
class: "input-style min-w-[60px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 온도 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">온도 (°C)</div>
|
||||||
|
<div class="text-xl flex items-center">
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][temperature]", s.temperature, min: -99, max: 99, step: "0.1", inputmode: "decimal", class: "input-style min-w-[68px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center space-x-4 basis-2/5 justify-end h-full">
|
||||||
|
<!-- 사용 여부 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full">
|
||||||
|
<div class="">ON/OFF</div>
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer h-full">
|
||||||
|
<%= check_box_tag "schedule[#{s.id}][is_active]", "1", s.is_active == true || s.is_active == 1, class: "sr-only peer" %>
|
||||||
|
<div class="toggle_style"></div>
|
||||||
|
<div class="btn-toggle-round"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 삭제 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full relative">
|
||||||
|
<div class=""> </div>
|
||||||
|
<%= link_to schedule_path(s),
|
||||||
|
data: { turbo_method: :delete, turbo_confirm: "정말 삭제하시겠습니까?" },
|
||||||
|
class: "btn bg-danger text-sm flex items-center px-2 py-0 my-1.5 lg:px-4 lg:py-1.5 lg:my-0" do %>
|
||||||
|
<div class="hidden lg:block">삭제</div>
|
||||||
|
<div class="block lg:hidden"><i class="fa-solid fa-xmark"></i></div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<li class="text-center p-4 w-full">저장된 스케줄이 없습니다.</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="flex 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>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
86
app/views/environment/edit_humidity.html.erb
Normal file
86
app/views/environment/edit_humidity.html.erb
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<div class="flex flex-col h-full lg:rounded-lg">
|
||||||
|
<div class="flex flex-row items-center space-x-4 p-2">
|
||||||
|
<%= link_to environment_index_path(id: params[:id]), class: "flex items-center" do %>
|
||||||
|
<i class="fa-solid fa-angle-left text-2xl"></i>
|
||||||
|
<% end %>
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= @controller.id %>동 습도 스케줄러</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4 overflow-y-hidden divide-y divide-base-border bg-base-content rounded-lg p-4">
|
||||||
|
<%= form_with url: edit_temp_environment_path, method: :post, class: 'flex flex-col h-full' do %>
|
||||||
|
<%= hidden_field_tag :controller_id, params[:id] %>
|
||||||
|
<div class="flex flex-col divide-y divide-base-border overflow-y-hidden flex-1 space-y-4">
|
||||||
|
<%= submit_tag "업데이트", class: "btn bg-primary w-fit" %>
|
||||||
|
<div class="overflow-y-auto flex-1 min-h-0 border border-base-border rounded-lg">
|
||||||
|
<ul class="divide-y divide-base-border whitespace-nowrap relative">
|
||||||
|
<% if @schedule.present? %>
|
||||||
|
<% @schedule.each do |s| %>
|
||||||
|
<li class="flex justify-between items-center px-4 py-2 space-x-4 h-full relative">
|
||||||
|
<div class="flex items-center space-x-4 basis-3/5 h-full">
|
||||||
|
<!-- 시간 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">시간</div>
|
||||||
|
<div class="text-xl flex items-center space-x-1 w-full h-full">
|
||||||
|
<%= select_tag "schedule[#{s.id}][hour]",
|
||||||
|
options_for_select((0..23).map { |h| [h.to_s.rjust(2, '0'), h] }, s.hour),
|
||||||
|
class: "select-style min-w-[60px]" %>
|
||||||
|
<div>:</div>
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][minute]",
|
||||||
|
s.minute, min: 0, max: 59, step: 1, inputmode: "decimal",
|
||||||
|
class: "input-style min-w-[60px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 온도 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">온도 (°C)</div>
|
||||||
|
<div class="text-xl flex items-center">
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][temperature]", s.temperature, min: -99, max: 99, step: "0.1", inputmode: "decimal", class: "input-style min-w-[68px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center space-x-4 basis-2/5 justify-end h-full">
|
||||||
|
<!-- 사용 여부 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full">
|
||||||
|
<div class="">ON/OFF</div>
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer h-full">
|
||||||
|
<%= check_box_tag "schedule[#{s.id}][is_active]", "1", s.is_active == true || s.is_active == 1, class: "sr-only peer" %>
|
||||||
|
<div class="toggle_style"></div>
|
||||||
|
<div class="btn-toggle-round"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 삭제 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full relative">
|
||||||
|
<div class=""> </div>
|
||||||
|
<%= link_to schedule_path(s),
|
||||||
|
data: { turbo_method: :delete, turbo_confirm: "정말 삭제하시겠습니까?" },
|
||||||
|
class: "btn bg-danger text-sm flex items-center px-2 py-0 my-1.5 lg:px-4 lg:py-1.5 lg:my-0" do %>
|
||||||
|
<div class="hidden lg:block">삭제</div>
|
||||||
|
<div class="block lg:hidden"><i class="fa-solid fa-xmark"></i></div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<li class="text-center p-4 w-full">저장된 스케줄이 없습니다.</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="flex 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>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
104
app/views/environment/edit_temp.html.erb
Normal file
104
app/views/environment/edit_temp.html.erb
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<div class="flex flex-col h-full lg:rounded-lg">
|
||||||
|
<div class="flex flex-row items-center space-x-4 p-2">
|
||||||
|
<%= link_to environment_index_path(id: params[:id]), class: "flex items-center" do %>
|
||||||
|
<i class="fa-solid fa-angle-left text-2xl"></i>
|
||||||
|
<% end %>
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= @controller.id %>동 환경 스케줄러</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4 overflow-y-hidden divide-y divide-base-border">
|
||||||
|
<%= form_with url: edit_temp_environment_path, method: :post, class: 'flex flex-col h-full' do %>
|
||||||
|
<div class="flex flex-col h-full">
|
||||||
|
<div class="flex flex-col overflow-y-hidden flex-1 space-y-4">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<%= submit_tag "업데이트", class: "btn bg-primary w-fit" %>
|
||||||
|
<div class="flex space-x-4">
|
||||||
|
<%= link_to "추가하기", open_modals_path(type: "add_feeder_schedule"),
|
||||||
|
class: "btn bg-default-slate",
|
||||||
|
data: {
|
||||||
|
turbo_method: :post,
|
||||||
|
turbo_frame: "modals"
|
||||||
|
} %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-y-auto flex-1 min-h-0 border border-base-border rounded-lg bg-base-content">
|
||||||
|
<ul class="divide-y divide-base-border whitespace-nowrap relative">
|
||||||
|
<!-- 헤더 -->
|
||||||
|
<li class="flex justify-between items-center px-4 py-2 text-md font-semibold text-gray-600 bg-base-content rounded-t-md">
|
||||||
|
<div class="flex basis-3/5 space-x-8">
|
||||||
|
<div class="w-[40px]">번호</div>
|
||||||
|
<div class="w-[120px]">시작 시간 (시/분)</div>
|
||||||
|
<div class="w-[120px]">온도 (°C)</div>
|
||||||
|
<div class="w-[120px]">습도 (%)</div>
|
||||||
|
<div class="w-[120px]">CO<span class="text-xs">2</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex basis-2/5 items-center justify-end space-x-8">
|
||||||
|
<div class="w-[100px] text-center">사용 여부</div>
|
||||||
|
<div class="w-[80px] text-center">작업</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="flex justify-between items-center px-4 py-2 space-x-4 h-full border-b border-base-border relative bg-base-content">
|
||||||
|
<div class="flex items-center basis-3/5 space-x-8 h-full">
|
||||||
|
<!-- 번호 -->
|
||||||
|
<div class="w-[40px]">1</div>
|
||||||
|
|
||||||
|
<!-- 시작 시각 -->
|
||||||
|
<div class="w-[120px] flex space-x-2 items-center">
|
||||||
|
<select class="select-style w-1/2">
|
||||||
|
<option selected>08</option>
|
||||||
|
<option>09</option>
|
||||||
|
<option>10</option>
|
||||||
|
</select>
|
||||||
|
<div> : </div>
|
||||||
|
<select class="select-style w-1/2">
|
||||||
|
<option selected>30</option>
|
||||||
|
<option>45</option>
|
||||||
|
<option>00</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 온도 -->
|
||||||
|
<div class="w-[120px] flex space-x-2 items-center">
|
||||||
|
<input type="number" value="15" min="0" max="60" class="input-style w-full" />
|
||||||
|
<div>~</div>
|
||||||
|
<input type="number" value="20" min="0" max="60" class="input-style w-full" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 습도 -->
|
||||||
|
<div class="w-[120px] flex space-x-2 items-center">
|
||||||
|
<input type="number" value="78" class="input-style" />
|
||||||
|
<div>~</div>
|
||||||
|
<input type="number" value="80" class="input-style" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- CO2 -->
|
||||||
|
<div class="w-[130px] flex space-x-2 items-center">
|
||||||
|
<input type="number" value="300" class="input-style" />
|
||||||
|
<div>~</div>
|
||||||
|
<input type="number" value="1000" class="input-style" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex basis-2/5 justify-end items-center space-x-8 h-full">
|
||||||
|
<!-- 토글 -->
|
||||||
|
<div class="w-[100px] flex justify-center">
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer">
|
||||||
|
<input type="checkbox" checked class="sr-only peer" />
|
||||||
|
<div class="toggle_style"></div>
|
||||||
|
<div class="btn-toggle-round"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 삭제 버튼 -->
|
||||||
|
<div class="w-[80px] flex justify-center">
|
||||||
|
<button class="btn bg-danger text-md px-2 py-1">삭제</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
482
app/views/environment/index.html.erb
Normal file
482
app/views/environment/index.html.erb
Normal file
@@ -0,0 +1,482 @@
|
|||||||
|
<div class="w-full h-full content overflow-y-auto space-y-4">
|
||||||
|
<div class="flex-1 grid grid-cols-2 xl:grid-cols-3 gap-4">
|
||||||
|
<!-- 실내온도 -->
|
||||||
|
<%= link_to edit_temp_environment_path(id: params[:id]), class: "dashboard-group bg-base-content" do %>
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-temperature-quarter text-env-color text-4xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">온도</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">현재 온도</div>
|
||||||
|
<div class="text-3xl">18.5<span class="font-normal text-2xl"> °C</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">설정 온도</div>
|
||||||
|
<div class="text-3xl">18<span class="font-normal text-2xl"> °C</span> ~ 20<span class="font-normal text-2xl"> °C</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<!-- 실내습도 -->
|
||||||
|
<%= link_to edit_humidity_environment_path(id: params[:id]), class: "dashboard-group bg-base-content" do %>
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-droplet text-env-color text-4xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">습도</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">현재 습도</div>
|
||||||
|
<div class="text-3xl">78<span class="font-normal text-2xl"> %</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">설정 습도</div>
|
||||||
|
<div class="text-3xl">75<span class="font-normal text-2xl"> %</span> ~ 80<span class="font-normal text-2xl"> %</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<!-- CO2 -->
|
||||||
|
<%= link_to edit_co2_environment_path(id: params[:id]), class: "dashboard-group bg-base-content" do %>
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<div class="size-10 bg-env-color rounded-full text-base-background flex flex-col justify-center">
|
||||||
|
<div class="flex flex-row justify-center items-baseline">
|
||||||
|
<span class="text-xl">CO</span><span class="text-sm">2</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">CO</span><span class="text-xl">2</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">현재 CO</span><span class="text-sm">2</div>
|
||||||
|
<div class="text-3xl">700<span class="font-normal text-2xl"> ppm</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">최소 CO</span><span class="text-sm">2</div>
|
||||||
|
<div class="text-3xl">300<span class="font-normal text-2xl"> ppm</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full pl-4">
|
||||||
|
<div class="opacity-50">최대 CO</span><span class="text-sm">2</div>
|
||||||
|
<div class="text-3xl">1000<span class="font-normal text-2xl"> ppm</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1 grid grid-cols-2 xl:grid-cols-6 gap-4 min-h-32">
|
||||||
|
|
||||||
|
<!-- 스케줄러 동작 -->
|
||||||
|
<div class="dashboard-group cursor-pointer w-full h-full bg-base-content">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div class="dashboard-title" data-modbus-target="statusTitle">스케줄러 동작</div>
|
||||||
|
<div class="flex flex-row justify-end items-baseline w-full">
|
||||||
|
<div class="flex size-12 items-center justify-center">
|
||||||
|
<i class="fa-solid fa-play text-etc-color text-5xl text-left"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 긴급 정지 -->
|
||||||
|
<div class="dashboard-group cursor-pointer w-full h-full bg-base-content">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div class="dashboard-title">긴급 정지</div>
|
||||||
|
<div class="flex flex-row justify-end items-baseline w-full">
|
||||||
|
<div class="flex size-12 items-center justify-center">
|
||||||
|
<i class="fa-solid fa-stop text-etc-color text-5xl text-left"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- LED 스케줄러 설정 -->
|
||||||
|
<div class="dashboard-group cursor-pointer w-full h-full bg-base-content">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div class="dashboard-title">LED 스케줄러 설정</div>
|
||||||
|
<div class="flex flex-row justify-end items-baseline w-full">
|
||||||
|
<div class="flex size-12 items-center justify-center">
|
||||||
|
<i class="fa-solid fa-calendar-days text-etc-color text-5xl text-left"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 재배용 FAN 스케줄러 설정 -->
|
||||||
|
<div class="dashboard-group cursor-pointer w-full h-full bg-base-content">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div class="dashboard-title">재배용 FAN 스케줄러 설정</div>
|
||||||
|
<div class="flex flex-row justify-end items-baseline w-full">
|
||||||
|
<div class="flex size-12 items-center justify-center">
|
||||||
|
<i class="fa-solid fa-calendar-days text-etc-color text-5xl text-left"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 환기용 FAN 스케줄러 설정 -->
|
||||||
|
<div class="dashboard-group cursor-pointer w-full h-full bg-base-content">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div class="dashboard-title">환기용 FAN 스케줄러 설정</div>
|
||||||
|
<div class="flex flex-row justify-end items-baseline w-full">
|
||||||
|
<div class="flex size-12 items-center justify-center">
|
||||||
|
<i class="fa-solid fa-calendar-days text-etc-color text-5xl text-left"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 환경 스케줄러 설정 -->
|
||||||
|
<div class="dashboard-group cursor-pointer w-full h-full bg-base-content">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div class="dashboard-title">환경 스케줄러 설정</div>
|
||||||
|
<div class="flex flex-row justify-end items-baseline w-full">
|
||||||
|
<div class="flex size-12 items-center justify-center">
|
||||||
|
<i class="fa-solid fa-calendar-days text-etc-color text-5xl text-left"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1 grid grid-cols-2 xl:grid-cols-6 gap-4">
|
||||||
|
<!-- 1번 랙 -->
|
||||||
|
<div class="dashboard-group bg-base-content">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-timeline text-etc-color text-4xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">1번 랙</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">LED</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full ml-4">
|
||||||
|
<div class="opacity-50">FAN</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 2번 랙 -->
|
||||||
|
<div class="dashboard-group bg-base-content">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-timeline text-etc-color text-4xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">2번 랙</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">LED</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full ml-4">
|
||||||
|
<div class="opacity-50">FAN</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 3번 랙 -->
|
||||||
|
<div class="dashboard-group bg-base-content">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-timeline text-etc-color text-4xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">3번 랙</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">LED</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full ml-4">
|
||||||
|
<div class="opacity-50">FAN</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 4번 랙 -->
|
||||||
|
<div class="dashboard-group bg-base-content">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-timeline text-etc-color text-4xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">4번 랙</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">LED</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full ml-4">
|
||||||
|
<div class="opacity-50">FAN</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 5번 랙 -->
|
||||||
|
<div class="dashboard-group bg-base-content">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-timeline text-etc-color text-4xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">5번 랙</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">LED</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full ml-4">
|
||||||
|
<div class="opacity-50">FAN</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 벌통 Spot Light -->
|
||||||
|
<div class="dashboard-group bg-base-content">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-timeline text-etc-color text-4xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">벌통 Spot Light</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">상태</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1 grid grid-cols-1 gap-4">
|
||||||
|
<!-- 환기용 FAN -->
|
||||||
|
<div class="dashboard-group bg-base-content">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-fan text-etc-color text-4xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">환기용 FAN</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex-1 grid grid-cols-2 xl:grid-cols-8 gap-2">
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">1번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">2번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">3번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">4번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">5번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">6번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">7번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">8번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">9번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">10번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">11번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">12번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">13번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">14번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">15번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card px-3 py-1.5 rounded-lg cursor-pointer">
|
||||||
|
<div class="opacity-50">16번</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2 text-notice">
|
||||||
|
<div class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="text-3xl">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
79
app/views/environment/view_area.html.erb
Normal file
79
app/views/environment/view_area.html.erb
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<div class="flex flex-col h-full divide-y divide-base-border lg:rounded-lg">
|
||||||
|
<div class="flex flex-col flex-1 space-y-4 overflow-y-hidden">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<div class="flex flex-row items-center space-x-4">
|
||||||
|
<%= link_to environment_index_path(id: params[:id]), class: "flex items-center" do %>
|
||||||
|
<i class="fa-solid fa-angle-left text-3xl"></i>
|
||||||
|
<% end %>
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= @controller.id %>동 센서구역 정보</div>
|
||||||
|
</div>
|
||||||
|
<%= link_to "수정", schedule_edit_schedule_path(@controller.id), class: "btn bg-default-slate" %>
|
||||||
|
</div>
|
||||||
|
<div class="base-list">
|
||||||
|
<ul>
|
||||||
|
<% if @schedule.present? %>
|
||||||
|
<% @schedule.each do |s| %>
|
||||||
|
<li>
|
||||||
|
<div class="flex items-center space-x-6">
|
||||||
|
<!-- 시간 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-2">
|
||||||
|
<div>시간</div>
|
||||||
|
<div class="text-3xl flex items-baseline space-x-1">
|
||||||
|
<div class="text-center w-[36px]"><%= s.hour.to_s.rjust(2, '0') %></div>
|
||||||
|
<div>:</div>
|
||||||
|
<div class="text-center w-[36px]"><%= s.minute.to_s.rjust(2, '0') %></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 온도 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-4">
|
||||||
|
<div>온도</div>
|
||||||
|
<div class="text-3xl flex items-end space-x-2">
|
||||||
|
<div class="text-left w-[50px]"><%= s.temperature %></div>
|
||||||
|
<div class="text-base leading-7">°C</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 사용 여부 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-0 lg:space-x-4 items-end">
|
||||||
|
<div class="mb-1">ON/OFF</div>
|
||||||
|
<div class="text-3xl w-8 mr-0">
|
||||||
|
<%#= s.is_active %>
|
||||||
|
<% if s.is_active %>
|
||||||
|
<i class="fa-solid fa-check text-notice"></i>
|
||||||
|
<% else %>
|
||||||
|
<i class="fa-solid fa-xmark text-danger"></i>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<li class="text-center p-4 w-full">저장된 스케줄이 없습니다.</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- <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>
|
||||||
58
app/views/environment/view_co2.html.erb
Normal file
58
app/views/environment/view_co2.html.erb
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<div class="flex flex-col h-full divide-y divide-base-border lg:rounded-lg">
|
||||||
|
<div class="flex flex-col flex-1 space-y-4 overflow-y-hidden">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<div class="flex flex-row items-center space-x-4">
|
||||||
|
<%= link_to environment_index_path(id: params[:id]), class: "flex items-center" do %>
|
||||||
|
<i class="fa-solid fa-angle-left text-3xl"></i>
|
||||||
|
<% end %>
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= @controller.id %>동 CO2 스케줄러</div>
|
||||||
|
</div>
|
||||||
|
<%= link_to "수정", schedule_edit_schedule_path(@controller.id), class: "btn bg-default-slate" %>
|
||||||
|
</div>
|
||||||
|
<div class="base-list">
|
||||||
|
<ul>
|
||||||
|
<% if @schedule.present? %>
|
||||||
|
<% @schedule.each do |s| %>
|
||||||
|
<li>
|
||||||
|
<div class="flex items-center space-x-6">
|
||||||
|
<!-- 시간 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-2">
|
||||||
|
<div>시간</div>
|
||||||
|
<div class="text-3xl flex items-baseline space-x-1">
|
||||||
|
<div class="text-center w-[36px]"><%= s.hour.to_s.rjust(2, '0') %></div>
|
||||||
|
<div>:</div>
|
||||||
|
<div class="text-center w-[36px]"><%= s.minute.to_s.rjust(2, '0') %></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 농도 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-4">
|
||||||
|
<div>농도</div>
|
||||||
|
<div class="text-3xl flex items-end space-x-2">
|
||||||
|
<div class="text-left w-[50px]"><%= s.temperature %></div>
|
||||||
|
<div class="text-base leading-7">%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 사용 여부 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-0 lg:space-x-4 items-end">
|
||||||
|
<div class="mb-1">ON/OFF</div>
|
||||||
|
<div class="text-3xl w-8 mr-0">
|
||||||
|
<%#= s.is_active %>
|
||||||
|
<% if s.is_active %>
|
||||||
|
<i class="fa-solid fa-check text-notice"></i>
|
||||||
|
<% else %>
|
||||||
|
<i class="fa-solid fa-xmark text-danger"></i>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<li class="text-center p-4 w-full">저장된 스케줄이 없습니다.</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
58
app/views/environment/view_humidity.html.erb
Normal file
58
app/views/environment/view_humidity.html.erb
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<div class="flex flex-col h-full divide-y divide-base-border lg:rounded-lg">
|
||||||
|
<div class="flex flex-col flex-1 space-y-4 overflow-y-hidden">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<div class="flex flex-row items-center space-x-4">
|
||||||
|
<%= link_to environment_index_path(id: params[:id]), class: "flex items-center" do %>
|
||||||
|
<i class="fa-solid fa-angle-left text-3xl"></i>
|
||||||
|
<% end %>
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= @controller.id %>동 습도 스케줄러</div>
|
||||||
|
</div>
|
||||||
|
<%= link_to "수정", schedule_edit_schedule_path(@controller.id), class: "btn bg-default-slate" %>
|
||||||
|
</div>
|
||||||
|
<div class="base-list">
|
||||||
|
<ul>
|
||||||
|
<% if @schedule.present? %>
|
||||||
|
<% @schedule.each do |s| %>
|
||||||
|
<li>
|
||||||
|
<div class="flex items-center space-x-6">
|
||||||
|
<!-- 시간 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-2">
|
||||||
|
<div>시간</div>
|
||||||
|
<div class="text-3xl flex items-baseline space-x-1">
|
||||||
|
<div class="text-center w-[36px]"><%= s.hour.to_s.rjust(2, '0') %></div>
|
||||||
|
<div>:</div>
|
||||||
|
<div class="text-center w-[36px]"><%= s.minute.to_s.rjust(2, '0') %></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 습도 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-4">
|
||||||
|
<div>습도</div>
|
||||||
|
<div class="text-3xl flex items-end space-x-2">
|
||||||
|
<div class="text-left w-[50px]"><%= s.temperature %></div>
|
||||||
|
<div class="text-base leading-7">%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 사용 여부 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-0 lg:space-x-4 items-end">
|
||||||
|
<div class="mb-1">ON/OFF</div>
|
||||||
|
<div class="text-3xl w-8 mr-0">
|
||||||
|
<%#= s.is_active %>
|
||||||
|
<% if s.is_active %>
|
||||||
|
<i class="fa-solid fa-check text-notice"></i>
|
||||||
|
<% else %>
|
||||||
|
<i class="fa-solid fa-xmark text-danger"></i>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<li class="text-center p-4 w-full">저장된 스케줄이 없습니다.</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
64
app/views/environment/view_temp.html.erb
Normal file
64
app/views/environment/view_temp.html.erb
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<div class="flex flex-col h-full lg:rounded-lg">
|
||||||
|
<div class="flex flex-row items-center space-x-4 p-2">
|
||||||
|
<%= link_to environment_index_path(id: params[:id]), class: "flex items-center" do %>
|
||||||
|
<i class="fa-solid fa-angle-left text-2xl"></i>
|
||||||
|
<% end %>
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= @controller.id %>동 온도 스케줄러</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col flex-1 space-y-4 overflow-y-hidden lg:p-4 bg-base-content rounded-lg">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<%= link_to "수정", edit_temp_environment_path(@controller.id), class: "btn bg-default-slate" %>
|
||||||
|
</div>
|
||||||
|
<div class="lg:rounded-lg border border-base-border overflow-y-hidden">
|
||||||
|
<table class="base-table border-b border-base-border table-fixed w-full">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-1/3">시간</th>
|
||||||
|
<th class="w-1/3">온도</th>
|
||||||
|
<th>사용여부</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- tbody 감싸는 div -->
|
||||||
|
<div class="overflow-y-auto max-h-full overflow-x-hidden">
|
||||||
|
<table class="base-table table-fixed w-full">
|
||||||
|
<tbody>
|
||||||
|
<% if @schedule.present? %>
|
||||||
|
<% @schedule.each do |s| %>
|
||||||
|
<tr>
|
||||||
|
<td class="w-1/3">
|
||||||
|
<div class="text-2xl flex items-baseline space-x-1">
|
||||||
|
<div class="text-center w-[36px]"><%= s.hour.to_s.rjust(2, '0') %></div>
|
||||||
|
<div>:</div>
|
||||||
|
<div class="text-center w-[36px]"><%= s.minute.to_s.rjust(2, '0') %></div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="w-1/3">
|
||||||
|
<div class="text-2xl flex items-end space-x-2">
|
||||||
|
<div class="text-left w-[50px]"><%= s.temperature %></div>
|
||||||
|
<div class="text-base leading-7">°C</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="text-2xl w-8 mr-0">
|
||||||
|
<% if s.is_active %>
|
||||||
|
<i class="fa-solid fa-check text-notice"></i>
|
||||||
|
<% else %>
|
||||||
|
<i class="fa-solid fa-xmark text-danger"></i>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" class="text-center p-4 w-full">저장된 스케줄이 없습니다.</td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -21,17 +21,31 @@
|
|||||||
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
|
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
|
||||||
<%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
|
<%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
|
||||||
<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">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Chakra+Petch:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="h-screen overflow-hidden">
|
<body class="min-h-screen min-w-full overflow-x-auto h-full whitespace-nowrap">
|
||||||
<main class="flex flex-col h-full divide-y divide-border-table-border">
|
<%= turbo_frame_tag :modals %>
|
||||||
<%= render "partials/header" %>
|
<main class="flex flex-row h-full min-h-screen" data-controller="sidebar">
|
||||||
<div class="flex flex-row flex-1 w-full divide-x divide-border-table-border overflow-hidden">
|
|
||||||
<%= render "partials/sidebar" %>
|
<%= render "partials/sidebar" %>
|
||||||
<div class="w-full h-full">
|
<div class="flex flex-col flex-1 w-full h-[100vh]">
|
||||||
|
<%= render "partials/header" %>
|
||||||
|
<div class="flex-1 h-full pb-0 px-0 lg:pb-4 lg:px-4 lg:overflow-y-auto">
|
||||||
|
<div class="flex-1 h-full min-h-full">
|
||||||
|
<% if flash[:notice] %>
|
||||||
|
<div class="rounded-tl-md rounded-tr-md px-4 py-2 bg-accept text-white">
|
||||||
|
<%= raw flash[:notice] %>
|
||||||
|
</div>
|
||||||
|
<% elsif flash[:alert] %>
|
||||||
|
<div class="rounded-tl-md rounded-tr-md px-4 py-2 bg-danger text-white">
|
||||||
|
<%= raw flash[:alert] %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
<%= yield %>
|
<%= yield %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="h-10 text-md text-center opacity-60">© 2024 FARMITRY All Rights Reserved.</div>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
50
app/views/layouts/device.html.erb
Normal file
50
app/views/layouts/device.html.erb
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><%= content_for(:title) || "Farmitry Hvac" %></title>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<%= csrf_meta_tags %>
|
||||||
|
<%= csp_meta_tag %>
|
||||||
|
|
||||||
|
<%= yield :head %>
|
||||||
|
|
||||||
|
<%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
|
||||||
|
<%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>
|
||||||
|
|
||||||
|
<link rel="icon" href="/icon.png" type="image/png">
|
||||||
|
<link rel="icon" href="/icon.svg" type="image/svg+xml">
|
||||||
|
<link rel="apple-touch-icon" href="/icon.png">
|
||||||
|
|
||||||
|
<%# Includes all stylesheet files in app/assets/stylesheets %>
|
||||||
|
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
|
||||||
|
<%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
|
||||||
|
<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">
|
||||||
|
<%= turbo_frame_tag :modals %>
|
||||||
|
<main class="flex flex-col h-full">
|
||||||
|
<%= 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>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
47
app/views/layouts/web.html.erb
Normal file
47
app/views/layouts/web.html.erb
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><%= content_for(:title) || "Farmitry Hvac" %></title>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<%= csrf_meta_tags %>
|
||||||
|
<%= csp_meta_tag %>
|
||||||
|
|
||||||
|
<%= yield :head %>
|
||||||
|
|
||||||
|
<%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
|
||||||
|
<%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>
|
||||||
|
|
||||||
|
<link rel="icon" href="/icon.png" type="image/png">
|
||||||
|
<link rel="icon" href="/icon.svg" type="image/svg+xml">
|
||||||
|
<link rel="apple-touch-icon" href="/icon.png">
|
||||||
|
|
||||||
|
<%# Includes all stylesheet files in app/assets/stylesheets %>
|
||||||
|
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
|
||||||
|
<%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<%= turbo_frame_tag :modals %>
|
||||||
|
<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 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>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<% if flash[:notice] %>
|
|
||||||
<div class="mb-4 rounded bg-green-100 px-4 py-2 text-green-800 border border-green-300">
|
|
||||||
<%= flash[:notice] %>
|
|
||||||
</div>
|
|
||||||
<% elsif flash[:alert] %>
|
|
||||||
<div class="mb-4 rounded bg-red-100 px-4 py-2 text-red-800 border border-red-300">
|
|
||||||
<%= flash[:alert] %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<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">
|
|
||||||
<table class="base-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>시간</th>
|
|
||||||
<th>온도</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<% @schedule.each do |s| %>
|
|
||||||
<tr>
|
|
||||||
<td><%= s.hour %>시</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 "수정", "/modbus/schedule_edit", class: "btn bg-default-slate" %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<%= 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">
|
|
||||||
<table class="base-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>시간</th>
|
|
||||||
<th>온도</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<% @schedule.each do |s| %>
|
|
||||||
<tr>
|
|
||||||
<td><%= s.hour %>시</td>
|
|
||||||
<td><%= number_field_tag "schedule[#{s.id}][temperature]", s.temperature, step: "0.1", inputmode: "decimal", class: "border px-2 py-1 rounded" %> °C</td>
|
|
||||||
</tr>
|
|
||||||
<% end %>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex p-4">
|
|
||||||
<%= submit_tag "업데이트", class: "btn bg-primary" %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
86
app/views/nutrient/edit_ec.html.erb
Normal file
86
app/views/nutrient/edit_ec.html.erb
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<div class="flex flex-col h-full lg:rounded-lg">
|
||||||
|
<div class="flex flex-row items-center space-x-4 p-2">
|
||||||
|
<%= link_to environment_index_path(id: params[:id]), class: "flex items-center" do %>
|
||||||
|
<i class="fa-solid fa-angle-left text-2xl"></i>
|
||||||
|
<% end %>
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= @controller.id %>동 온도 스케줄러</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4 overflow-y-hidden divide-y divide-base-border bg-base-content rounded-lg p-4">
|
||||||
|
<%= form_with url: edit_temp_environment_path, method: :post, class: 'flex flex-col h-full' do %>
|
||||||
|
<%= hidden_field_tag :controller_id, params[:id] %>
|
||||||
|
<div class="flex flex-col divide-y divide-base-border overflow-y-hidden flex-1 space-y-4">
|
||||||
|
<%= submit_tag "업데이트", class: "btn bg-primary w-fit" %>
|
||||||
|
<div class="overflow-y-auto flex-1 min-h-0 border border-base-border rounded-lg">
|
||||||
|
<ul class="divide-y divide-base-border whitespace-nowrap relative">
|
||||||
|
<% if @schedule.present? %>
|
||||||
|
<% @schedule.each do |s| %>
|
||||||
|
<li class="flex justify-between items-center px-4 py-2 space-x-4 h-full relative">
|
||||||
|
<div class="flex items-center space-x-4 basis-3/5 h-full">
|
||||||
|
<!-- 시간 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">시간</div>
|
||||||
|
<div class="text-xl flex items-center space-x-1 w-full h-full">
|
||||||
|
<%= select_tag "schedule[#{s.id}][hour]",
|
||||||
|
options_for_select((0..23).map { |h| [h.to_s.rjust(2, '0'), h] }, s.hour),
|
||||||
|
class: "select-style min-w-[60px]" %>
|
||||||
|
<div>:</div>
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][minute]",
|
||||||
|
s.minute, min: 0, max: 59, step: 1, inputmode: "decimal",
|
||||||
|
class: "input-style min-w-[60px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 온도 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">온도 (°C)</div>
|
||||||
|
<div class="text-xl flex items-center">
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][temperature]", s.temperature, min: -99, max: 99, step: "0.1", inputmode: "decimal", class: "input-style min-w-[68px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center space-x-4 basis-2/5 justify-end h-full">
|
||||||
|
<!-- 사용 여부 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full">
|
||||||
|
<div class="">ON/OFF</div>
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer h-full">
|
||||||
|
<%= check_box_tag "schedule[#{s.id}][is_active]", "1", s.is_active == true || s.is_active == 1, class: "sr-only peer" %>
|
||||||
|
<div class="toggle_style"></div>
|
||||||
|
<div class="btn-toggle-round"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 삭제 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full relative">
|
||||||
|
<div class=""> </div>
|
||||||
|
<%= link_to schedule_path(s),
|
||||||
|
data: { turbo_method: :delete, turbo_confirm: "정말 삭제하시겠습니까?" },
|
||||||
|
class: "btn bg-danger text-sm flex items-center px-2 py-0 my-1.5 lg:px-4 lg:py-1.5 lg:my-0" do %>
|
||||||
|
<div class="hidden lg:block">삭제</div>
|
||||||
|
<div class="block lg:hidden"><i class="fa-solid fa-xmark"></i></div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<li class="text-center p-4 w-full">저장된 스케줄이 없습니다.</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="flex 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>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
86
app/views/nutrient/edit_mixa.html.erb
Normal file
86
app/views/nutrient/edit_mixa.html.erb
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<div class="flex flex-col h-full lg:rounded-lg">
|
||||||
|
<div class="flex flex-row items-center space-x-4 p-2">
|
||||||
|
<%= link_to environment_index_path(id: params[:id]), class: "flex items-center" do %>
|
||||||
|
<i class="fa-solid fa-angle-left text-2xl"></i>
|
||||||
|
<% end %>
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= @controller.id %>동 온도 스케줄러</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4 overflow-y-hidden divide-y divide-base-border bg-base-content rounded-lg p-4">
|
||||||
|
<%= form_with url: edit_temp_environment_path, method: :post, class: 'flex flex-col h-full' do %>
|
||||||
|
<%= hidden_field_tag :controller_id, params[:id] %>
|
||||||
|
<div class="flex flex-col divide-y divide-base-border overflow-y-hidden flex-1 space-y-4">
|
||||||
|
<%= submit_tag "업데이트", class: "btn bg-primary w-fit" %>
|
||||||
|
<div class="overflow-y-auto flex-1 min-h-0 border border-base-border rounded-lg">
|
||||||
|
<ul class="divide-y divide-base-border whitespace-nowrap relative">
|
||||||
|
<% if @schedule.present? %>
|
||||||
|
<% @schedule.each do |s| %>
|
||||||
|
<li class="flex justify-between items-center px-4 py-2 space-x-4 h-full relative">
|
||||||
|
<div class="flex items-center space-x-4 basis-3/5 h-full">
|
||||||
|
<!-- 시간 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">시간</div>
|
||||||
|
<div class="text-xl flex items-center space-x-1 w-full h-full">
|
||||||
|
<%= select_tag "schedule[#{s.id}][hour]",
|
||||||
|
options_for_select((0..23).map { |h| [h.to_s.rjust(2, '0'), h] }, s.hour),
|
||||||
|
class: "select-style min-w-[60px]" %>
|
||||||
|
<div>:</div>
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][minute]",
|
||||||
|
s.minute, min: 0, max: 59, step: 1, inputmode: "decimal",
|
||||||
|
class: "input-style min-w-[60px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 온도 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">온도 (°C)</div>
|
||||||
|
<div class="text-xl flex items-center">
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][temperature]", s.temperature, min: -99, max: 99, step: "0.1", inputmode: "decimal", class: "input-style min-w-[68px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center space-x-4 basis-2/5 justify-end h-full">
|
||||||
|
<!-- 사용 여부 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full">
|
||||||
|
<div class="">ON/OFF</div>
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer h-full">
|
||||||
|
<%= check_box_tag "schedule[#{s.id}][is_active]", "1", s.is_active == true || s.is_active == 1, class: "sr-only peer" %>
|
||||||
|
<div class="toggle_style"></div>
|
||||||
|
<div class="btn-toggle-round"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 삭제 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full relative">
|
||||||
|
<div class=""> </div>
|
||||||
|
<%= link_to schedule_path(s),
|
||||||
|
data: { turbo_method: :delete, turbo_confirm: "정말 삭제하시겠습니까?" },
|
||||||
|
class: "btn bg-danger text-sm flex items-center px-2 py-0 my-1.5 lg:px-4 lg:py-1.5 lg:my-0" do %>
|
||||||
|
<div class="hidden lg:block">삭제</div>
|
||||||
|
<div class="block lg:hidden"><i class="fa-solid fa-xmark"></i></div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<li class="text-center p-4 w-full">저장된 스케줄이 없습니다.</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="flex 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>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
86
app/views/nutrient/edit_mixb.html.erb
Normal file
86
app/views/nutrient/edit_mixb.html.erb
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<div class="flex flex-col h-full lg:rounded-lg">
|
||||||
|
<div class="flex flex-row items-center space-x-4 p-2">
|
||||||
|
<%= link_to environment_index_path(id: params[:id]), class: "flex items-center" do %>
|
||||||
|
<i class="fa-solid fa-angle-left text-2xl"></i>
|
||||||
|
<% end %>
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= @controller.id %>동 온도 스케줄러</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4 overflow-y-hidden divide-y divide-base-border bg-base-content rounded-lg p-4">
|
||||||
|
<%= form_with url: edit_temp_environment_path, method: :post, class: 'flex flex-col h-full' do %>
|
||||||
|
<%= hidden_field_tag :controller_id, params[:id] %>
|
||||||
|
<div class="flex flex-col divide-y divide-base-border overflow-y-hidden flex-1 space-y-4">
|
||||||
|
<%= submit_tag "업데이트", class: "btn bg-primary w-fit" %>
|
||||||
|
<div class="overflow-y-auto flex-1 min-h-0 border border-base-border rounded-lg">
|
||||||
|
<ul class="divide-y divide-base-border whitespace-nowrap relative">
|
||||||
|
<% if @schedule.present? %>
|
||||||
|
<% @schedule.each do |s| %>
|
||||||
|
<li class="flex justify-between items-center px-4 py-2 space-x-4 h-full relative">
|
||||||
|
<div class="flex items-center space-x-4 basis-3/5 h-full">
|
||||||
|
<!-- 시간 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">시간</div>
|
||||||
|
<div class="text-xl flex items-center space-x-1 w-full h-full">
|
||||||
|
<%= select_tag "schedule[#{s.id}][hour]",
|
||||||
|
options_for_select((0..23).map { |h| [h.to_s.rjust(2, '0'), h] }, s.hour),
|
||||||
|
class: "select-style min-w-[60px]" %>
|
||||||
|
<div>:</div>
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][minute]",
|
||||||
|
s.minute, min: 0, max: 59, step: 1, inputmode: "decimal",
|
||||||
|
class: "input-style min-w-[60px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 온도 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">온도 (°C)</div>
|
||||||
|
<div class="text-xl flex items-center">
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][temperature]", s.temperature, min: -99, max: 99, step: "0.1", inputmode: "decimal", class: "input-style min-w-[68px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center space-x-4 basis-2/5 justify-end h-full">
|
||||||
|
<!-- 사용 여부 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full">
|
||||||
|
<div class="">ON/OFF</div>
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer h-full">
|
||||||
|
<%= check_box_tag "schedule[#{s.id}][is_active]", "1", s.is_active == true || s.is_active == 1, class: "sr-only peer" %>
|
||||||
|
<div class="toggle_style"></div>
|
||||||
|
<div class="btn-toggle-round"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 삭제 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full relative">
|
||||||
|
<div class=""> </div>
|
||||||
|
<%= link_to schedule_path(s),
|
||||||
|
data: { turbo_method: :delete, turbo_confirm: "정말 삭제하시겠습니까?" },
|
||||||
|
class: "btn bg-danger text-sm flex items-center px-2 py-0 my-1.5 lg:px-4 lg:py-1.5 lg:my-0" do %>
|
||||||
|
<div class="hidden lg:block">삭제</div>
|
||||||
|
<div class="block lg:hidden"><i class="fa-solid fa-xmark"></i></div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<li class="text-center p-4 w-full">저장된 스케줄이 없습니다.</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="flex 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>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
86
app/views/nutrient/edit_ph.html.erb
Normal file
86
app/views/nutrient/edit_ph.html.erb
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<div class="flex flex-col h-full lg:rounded-lg">
|
||||||
|
<div class="flex flex-row items-center space-x-4 p-2">
|
||||||
|
<%= link_to nutrient_index_path(id: params[:id]), class: "flex items-center" do %>
|
||||||
|
<i class="fa-solid fa-angle-left text-2xl"></i>
|
||||||
|
<% end %>
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= @controller.id %>동 온도 스케줄러</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4 overflow-y-hidden divide-y divide-base-border bg-base-content rounded-lg p-4">
|
||||||
|
<%= form_with url: edit_temp_environment_path, method: :post, class: 'flex flex-col h-full' do %>
|
||||||
|
<%= hidden_field_tag :controller_id, params[:id] %>
|
||||||
|
<div class="flex flex-col divide-y divide-base-border overflow-y-hidden flex-1 space-y-4">
|
||||||
|
<%= submit_tag "업데이트", class: "btn bg-primary w-fit" %>
|
||||||
|
<div class="overflow-y-auto flex-1 min-h-0 border border-base-border rounded-lg">
|
||||||
|
<ul class="divide-y divide-base-border whitespace-nowrap relative">
|
||||||
|
<% if @schedule.present? %>
|
||||||
|
<% @schedule.each do |s| %>
|
||||||
|
<li class="flex justify-between items-center px-4 py-2 space-x-4 h-full relative">
|
||||||
|
<div class="flex items-center space-x-4 basis-3/5 h-full">
|
||||||
|
<!-- 시간 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">시간</div>
|
||||||
|
<div class="text-xl flex items-center space-x-1 w-full h-full">
|
||||||
|
<%= select_tag "schedule[#{s.id}][hour]",
|
||||||
|
options_for_select((0..23).map { |h| [h.to_s.rjust(2, '0'), h] }, s.hour),
|
||||||
|
class: "select-style min-w-[60px]" %>
|
||||||
|
<div>:</div>
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][minute]",
|
||||||
|
s.minute, min: 0, max: 59, step: 1, inputmode: "decimal",
|
||||||
|
class: "input-style min-w-[60px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 온도 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">온도 (°C)</div>
|
||||||
|
<div class="text-xl flex items-center">
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][temperature]", s.temperature, min: -99, max: 99, step: "0.1", inputmode: "decimal", class: "input-style min-w-[68px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center space-x-4 basis-2/5 justify-end h-full">
|
||||||
|
<!-- 사용 여부 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full">
|
||||||
|
<div class="">ON/OFF</div>
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer h-full">
|
||||||
|
<%= check_box_tag "schedule[#{s.id}][is_active]", "1", s.is_active == true || s.is_active == 1, class: "sr-only peer" %>
|
||||||
|
<div class="toggle_style"></div>
|
||||||
|
<div class="btn-toggle-round"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 삭제 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full relative">
|
||||||
|
<div class=""> </div>
|
||||||
|
<%= link_to schedule_path(s),
|
||||||
|
data: { turbo_method: :delete, turbo_confirm: "정말 삭제하시겠습니까?" },
|
||||||
|
class: "btn bg-danger text-sm flex items-center px-2 py-0 my-1.5 lg:px-4 lg:py-1.5 lg:my-0" do %>
|
||||||
|
<div class="hidden lg:block">삭제</div>
|
||||||
|
<div class="block lg:hidden"><i class="fa-solid fa-xmark"></i></div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<li class="text-center p-4 w-full">저장된 스케줄이 없습니다.</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="flex 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>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
453
app/views/nutrient/index.html.erb
Normal file
453
app/views/nutrient/index.html.erb
Normal file
@@ -0,0 +1,453 @@
|
|||||||
|
<div class="w-full h-full content overflow-y-auto">
|
||||||
|
<div class="flex-1 grid grid-cols-2 xl:grid-cols-4 gap-4">
|
||||||
|
<!-- pH -->
|
||||||
|
<%= link_to edit_temp_environment_path(id: params[:id]), class: "dashboard-group bg-base-content" do %>
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<div class="size-8 bg-nut-color rounded-full text-base-background flex flex-col justify-center">
|
||||||
|
<div class="flex flex-row justify-center items-baseline">
|
||||||
|
<span class="text-xl">pH</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">pH</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<i class="fa-solid fa-calendar-days text-2xl"></i>
|
||||||
|
<i class="fa-solid fa-angle-right text-2xl opacity-90"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">현재 pH</div>
|
||||||
|
<div class="font-bold text-3xl">6.0<span class="font-normal text-2xl"> pH</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full pl-4">
|
||||||
|
<div class="opacity-50">오차 pH</div>
|
||||||
|
<div class="font-bold text-3xl">0.1<span class="font-normal text-2xl"> pH</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<!-- EC -->
|
||||||
|
<%= link_to edit_temp_environment_path(id: params[:id]), class: "dashboard-group bg-base-content" do %>
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<div class="size-8 bg-nut-color rounded-full text-base-background flex flex-col justify-center">
|
||||||
|
<div class="flex flex-row justify-center items-baseline">
|
||||||
|
<span class="text-xl">EC</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">EC</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<i class="fa-solid fa-calendar-days text-2xl"></i>
|
||||||
|
<i class="fa-solid fa-angle-right text-2xl opacity-90"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">현재 EC</div>
|
||||||
|
<div class="font-bold text-3xl">1.2<span class="font-normal text-2xl"> mS/cm</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full pl-4">
|
||||||
|
<div class="opacity-50">오차 pH</div>
|
||||||
|
<div class="font-bold text-3xl">0.1<span class="font-normal text-2xl"> mS/cm</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<!-- 양액 온도 -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-temperature-low text-nut-color text-3xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">양액 온도</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">양액 온도</div>
|
||||||
|
<div class="font-bold text-3xl">20.2<span class="font-normal text-2xl"> °C</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full pl-4">
|
||||||
|
<div class="opacity-50">원액 온도</div>
|
||||||
|
<div class="font-bold text-3xl">20.2<span class="font-normal text-2xl"> °C</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 수위 정보 -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-glass-water-droplet text-nut-color text-3xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">수위 정보</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">상태</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2">
|
||||||
|
<div data-toggle-target="dot" class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div class="font-bold text-3xl text-notice">정상</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 원수 탱크 -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2 cursor-pointer">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-fan text-etc-color text-3xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">원수 탱크</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">모드</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2">
|
||||||
|
<div data-toggle-target="dot" class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div data-toggle-target="label" class="font-bold text-3xl text-notice">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 원수 펌프 -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<%= render "partials/icons/pump_icon", class: "w-auto h-6 text-etc-color" %>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">원수 펌프</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">모드</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2">
|
||||||
|
<div data-toggle-target="dot" class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div data-toggle-target="label" class="font-bold text-3xl text-notice">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 교반기A -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-fan text-etc-color text-3xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">교반기A</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<i class="fa-solid fa-calendar-days text-2xl"></i>
|
||||||
|
<i class="fa-solid fa-angle-right text-2xl opacity-90"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">설정 시간</div>
|
||||||
|
<div class="font-bold text-3xl">30<span class="font-normal text-2xl"> 분</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full pl-4">
|
||||||
|
<div class="opacity-50">모드</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2">
|
||||||
|
<div data-toggle-target="dot" class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div data-toggle-target="label" class="font-bold text-3xl text-notice">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 교반기B -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<i class="fa-solid fa-fan text-etc-color text-3xl text-left"></i>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">교반기B</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<i class="fa-solid fa-calendar-days text-2xl"></i>
|
||||||
|
<i class="fa-solid fa-angle-right text-2xl opacity-90"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">설정 시간</div>
|
||||||
|
<div class="font-bold text-3xl">30<span class="font-normal text-2xl"> 분</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full pl-4">
|
||||||
|
<div class="opacity-50">모드</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2">
|
||||||
|
<div data-toggle-target="dot" class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div data-toggle-target="label" class="font-bold text-3xl text-notice">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 급수/공급 펌프 -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<%= render "partials/icons/pump_icon", class: "w-auto h-6 text-etc-color" %>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">급수/공급 펌프</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">모드</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2">
|
||||||
|
<div data-toggle-target="dot" class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div data-toggle-target="label" class="font-bold text-3xl text-notice">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 1번급수 솔벨브 -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<%= render "partials/icons/valve_icon", class: "w-auto h-6 text-etc-color" %>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">1번급수 솔벨브</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">모드</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2">
|
||||||
|
<div data-toggle-target="dot" class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div data-toggle-target="label" class="font-bold text-3xl text-notice">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 2번급수 솔벨브 -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<%= render "partials/icons/valve_icon", class: "w-auto h-6 text-etc-color" %>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">2번급수 솔벨브</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">모드</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2">
|
||||||
|
<div data-toggle-target="dot" class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div data-toggle-target="label" class="font-bold text-3xl text-notice">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 퇴수 솔벨브 -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<%= render "partials/icons/valve_icon", class: "w-auto h-6 text-etc-color" %>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">퇴수 솔벨브</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">모드</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2">
|
||||||
|
<div data-toggle-target="dot" class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div data-toggle-target="label" class="font-bold text-3xl text-notice">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 솔레노이드 벨브A -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<%= render "partials/icons/valve_icon", class: "w-auto h-6 text-etc-color" %>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">솔레노이드 벨브A</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">모드</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2">
|
||||||
|
<div data-toggle-target="dot" class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div data-toggle-target="label" class="font-bold text-3xl text-notice">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 솔레노이드 벨브B -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<%= render "partials/icons/valve_icon", class: "w-auto h-6 text-etc-color" %>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">솔레노이드 벨브B</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">모드</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2">
|
||||||
|
<div data-toggle-target="dot" class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div data-toggle-target="label" class="font-bold text-3xl text-notice">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 솔레노이드 벨브C -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full space-y-2">
|
||||||
|
<div class="flex flex-row justify-between w-full">
|
||||||
|
<div class="flex flex-row space-x-4 items-center">
|
||||||
|
<%= render "partials/icons/valve_icon", class: "w-auto h-6 text-etc-color" %>
|
||||||
|
<div class="items-baseline">
|
||||||
|
<span class="text-2xl">솔레노이드 벨브C</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full divide-x divide-base-border">
|
||||||
|
<div class="flex flex-col items-baseline text-right w-full">
|
||||||
|
<div class="opacity-50">모드</div>
|
||||||
|
<div class="flex flex-row items-center space-x-2">
|
||||||
|
<div data-toggle-target="dot" class="size-5 bg-notice rounded-full"></div>
|
||||||
|
<div data-toggle-target="label" class="font-bold text-3xl text-notice">ON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 긴급 정지 -->
|
||||||
|
<div class="dashboard-group bg-base-content"
|
||||||
|
data-controller="toggle"
|
||||||
|
data-action="click->toggle#toggle"
|
||||||
|
data-toggle-target="background">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div class="text-2xl">긴급 정지</div>
|
||||||
|
<div class="flex flex-row justify-end items-baseline w-full">
|
||||||
|
<div class="flex size-12 bg-etc-color rounded-lg items-center justify-center">
|
||||||
|
<i class="fa-solid fa-pause text-white text-3xl text-left"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,11 +1,18 @@
|
|||||||
<header class="flex header" data-controller="timer">
|
<header class="flex header justify-between items-center px-4" data-controller="timer">
|
||||||
<div class="flex flex-row h-full">
|
<div class="flex items-center space-x-4">
|
||||||
<%= image_tag "svg/svg_logo.svg", class: "w-16 h-auto" %>
|
<!-- <div data-timer-target="output" class="hidden lg:block"></div>-->
|
||||||
<div class="flex h-full items-center tracking-[0.5rem]">FARMITRY</div>
|
<button data-action="click->sidebar#toggle" class="block lg:hidden p-2 cursor-pointer">
|
||||||
|
<i class="fa-solid fa-bars text-2xl"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div data-timer-target="output" class="text-gray-600"></div>
|
|
||||||
<div class="flex flex-row items-center gap-x-4">
|
<!-- 로그인 정보 -->
|
||||||
<i class="fa-solid fa-circle-user text-4xl text-default-slate-dark"></i>
|
<div class="flex items-center space-x-2">
|
||||||
<%= link_to "로그아웃", '', class: "btn" %>
|
<i class="fa-solid fa-user-circle text-3xl text-base-text/60"></i>
|
||||||
|
<span class="font-bold text-base-text">홍길동</span><span class="font-normal"> 님</span>
|
||||||
|
<%= button_to "로그아웃", root_path,
|
||||||
|
method: :post,
|
||||||
|
data: { turbo_confirm: "로그아웃 하시겠습니까?" },
|
||||||
|
class: "btn bg-danger" %>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
44
app/views/partials/_modals_open.html.erb
Normal file
44
app/views/partials/_modals_open.html.erb
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<%= turbo_frame_tag :modals do %>
|
||||||
|
<div class="relative z-50" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
||||||
|
<!--
|
||||||
|
Background backdrop, show/hide based on modal state.
|
||||||
|
|
||||||
|
Entering: "ease-out duration-300"
|
||||||
|
From: "opacity-0"
|
||||||
|
To: "opacity-100"
|
||||||
|
Leaving: "ease-in duration-200"
|
||||||
|
From: "opacity-100"
|
||||||
|
To: "opacity-0"
|
||||||
|
-->
|
||||||
|
<div id="modals-backdrop" class="fixed inset-0 bg-base-background transition-opacity opacity-0 ease-out duration-500" aria-hidden="true"></div>
|
||||||
|
|
||||||
|
<div class="fixed inset-0 z-50 w-screen overflow-y-auto py-4">
|
||||||
|
<div class="flex min-h-full items-center justify-center text-center sm:items-center">
|
||||||
|
<!--
|
||||||
|
Modal panel, show/hide based on modal state.
|
||||||
|
|
||||||
|
Entering: "ease-out duration-300"
|
||||||
|
From: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||||
|
To: "opacity-100 translate-y-0 sm:scale-100"
|
||||||
|
Leaving: "ease-in duration-200"
|
||||||
|
From: "opacity-100 translate-y-0 sm:scale-100"
|
||||||
|
To: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||||
|
-->
|
||||||
|
<div id="modals-panel" class="p-4 space-y-4 z-50 relative transform overflow-hidden rounded-md bg-base-sidebar text-left shadow-xl transition-all w-full max-w-full mx-2 sm:mx-10 opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95 ease-out duration-500" data-controller="modals" data-action="modals:click:outside->modals#close">
|
||||||
|
<div>
|
||||||
|
<%= render "partials/modals/#{type}" %>
|
||||||
|
</div>
|
||||||
|
<% if close_button != "false" %>
|
||||||
|
<%= button_tag type: 'button',
|
||||||
|
data: {
|
||||||
|
action: 'modals#close'
|
||||||
|
},
|
||||||
|
class: 'inline-flex btn w-full' do %>
|
||||||
|
<div class="items-center"><i class="fa-solid fa-square-caret-down"></i> 닫기</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
@@ -1,42 +1,37 @@
|
|||||||
<nav class="side-bar overflow-y-auto">
|
<div data-sidebar-target="overlay" data-action="click->sidebar#close" class="fixed inset-0 bg-black/50 z-40 hidden h-full"></div>
|
||||||
<div class="menu-group">
|
<nav class="side-bar overflow-y-auto transition-transform lg:translate-x-0 -translate-x-full bg-base-sidebar lg:bg-base-sidebar fixed lg:relative top-0 left-0 h-full lg:h-[100vh] z-40" data-sidebar-target="menu">
|
||||||
|
<div class="flex flex-row items-center justify-center">
|
||||||
|
<%= image_tag "svg/svg_logo.svg", class: "w-auto h-8 mr-4" %>
|
||||||
|
<%= link_to root_path do %>
|
||||||
|
<div class="flex h-full items-center text-center tracking-[0.5rem] text-2xl"><span class="text-logo-color">FARM</span>ITRY</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div class="mt-8">
|
||||||
|
<%= link_to "#", class: "menu-group #{'menu-group-active' if false}" do %>
|
||||||
<div class="menu-group-icon">
|
<div class="menu-group-icon">
|
||||||
<i class="fa-solid fa-house text-default-slate-dark text-4xl"></i>
|
<i class="fa-solid fa-chart-line text-2xl"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-group-name">
|
<div class="menu-group-name">
|
||||||
HOME
|
양액부
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<% end %>
|
||||||
<div class="menu-group">
|
<hr>
|
||||||
|
<%= link_to "#", class: "menu-group menu-group-active" do %>
|
||||||
<div class="menu-group-icon">
|
<div class="menu-group-icon">
|
||||||
<i class="fa-solid fa-temperature-high text-default-slate-dark text-4xl"></i>
|
<i class="fa-solid fa-leaf text-2xl"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-group-name">
|
<div class="menu-group-name">
|
||||||
메뉴 2
|
1동 환경부
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<% end %>
|
||||||
<div class="menu-group">
|
<hr>
|
||||||
|
<%= link_to "#", class: "menu-group #{'menu-group-active' if false}" do %>
|
||||||
<div class="menu-group-icon">
|
<div class="menu-group-icon">
|
||||||
<i class="fa-solid fa-fan text-default-slate-dark text-4xl"></i>
|
<i class="fa-solid fa-leaf text-2xl"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-group-name">
|
<div class="menu-group-name">
|
||||||
메뉴 3
|
2동 환경부
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="menu-group">
|
|
||||||
<div class="menu-group-icon">
|
|
||||||
<i class="fa-solid fa-arrows-spin text-default-slate-dark 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-default-slate-dark text-4xl"></i>
|
|
||||||
</div>
|
|
||||||
<div class="menu-group-name">
|
|
||||||
설청
|
|
||||||
</div>
|
</div>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
5
app/views/partials/icons/_pump_icon.html.erb
Normal file
5
app/views/partials/icons/_pump_icon.html.erb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="286" height="164" viewBox="0 0 286 164" fill="currentColor" xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>">
|
||||||
|
<path d="M259 153L179.015 153C197.205 142.983 211.133 126.18 217.385 106L259 106L259 94L286 94L286 164L259 164L259 153Z"/>
|
||||||
|
<path d="M6.11959e-06 -1.21518e-05L27 -9.79135e-06L27 11L139 11L139 11.0078C108.644 11.4269 82.8939 30.8954 73.1602 58L27 58L27 70L0 70L6.11959e-06 -1.21518e-05Z"/>
|
||||||
|
<path d="M140 11C179.212 11 211 42.7878 211 82C211 121.212 179.212 153 140 153C100.788 153 69 121.212 69 82C69 42.7878 100.788 11 140 11ZM155.737 102.695C151.368 106.023 145.916 108 140 108C134.331 108 129.087 106.185 124.814 103.105L102.157 116.188C111.49 126.512 124.987 133 140 133C155.245 133 168.925 126.308 178.271 115.705L155.737 102.695ZM131 31.792C107.126 36.0428 89 56.9045 89 82C89 88.3231 90.1514 94.3772 92.2549 99.9648L114.505 87.1191C114.174 85.464 114 83.7523 114 82C114 70.8045 121.076 61.2623 131 57.6006V31.792ZM150 57.9932C159.396 61.9114 166 71.1843 166 82C166 83.5208 165.868 85.0108 165.617 86.46L187.967 99.3633C189.929 93.9436 191 88.0969 191 82C191 57.2561 173.378 36.6283 150 31.9805V57.9932ZM140 72C134.477 72 130 76.4772 130 82C130 87.5228 134.477 92 140 92C145.523 92 150 87.5228 150 82C150 76.4772 145.523 72 140 72Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
12
app/views/partials/icons/_valve_icon.html.erb
Normal file
12
app/views/partials/icons/_valve_icon.html.erb
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 117.35" fill="currentColor" class="<%= local_assigns[:class] %>">
|
||||||
|
<path d="M79.22,54.41c5.16,3.03,9.06,6.88,11.81,11.19h5.36v36.21h-5.73c-5.72,9.03-15.74,15.53-28.98,15.53
|
||||||
|
c-13.23,0-23.34-6.5-29.09-15.53h-5.96V65.6h5.91c2.98-4.48,7.25-8.48,12.96-11.58c3.04-1.64,4.88-3.69,4.9-5.53
|
||||||
|
c0.03-2.33-2.52-4.31-5.26-4.31h-1.36c-3.32,0-6.04-2.72-6.04-6.04l0,0c0-3.32,2.72-6.04,6.04-6.04h7.76l6.1-7.57
|
||||||
|
c-0.02-0.28-0.04-0.57-0.04-0.87v-9.99H32.87c-3.77,0-6.85-3.08-6.85-6.85l0,0c0-3.76,3.08-6.85,6.85-6.85h58.18
|
||||||
|
c3.76,0,6.85,3.08,6.85,6.85l0,0c0,3.77-3.08,6.85-6.85,6.85H67.51v9.99c0,0.41-0.03,0.82-0.07,1.21l5.48,7.23h6.67
|
||||||
|
c3.32,0,6.04,2.72,6.04,6.04l0,0c0,3.32-2.72,5.97-6.04,6.04c-4.57,0.1-6.25,2.06-6.04,4.34C73.91,52.73,76.83,53.01,79.22,54.41
|
||||||
|
L79.22,54.41z M101.34,65.6h4.2v-8.65c0-1.54,1.26-2.79,2.79-2.79h11.76c1.54,0,2.79,1.26,2.79,2.79v53.51
|
||||||
|
c0,1.54-1.26,2.79-2.79,2.79h-11.76c-1.54,0-2.79-1.26-2.79-2.79v-8.65h-4.2V65.6L101.34,65.6z M21.67,101.81h-4.33v8.65
|
||||||
|
c0,1.54-1.26,2.79-2.79,2.79H2.79C1.26,113.25,0,112,0,110.46V56.95c0-1.54,1.26-2.79,2.79-2.79h11.76c1.54,0,2.79,1.26,2.79,2.79
|
||||||
|
v8.65h4.33V101.81L21.67,101.81z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
41
app/views/partials/modals/_add_schedule.html.erb
Normal file
41
app/views/partials/modals/_add_schedule.html.erb
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<%= form_with model: @schedule, method: :post, data: { turbo: false }, class: 'flex flex-col h-full space-y-4' do |form| %>
|
||||||
|
<%= form.hidden_field :controller_id, value: params[:id] %>
|
||||||
|
<div class="flex-1 text-white space-y-4">
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= Controller.find(params[:id]).name %> 컨트롤러</div>
|
||||||
|
<table class="base-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>시간</th>
|
||||||
|
<th>분</th>
|
||||||
|
<th>ON/OFF</th>
|
||||||
|
<th>온도</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<%= form.select :hour,
|
||||||
|
options_for_select((0..23).map { |h| [h.to_s.rjust(2, '0'), h] }, @schedule.hour),
|
||||||
|
{},
|
||||||
|
class: "select-style text-white" %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= form.number_field :minute,
|
||||||
|
min: 0, max: 59, step: 1, inputmode: "decimal", class: "input-style" %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= check_box_tag "schedule[is_active]", "1", true %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= form.number_field :temperature,
|
||||||
|
step: "0.1", inputmode: "decimal", class: "input-style" %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex">
|
||||||
|
<%= submit_tag "추가", class: "btn bg-primary" %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
38
app/views/rack/index.html.erb
Normal file
38
app/views/rack/index.html.erb
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<div class="flex flex-col h-full divide-y divide-base-border lg:rounded-lg bg-base-third border border-table-border">
|
||||||
|
<div class="flex flex-col flex-1 space-y-4 overflow-y-hidden">
|
||||||
|
<div class="base-list">
|
||||||
|
<div class="flex justify-between p-4">
|
||||||
|
<div class="flex flex-row items-center space-x-4">
|
||||||
|
<%= link_to environment_index_path(id: 1), class: "flex items-center" do %>
|
||||||
|
<i class="fa-solid fa-angle-left text-3xl"></i>
|
||||||
|
<% end %>
|
||||||
|
<div class="text-2xl font-bold leading-10">통합 랙 정보</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<div class="flex items-center space-x-6">
|
||||||
|
<!-- 시간 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-2">
|
||||||
|
<div>시간</div>
|
||||||
|
<div class="text-3xl flex items-baseline space-x-1">
|
||||||
|
<div class="text-center w-[36px]">00</div>
|
||||||
|
<div>:</div>
|
||||||
|
<div class="text-center w-[36px]">00</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 온도 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-4">
|
||||||
|
<div>온도</div>
|
||||||
|
<div class="text-3xl flex items-end space-x-2">
|
||||||
|
<div class="text-left w-[50px]">00</div>
|
||||||
|
<div class="text-base leading-7">°C</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
159
app/views/schedules/index.html.erb
Normal file
159
app/views/schedules/index.html.erb
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<div class="flex-1 grid grid-cols-2 lg:grid-cols-4 gap-4 p-4">
|
||||||
|
<!-- 실내온도 -->
|
||||||
|
<div class="dashboard-group">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div class="text-xl">실내온도</div>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full">
|
||||||
|
<i class="fa-solid fa-temperature-quarter text-base-text/80 text-4xl text-left w-full"></i>
|
||||||
|
<div class="items-baseline text-right w-full">
|
||||||
|
<span class="text-4xl font-['Chakra_Petch'] font-light">15.5</span>
|
||||||
|
<span class="text-2xl">°C</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 실내습도 -->
|
||||||
|
<div class="dashboard-group">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div class="text-xl">실내습도</div>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full">
|
||||||
|
<i class="fa-solid fa-droplet text-base-text/80 text-4xl text-left w-full"></i>
|
||||||
|
<div class="items-baseline text-right w-full"><span class="text-4xl font-['Chakra_Petch']">60</span> <span class="text-2xl">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- CO2 -->
|
||||||
|
<div class="dashboard-group">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div><span class="text-xl">CO</span><span class="text-sm">2</span></div>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full">
|
||||||
|
<div class="size-10 bg-base-text/80 rounded-full text-base-background flex flex-col justify-center">
|
||||||
|
<div class="flex flex-row justify-center items-baseline">
|
||||||
|
<span class="text-xl">CO</span><span class="text-sm">2</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="items-baseline text-right"><span class="text-4xl font-['Chakra_Petch']">60</span> <span class="text-2xl">%</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- none -->
|
||||||
|
<div class="dashboard-group">1</div>
|
||||||
|
|
||||||
|
<!-- Fan -->
|
||||||
|
<div class="dashboard-group">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div class="text-xl">FAN</div>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full">
|
||||||
|
<i class="fa-solid fa-fan text-base-text/80 text-4xl text-left w-full"></i>
|
||||||
|
<div class="items-baseline text-right w-full"><span class="text-4xl font-['Chakra_Petch']">60</span> <span class="text-2xl">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 공조기 -->
|
||||||
|
<div class="dashboard-group">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div class="text-xl">공조기</div>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full">
|
||||||
|
<i class="fa-solid fa-temperature-quarter text-base-text/80 text-4xl text-left w-full"></i>
|
||||||
|
<div class="items-baseline text-right w-full"><span class="text-4xl font-['Chakra_Petch']">15.5</span>
|
||||||
|
<span class="text-2xl">°C</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- LED -->
|
||||||
|
<div class="dashboard-group">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div class="text-xl">LED</div>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full">
|
||||||
|
<i class="fa-solid fa-lightbulb text-base-text/80 text-4xl text-left w-full"></i>
|
||||||
|
<div class="items-baseline text-right w-full"><span class="text-4xl font-['Chakra_Petch']">60</span> <span class="text-2xl">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- none -->
|
||||||
|
<div class="dashboard-group">1</div>
|
||||||
|
|
||||||
|
<!-- pH -->
|
||||||
|
<div class="dashboard-group">
|
||||||
|
<div class="flex justify-between items-baseline">
|
||||||
|
<div class="text-4xl">pH</div>
|
||||||
|
<div class="text-3xl font-['Chakra_Petch']">7.1</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-baseline">
|
||||||
|
<div class="text-2xl">
|
||||||
|
6.0 ~ 6.0
|
||||||
|
</div>
|
||||||
|
<div><i class="fa-solid fa-gear text-base-text/30 text-2xl"></i></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- EC -->
|
||||||
|
<div class="dashboard-group">
|
||||||
|
<div class="flex justify-between items-baseline">
|
||||||
|
<div class="text-4xl">EC</div>
|
||||||
|
<div class="text-3xl font-['Chakra_Petch']">0.1</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-baseline">
|
||||||
|
<div class="text-2xl">
|
||||||
|
0 ~ 1
|
||||||
|
</div>
|
||||||
|
<div><i class="fa-solid fa-gear text-base-text/30 text-2xl"></i></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 양액온도 -->
|
||||||
|
<div class="dashboard-group">
|
||||||
|
<div class="flex flex-col justify-between h-full">
|
||||||
|
<div class="text-xl">양액온도</div>
|
||||||
|
<div class="flex flex-row justify-between items-baseline w-full">
|
||||||
|
<!-- <i class="fa-solid fa-temperature-quarter text-base-text/80 text-4xl text-left w-full"></i>-->
|
||||||
|
<div class="items-baseline text-right w-full"><span class="text-4xl font-['Chakra_Petch']">20.96</span>
|
||||||
|
<span class="text-2xl">°C</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- none -->
|
||||||
|
<div class="dashboard-group">1</div>
|
||||||
|
|
||||||
|
<!-- none -->
|
||||||
|
<div class="dashboard-group">
|
||||||
|
<div class="flex flex-col justify-center space-y-4 h-full">
|
||||||
|
<i class="fa-solid fa-recycle text-base-text/80 text-5xl text-center w-full"></i>
|
||||||
|
<div class="text-xl text-center">자동화</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- none -->
|
||||||
|
<div class="dashboard-group">
|
||||||
|
<div class="flex flex-col justify-center space-y-4 h-full">
|
||||||
|
<i class="fa-solid fa-rotate-right text-base-text/80 text-5xl text-center w-full"></i>
|
||||||
|
<div class="text-xl text-center">초기화</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- none -->
|
||||||
|
<div class="dashboard-group">
|
||||||
|
<div class="flex flex-col justify-center space-y-4 h-full">
|
||||||
|
<i class="fa-solid fa-circle-pause text-base-text/80 text-5xl text-center w-full"></i>
|
||||||
|
<div class="text-xl text-center">긴급 정지</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- none -->
|
||||||
|
<div class="dashboard-group">
|
||||||
|
<div class="flex flex-col justify-center space-y-4 h-full">
|
||||||
|
<i class="fa-solid fa-faucet-drip text-base-text/80 text-5xl text-center w-full"></i>
|
||||||
|
<div class="text-xl text-center">1회 급수 설정</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
86
app/views/schedules/schedule_edit.html.erb
Normal file
86
app/views/schedules/schedule_edit.html.erb
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<div class="flex flex-col h-full lg:rounded-lg">
|
||||||
|
<div class="flex flex-row items-center space-x-4 p-2">
|
||||||
|
<%= link_to environment_index_path(id: params[:id]), class: "flex items-center" do %>
|
||||||
|
<i class="fa-solid fa-angle-left text-2xl"></i>
|
||||||
|
<% end %>
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= @controller.id %>동 온도 스케줄러</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4 overflow-y-hidden divide-y divide-base-border bg-base-content rounded-lg p-4">
|
||||||
|
<%= form_with url: edit_temp_environment_path, method: :post, class: 'flex flex-col h-full' do %>
|
||||||
|
<%= hidden_field_tag :controller_id, params[:id] %>
|
||||||
|
<div class="flex flex-col divide-y divide-base-border overflow-y-hidden flex-1 space-y-4">
|
||||||
|
<%= submit_tag "업데이트", class: "btn bg-primary w-fit" %>
|
||||||
|
<div class="overflow-y-auto flex-1 min-h-0 border border-base-border rounded-lg">
|
||||||
|
<ul class="divide-y divide-base-border whitespace-nowrap relative">
|
||||||
|
<% if @schedule.present? %>
|
||||||
|
<% @schedule.each do |s| %>
|
||||||
|
<li class="flex justify-between items-center px-4 py-2 space-x-4 h-full relative">
|
||||||
|
<div class="flex items-center space-x-4 basis-3/5 h-full">
|
||||||
|
<!-- 시간 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">시간</div>
|
||||||
|
<div class="text-xl flex items-center space-x-1 w-full h-full">
|
||||||
|
<%= select_tag "schedule[#{s.id}][hour]",
|
||||||
|
options_for_select((0..23).map { |h| [h.to_s.rjust(2, '0'), h] }, s.hour),
|
||||||
|
class: "select-style min-w-[60px]" %>
|
||||||
|
<div>:</div>
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][minute]",
|
||||||
|
s.minute, min: 0, max: 59, step: 1, inputmode: "decimal",
|
||||||
|
class: "input-style min-w-[60px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 온도 -->
|
||||||
|
<div class="flex flex-col lg:flex-row h-full items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0">
|
||||||
|
<div class=" text-left w-full lg:w-auto">온도 (°C)</div>
|
||||||
|
<div class="text-xl flex items-center">
|
||||||
|
<%= number_field_tag "schedule[#{s.id}][temperature]", s.temperature, min: -99, max: 99, step: "0.1", inputmode: "decimal", class: "input-style min-w-[68px]" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center space-x-4 basis-2/5 justify-end h-full">
|
||||||
|
<!-- 사용 여부 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full">
|
||||||
|
<div class="">ON/OFF</div>
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer h-full">
|
||||||
|
<%= check_box_tag "schedule[#{s.id}][is_active]", "1", s.is_active == true || s.is_active == 1, class: "sr-only peer" %>
|
||||||
|
<div class="toggle_style"></div>
|
||||||
|
<div class="btn-toggle-round"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 삭제 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-center space-x-0 space-y-2 lg:space-x-4 lg:space-y-0 h-full relative">
|
||||||
|
<div class=""> </div>
|
||||||
|
<%= link_to schedule_path(s),
|
||||||
|
data: { turbo_method: :delete, turbo_confirm: "정말 삭제하시겠습니까?" },
|
||||||
|
class: "btn bg-danger text-sm flex items-center px-2 py-0 my-1.5 lg:px-4 lg:py-1.5 lg:my-0" do %>
|
||||||
|
<div class="hidden lg:block">삭제</div>
|
||||||
|
<div class="block lg:hidden"><i class="fa-solid fa-xmark"></i></div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<li class="text-center p-4 w-full">저장된 스케줄이 없습니다.</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="flex 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>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
74
app/views/schedules/view.html.erb
Normal file
74
app/views/schedules/view.html.erb
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<div class="flex flex-col h-full divide-y divide-base-border lg:rounded-lg bg-base-third border border-table-border">
|
||||||
|
<div class="flex flex-col flex-1 space-y-4 overflow-y-hidden">
|
||||||
|
<div class="base-list">
|
||||||
|
<div class="flex justify-between p-4">
|
||||||
|
<div class="text-2xl font-bold leading-10"><%= @controller.name %> 컨트롤러</div>
|
||||||
|
<%= link_to "수정", schedule_edit_schedule_path(@controller.id), class: "btn bg-default-slate" %>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<% if @schedule.present? %>
|
||||||
|
<% @schedule.each do |s| %>
|
||||||
|
<li>
|
||||||
|
<div class="flex items-center space-x-6">
|
||||||
|
<!-- 시간 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-2">
|
||||||
|
<div>시간</div>
|
||||||
|
<div class="text-3xl flex items-baseline space-x-1">
|
||||||
|
<div class="text-center w-[36px]"><%= s.hour.to_s.rjust(2, '0') %></div>
|
||||||
|
<div>:</div>
|
||||||
|
<div class="text-center w-[36px]"><%= s.minute.to_s.rjust(2, '0') %></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 온도 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-4">
|
||||||
|
<div>온도</div>
|
||||||
|
<div class="text-3xl flex items-end space-x-2">
|
||||||
|
<div class="text-left w-[50px]"><%= s.temperature %></div>
|
||||||
|
<div class="text-base leading-7">°C</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 사용 여부 -->
|
||||||
|
<div class="flex flex-col lg:flex-row items-baseline space-x-0 lg:space-x-4 items-end">
|
||||||
|
<div class="mb-1">ON/OFF</div>
|
||||||
|
<div class="text-3xl w-8 mr-0">
|
||||||
|
<%#= s.is_active %>
|
||||||
|
<% if s.is_active %>
|
||||||
|
<i class="fa-solid fa-check text-notice"></i>
|
||||||
|
<% else %>
|
||||||
|
<i class="fa-solid fa-xmark text-danger"></i>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<li class="text-center p-4 w-full">저장된 스케줄이 없습니다.</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- <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>
|
||||||
@@ -28,5 +28,7 @@ module FarmitryHvac
|
|||||||
#
|
#
|
||||||
# config.time_zone = "Central Time (US & Canada)"
|
# config.time_zone = "Central Time (US & Canada)"
|
||||||
# config.eager_load_paths << Rails.root.join("extras")
|
# config.eager_load_paths << Rails.root.join("extras")
|
||||||
|
|
||||||
|
config.time_zone = "Asia/Seoul"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -10,14 +10,47 @@ Rails.application.routes.draw do
|
|||||||
# get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker
|
# get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker
|
||||||
|
|
||||||
# Defines the root path route ("/")
|
# Defines the root path route ("/")
|
||||||
root "modbus#index"
|
|
||||||
|
|
||||||
resources :modbus do
|
root to: redirect("/environment?id=1")
|
||||||
collection do
|
|
||||||
post "start"
|
resources :schedules do
|
||||||
post "stop"
|
member do
|
||||||
|
get "view"
|
||||||
get "schedule_edit"
|
get "schedule_edit"
|
||||||
|
post "reset"
|
||||||
|
end
|
||||||
|
collection do
|
||||||
post "schedule_edit_update"
|
post "schedule_edit_update"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources :modals do
|
||||||
|
collection do
|
||||||
|
post :open
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
resources :environment do
|
||||||
|
member do
|
||||||
|
get "edit_temp"
|
||||||
|
get "edit_humidity"
|
||||||
|
get "edit_co2"
|
||||||
|
get "edit_area"
|
||||||
|
end
|
||||||
|
collection do
|
||||||
|
post "edit_temp_update"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
resources :nutrient do
|
||||||
|
member do
|
||||||
|
get "edit_ph"
|
||||||
|
get "edit_ec"
|
||||||
|
get "edit_mixa"
|
||||||
|
get "edit_mixb"
|
||||||
|
end
|
||||||
|
collection do
|
||||||
|
post "edit_temp_update"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
resources :rack
|
||||||
end
|
end
|
||||||
|
|||||||
10
db/migrate/20250416131430_create_controllers.rb
Normal file
10
db/migrate/20250416131430_create_controllers.rb
Normal file
@@ -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
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
class CreateSchedules < ActiveRecord::Migration[8.0]
|
class CreateSchedules < ActiveRecord::Migration[8.0]
|
||||||
def change
|
def change
|
||||||
create_table :schedules do |t|
|
create_table :schedules do |t|
|
||||||
|
t.references :controller, null: false, foreign_key: true
|
||||||
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, [ :controller_id, :hour, :minute ], unique: true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
20
db/schema.rb
generated
20
db/schema.rb
generated
@@ -11,10 +11,24 @@
|
|||||||
# It's strongly recommended that you check this file into your version control system.
|
# 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
|
ActiveRecord::Schema[8.0].define(version: 2025_04_16_131440) do
|
||||||
create_table "schedules", force: :cascade do |t|
|
create_table "controllers", force: :cascade do |t|
|
||||||
t.integer "hour"
|
t.string "name"
|
||||||
t.float "temperature"
|
t.integer "station_id"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
end
|
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"
|
||||||
|
end
|
||||||
|
|
||||||
|
add_foreign_key "schedules", "controllers"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
# MovieGenre.find_or_create_by!(name: genre_name)
|
# MovieGenre.find_or_create_by!(name: genre_name)
|
||||||
# end
|
# end
|
||||||
|
|
||||||
|
Controller.create(name: "3번방", station_id: 7)
|
||||||
|
Controller.create(name: "2번방", station_id: 8)
|
||||||
|
|
||||||
(0..23).each do | h |
|
(0..23).each do | h |
|
||||||
Schedule.create!(hour: h, 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
|
end
|
||||||
|
|||||||
34
modbus.rb
Normal file
34
modbus.rb
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
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
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
"@hotwired/stimulus": "^3.2.2",
|
"@hotwired/stimulus": "^3.2.2",
|
||||||
"@hotwired/turbo-rails": "^8.0.13",
|
"@hotwired/turbo-rails": "^8.0.13",
|
||||||
"@tailwindcss/cli": "^4.1.4",
|
"@tailwindcss/cli": "^4.1.4",
|
||||||
"tailwindcss": "^4.1.4"
|
"tailwindcss": "^4.1.4",
|
||||||
|
"stimulus-use": "^0.52.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
sms/config.json
Executable file
7
sms/config.json
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"api_key": "NCSK1QGPTBWMUKBO",
|
||||||
|
"api_secret": "RSGJ20CGUOTRKK33RFONFUFHY2SWMU9X",
|
||||||
|
"protocol": "https",
|
||||||
|
"domain": "api.coolsms.co.kr",
|
||||||
|
"prefix": ""
|
||||||
|
}
|
||||||
84
sms/request.rb
Executable file
84
sms/request.rb
Executable file
@@ -0,0 +1,84 @@
|
|||||||
|
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
Executable file
11
sms/sms.rb
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
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
|
||||||
7
start.sh
Executable file
7
start.sh
Executable 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
|
||||||
12
test.rb
Normal file
12
test.rb
Normal 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
test/controllers/environment_controller_test.rb
Normal file
7
test/controllers/environment_controller_test.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class EnvironmentControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
# test "the truth" do
|
||||||
|
# assert true
|
||||||
|
# end
|
||||||
|
end
|
||||||
7
test/controllers/modals_controller_test.rb
Normal file
7
test/controllers/modals_controller_test.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class ModalsControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
# test "the truth" do
|
||||||
|
# assert true
|
||||||
|
# end
|
||||||
|
end
|
||||||
7
test/controllers/nutrient_controller_test.rb
Normal file
7
test/controllers/nutrient_controller_test.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class NutrientControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
# test "the truth" do
|
||||||
|
# assert true
|
||||||
|
# end
|
||||||
|
end
|
||||||
7
test/controllers/rack_controller_test.rb
Normal file
7
test/controllers/rack_controller_test.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class RackControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
# test "the truth" do
|
||||||
|
# assert true
|
||||||
|
# end
|
||||||
|
end
|
||||||
7
test/controllers/schedules_controller_test.rb
Normal file
7
test/controllers/schedules_controller_test.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class SchedulesControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
# test "the truth" do
|
||||||
|
# assert true
|
||||||
|
# end
|
||||||
|
end
|
||||||
11
test/fixtures/controllers.yml
vendored
Normal file
11
test/fixtures/controllers.yml
vendored
Normal file
@@ -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
|
||||||
7
test/models/controller_test.rb
Normal file
7
test/models/controller_test.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class ControllerTest < ActiveSupport::TestCase
|
||||||
|
# test "the truth" do
|
||||||
|
# assert true
|
||||||
|
# end
|
||||||
|
end
|
||||||
@@ -571,6 +571,11 @@ picomatch@^2.3.1:
|
|||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||||
|
|
||||||
|
stimulus-use@^0.52.2:
|
||||||
|
version "0.52.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/stimulus-use/-/stimulus-use-0.52.3.tgz#d6f35fa93277274957a2ed98a7b04b4d702cb1d6"
|
||||||
|
integrity sha512-stZ5dID6FUrGCR/ChWUa0FT5Z8iqkzT6lputOAb50eF+Ayg7RzJj4U/HoRlp2NV333QfvoRidru9HLbom4hZVw==
|
||||||
|
|
||||||
tailwindcss@4.1.4, tailwindcss@^4.1.4:
|
tailwindcss@4.1.4, tailwindcss@^4.1.4:
|
||||||
version "4.1.4"
|
version "4.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.1.4.tgz#27b3c910c6f1a47f4540451f3faf7cdd6d977a69"
|
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.1.4.tgz#27b3c910c6f1a47f4540451f3faf7cdd6d977a69"
|
||||||
|
|||||||
Reference in New Issue
Block a user