Browse Source

save null 값 허용

v2
원형 1 year ago
parent
commit
5696c826e9
  1. 1
      src/main/java/org/mobidgim/mobidigimproject/model/device/enums/CanBusType.java
  2. 5
      src/main/java/org/mobidgim/mobidigimproject/model/device/enums/CanSpeed.java
  3. 1
      src/main/java/org/mobidgim/mobidigimproject/model/device/enums/ImuRemap.java
  4. 1
      src/main/java/org/mobidgim/mobidigimproject/model/device/enums/ImuRemapSign.java
  5. 5
      src/main/java/org/mobidgim/mobidigimproject/model/device/enums/Rs485Baudrate.java
  6. 1
      src/main/java/org/mobidgim/mobidigimproject/model/device/enums/Rs485Mode.java
  7. 245
      src/main/resources/static/index.html
  8. 218
      src/main/resources/static/style.css

1
src/main/java/org/mobidgim/mobidigimproject/model/device/enums/CanBusType.java

@ -5,6 +5,7 @@ import lombok.AllArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
public enum CanBusType { public enum CanBusType {
NONE(""),
EXTENDED("extended"), EXTENDED("extended"),
STANDARD("standard"); STANDARD("standard");
private final String Type; private final String Type;

5
src/main/java/org/mobidgim/mobidigimproject/model/device/enums/CanSpeed.java

@ -5,15 +5,16 @@ import lombok.AllArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
public enum CanSpeed { public enum CanSpeed {
NONE(null),
CAN_SPEED_1000(1000), CAN_SPEED_1000(1000),
CAN_SPEED_2500(2500), CAN_SPEED_2500(2500),
CAN_SPEED_5000(5000), CAN_SPEED_5000(5000),
CAN_SPEED_10000(10000); CAN_SPEED_10000(10000);
private final int value; private final Integer value;
@JsonValue @JsonValue
public int getValue() { public Integer getValue() {
return value; return value;
} }
} }

1
src/main/java/org/mobidgim/mobidigimproject/model/device/enums/ImuRemap.java

@ -6,6 +6,7 @@ import lombok.AllArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
public enum ImuRemap { public enum ImuRemap {
NONE(""),
X("x"), X("x"),
Y("y"), Y("y"),
Z("z"); Z("z");

1
src/main/java/org/mobidgim/mobidigimproject/model/device/enums/ImuRemapSign.java

@ -5,6 +5,7 @@ import lombok.AllArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
public enum ImuRemapSign { public enum ImuRemapSign {
NONE(""),
MINUS("minus"), MINUS("minus"),
PLUS("plus"); PLUS("plus");
private final String sign; private final String sign;

5
src/main/java/org/mobidgim/mobidigimproject/model/device/enums/Rs485Baudrate.java

@ -5,13 +5,14 @@ import lombok.AllArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
public enum Rs485Baudrate { public enum Rs485Baudrate {
NONE(null),
MODBUS_SPEED_9600(9600), MODBUS_SPEED_9600(9600),
MODBUS_SPEED_115200(115200); MODBUS_SPEED_115200(115200);
private final int value; private final Integer value;
@JsonValue @JsonValue
public int getValue() { public Integer getValue() {
return value; return value;
} }
} }

1
src/main/java/org/mobidgim/mobidigimproject/model/device/enums/Rs485Mode.java

@ -5,6 +5,7 @@ import lombok.AllArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
public enum Rs485Mode { public enum Rs485Mode {
NONE(""),
HALF_DUPLEX("half"), HALF_DUPLEX("half"),
FULL_DUPLEX("full"); FULL_DUPLEX("full");
private final String value; private final String value;

245
src/main/resources/static/index.html

@ -4,228 +4,7 @@
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Device Configuration UI</title> <title>Device Configuration UI</title>
<link rel="stylesheet" href="style.css"/>
<style>
* {margin: 0;padding: 0;box-sizing: border-box}
body {font-family: "Pretendard", sans-serif;min-height: 100vh;display: flex;background: #fff;color: #111}
/* ───── Sidebar ───── */
.sidebar {width: 260px;border-right: 1px solid #ddd;padding: 32px 24px;display: flex;flex-direction: column}
.sidebar h2 {font-size: 24px;margin-bottom: 32px}
details.nav {
margin-bottom: 24px
}
.nav > summary {
list-style: none;
cursor: pointer;
display: flex;
align-items: center;
font-size: 20px
}
.nav > summary::before {
content: "";
width: 8px;
height: 8px;
margin-right: 8px;
border-left: 2px solid #111;
border-bottom: 2px solid #111;
transform: rotate(-135deg);
transition: .2s
}
.nav[open] > summary::before {
transform: rotate(-45deg)
}
.nav-items {
display: flex;
flex-direction: column;
margin-top: 10px;
padding-left: 16px;
gap: 6px
}
.nav-items button {
background: none;
border: none;
cursor: pointer;
font-size: 16px;
text-align: left;
padding: 4px 0
}
/* ───── Top-bar ───── */
.topbar {
height: 68px;
background: #d9d9d9;
display: flex;
align-items: center;
padding: 0 32px;
font-size: 28px;
font-weight: 500;
box-shadow: 0 2px 4px rgba(0, 0, 0, .15) inset
}
/* ───── Main Board ───── */
.content {
flex: 1;
display: flex;
flex-direction: column
}
.board {
flex: 1;
overflow: auto;
padding: 40px;
display: flex;
flex-direction: column;
gap: 32px
}
.card {
background: #fff;
border: 1px solid #e5e5e5;
box-shadow: 0 2px 4px rgba(0, 0, 0, .15) inset;
padding: 32px;
border-radius: 8px
}
.card h3 {
font-size: 24px;
margin-bottom: 24px
}
/* SSID rows */
.ssid-row {
display: grid;
grid-template-columns:220px 220px 60px 120px;
gap: 16px;
margin-bottom: 12px
}
.ssid-row input, .ssid-row select {
padding: 6px 8px;
font-size: 16px;
border: 1px solid #aaa;
border-radius: 4px;
width: 100%
}
.ssid-row button {padding: 6px 10px;font-size: 14px;background: #f1f1f1;border: 1px solid #777;border-radius: 4px;cursor: pointer}
/* 3-column grids (Network / Server) */
.grid-3-col {display: grid;grid-template-columns:180px 240px 120px;align-items: center;gap: 16px}
.grid-3-col input {padding: 6px 8px;font-size: 16px;border: 1px solid #aaa;border-radius: 1px;width: 100%}
/* radio */
.radio-group {display: flex;align-items: center;gap: 24px}
.radio-label {display: inline-flex;align-items: center;gap: 4px;cursor: pointer}
.radio-label input {margin: 0}
/* I/O */
.io-top {display: grid;grid-template-columns:repeat(auto-fit, minmax(360px, 1fr));gap: 40px;margin-bottom: 32px}
.io-bot {display: grid; /* Ethernet·LTE와 같은 2열(가변) 그리드 */grid-template-columns:repeat(auto-fit, minmax(360px, 1fr));gap: 40px;}
.io-group {display: flex;flex-direction: column;gap: 8px}
.io-group h4 {font-size: 20px;font-weight: 600;margin-bottom: 16px;display: flex;align-items: center;gap: 8px}
.io-group h4::before {content: "";width: 8px;height: 8px;background: #aaa;display: inline-block}
.io-fieldset {border: 1px solid #bbb;padding: 24px 32px;border-radius: 4px;display: flex;flex-direction: column;gap: 16px;width: 100%}
.io-row {display: grid;grid-template-columns:120px 12px 1fr;align-items: center;gap: 8px}
.io-row input, .io-row select {padding: 4px 6px;border: 1px solid #aaa;border-radius: 4px;font-size: 14px;width: 50%}
.io-row span.label {font-size: 14px;color: #333;display: block;width: 100%;}
.io-row span.label-inline {font-size: 14px;color: #333;display: inline;width: auto}
.io-row span.colon {font-size: 14px;color: #333;text-align: center;}
.reg-io-grid {display: grid;grid-template-columns: 1fr 1fr;gap: 4px}
.reg-io-grid .io-fieldset .io-row {display: grid;grid-template-columns: 140px 220px;align-items: center;gap: 4px;margin-bottom: 8px;}
/* config 내부 라벨 */
.reg-io-grid .io-fieldset .io-row span.label {font-size: 14px;color: #333;}
.reg-io-grid .io-fieldset .io-row input,
.reg-io-grid .io-fieldset .io-row select {padding: 4px 6px;border: 1px solid #aaa;border-radius: 4px;font-size: 14px;width: 100%;box-sizing: border-box;}
.reg-io-grid .io-fieldset .io-row.checkbox-group {display: flex;align-items: center;gap: 16px;grid-column: 2 / 3;}
.reg-io-grid .io-fieldset .io-row .checkbox-with-text {display: flex;align-items: center;gap: 10px;width: 100%;}
.reg-io-grid .io-fieldset .io-row .checkbox-with-text input[type="checkbox"] {width: auto;margin: 0;}
/* 비활성 input 시각화 */
.disabled {background: #f5f5f5;cursor: not-allowed}
/* Footer Buttons */
.actions {display: flex;gap: 24px;justify-content: center;padding: 20px}
.actions button {min-width: 140px;padding: 10px 18px;font-size: 16px;border: 1px solid #bbb;background: #d9d9d9;border-radius: 4px;cursor: pointer}
.config-actions{display: flex;gap: 24px;justify-content: center;background: #f7f7f7;border: 1px solid #bbb;border-radius: 4px;margin: 24px 0 0 0;padding: 20px 0;}
.config-actions button{min-width:140px;padding:10px 18px;font-size:16px;border:1px solid #bbb;background:#d9d9d9;border-radius:4px;cursor:pointer}
.topbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 32px;
height: 68px;
background: #d9d9d9;
font-size: 28px;
font-weight: 500;
box-shadow: 0 2px 4px rgba(0, 0, 0, .15) inset;
}
.save-file {
font-size: 16px;
padding: 8px 16px;
background-color: #4caf50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* MODBUS 화면 심플 스타일 */
#page-modbus .card {background: #f7f7f7;border: 1px solid #bbb;box-shadow: none;padding: 24px;border-radius: 4px;display: flex;flex-direction: column;}
#page-modbus table {width: 100%;border-collapse: collapse;background: #fff;border: 1px solid #bbb;}
#page-modbus th, #page-modbus td {border: 1px solid #bbb;padding: 6px 8px;text-align: left;background: #fff;}
#page-modbus th {background: #e5e5e5;font-weight: 500;}
#page-modbus .modbus-input-area {display: grid;grid-template-columns: 200px 200px 200px 200px;gap: 20px 20px;margin: 24px auto 16px auto;background: #eaeaea;padding: 16px;border-radius: 4px;margin-left: auto;margin-right: auto;}
#page-modbus .input-row {display: contents;}
#page-modbus .modbus-input-area label.nowrap {white-space: nowrap;}
#page-modbus .modbus-input-area label {min-width: auto;color: #444;white-space: normal;}
#page-modbus .modbus-input-area input {background: #e0e0e0;border: none;border-radius: 2px;padding: 4px 8px;min-width: auto;}
#page-modbus .modbus-actions {display: flex;gap: 24px;justify-content: center;margin: 16px 0 0 0;}
#page-modbus .modbus-actions button {background: #d3d3d3;border: 1px solid #aaa;border-radius: 2px;padding: 8px 24px;font-size: 16px;cursor: pointer;}
/* 하단 버튼 영역 */
#modbus-bottom-actions {display: flex;gap: 24px;justify-content: center;background: #f7f7f7;border: 1px solid #bbb;border-radius: 4px;margin: 24px 0 0 0;padding: 20px 0;}
#modbus-bottom-actions button {background: #e0e0e0;border: 1px solid #aaa;border-radius: 2px;padding: 10px 18px;font-size: 16px;min-width: 140px;cursor: pointer;}
/* OPC_UA 화면 */
#page-opcua .card {background: #f7f7f7;border: 1px solid #bbb;box-shadow: none;padding: 24px;border-radius: 4px;display: flex;flex-direction: column;}
#page-opcua table {width: 100%;border-collapse: collapse;background: #fff;border: 1px solid #bbb;}
#page-opcua th, #page-opcua td {border: 1px solid #bbb;padding: 6px 8px;text-align: left;background: #fff;}
#page-opcua th {background: #e5e5e5;font-weight: 500;}
#page-opcua .opcua-input-area {display: grid;grid-template-columns: 200px 200px 200px 200px;gap: 20px 20px;margin: 24px auto 16px auto;background: #eaeaea;padding: 16px;border-radius: 4px;margin-left: auto;margin-right: auto;}
#page-opcua .input-row {display: contents;}
#page-opcua .opcua-input-area label.nowrap {white-space: nowrap;}
#page-opcua .opcua-input-area label {min-width: auto;color: #444;white-space: normal;}
#page-opcua .opcua-input-area input {background: #e0e0e0;border: none;border-radius: 2px;padding: 4px 8px;min-width: auto;}
#page-opcua .opcua-actions {display: flex;gap: 24px;justify-content: center;margin: 16px 0 0 0;}
#page-opcua .opcua-actions button {background: #d3d3d3;border: 1px solid #aaa;border-radius: 2px;padding: 8px 24px;font-size: 16px;cursor: pointer;}
#opcUa-bottom-actions {display: flex;gap: 24px;justify-content: center;background: #f7f7f7;border: 1px solid #bbb;border-radius: 4px;margin: 24px 0 0 0;padding: 20px 0;}
#opcUa-bottom-actions button {background: #e0e0e0;border: 1px solid #aaa;border-radius: 2px;padding: 10px 18px;font-size: 16px;min-width: 140px;cursor: pointer;}
/* CAN 화면 스타일 */
#page-can .card {background: #f7f7f7;border: 1px solid #bbb;box-shadow: none;padding: 24px;border-radius: 4px;display: flex;flex-direction: column;}
#page-can table {width: 100%;border-collapse: collapse;background: #fff;border: 1px solid #bbb;}
#page-can th, #page-can td {border: 1px solid #bbb;padding: 6px 8px;text-align: left;background: #fff;}
#page-can th {background: #e5e5e5;font-weight: 500;}
#page-can .can-input-area {display: grid;grid-template-columns: 200px 200px 200px 200px;gap: 20px 20px;margin: 24px auto 16px auto;background: #eaeaea;padding: 16px;border-radius: 4px;margin-left: auto;margin-right: auto;}
#page-can .input-row {display: contents;}
#page-can .can-input-area label.nowrap {white-space: nowrap;}
#page-can .can-input-area label {min-width: auto;color: #444;white-space: normal;}
#page-can .can-input-area input {background: #e0e0e0;border: none;border-radius: 2px;padding: 4px 8px;min-width: auto;}
#page-can .can-actions {display: flex;gap: 24px;justify-content: center;margin: 16px 0 0 0;}
#page-can .can-actions button {background: #d3d3d3;border: 1px solid #aaa;border-radius: 2px;padding: 8px 24px;font-size: 16px;cursor: pointer;}
#can-bottom-actions {display: flex;gap: 24px;justify-content: center;background: #f7f7f7;border: 1px solid #bbb;border-radius: 4px;margin: 24px 0 0 0;padding: 20px 0;}
#can-bottom-actions button {background: #e0e0e0;border: 1px solid #aaa;border-radius: 2px;padding: 10px 18px;font-size: 16px;min-width: 140px;cursor: pointer;}
.selected-row {background: #d0e6ff !important;}
.selected-row > td {background: #d0e6ff !important;}
.file-upload-section{padding: 10px;margin: 10px;text-align: center;}
</style>
</head> </head>
<body> <body>
<!-- ───── Sidebar ───── --> <!-- ───── Sidebar ───── -->
@ -870,7 +649,6 @@
<!-- ───── Script ───── --> <!-- ───── Script ───── -->
<script> <script>
const data = { const data = {
/* Wi-Fi */ /* Wi-Fi */
wifi_static: 'off', wifi_ip: '', wifi_netmask: '', wifi_gateway: '', wifi_dns1: '', wifi_dns2: '', wifi_static: 'off', wifi_ip: '', wifi_netmask: '', wifi_gateway: '', wifi_dns1: '', wifi_dns2: '',
@ -1294,21 +1072,30 @@
res.wifi_static = document.querySelector('input[name="ipMode"]:checked')?.value === 'static' res.wifi_static = document.querySelector('input[name="ipMode"]:checked')?.value === 'static'
? 'on' : 'off'; ? 'on' : 'off';
/* 일반 input/select */ // 일반 input/select 처리
for (const k in res) { for (const k in res) {
const el = document.getElementById(k); const el = document.getElementById(k);
if (el) res[k] = el.value.trim(); if (el) {
const val = el.value.trim();
// can_baudrate, rs485_baudrate는 숫자 or null
if (['can_baudrate', 'rs485_baudrate'].includes(k)) {
res[k] = val === '' ? null : Number(val);
} else {
res[k] = val;
}
}
} }
/* IMU select → 축·부호 분리 */ // IMU select → 축·부호 분리
['x', 'y', 'z'].forEach(a => { ['x', 'y', 'z'].forEach(a => {
const { axis, sign } = axisValueToPair(document.getElementById(`imu_remap_${a}`).value); const { axis, sign } = axisValueToPair(document.getElementById(`imu_remap_${a}`).value);
res[`imu_remap_${a}`] = axis; res[`imu_remap_${a}`] = axis;
res[`imu_remap_${a}_sign`] = sign; res[`imu_remap_${a}_sign`] = sign;
}); });
/* SSID 배열 */ // SSID 배열
res.WIFI_SSID = gatherSsidRows(); res.WIFI_SSID = gatherSsidRows();
return res; return res;
} }
@ -2130,10 +1917,6 @@
document.getElementById('can_mask_input').value = tr.querySelector('.can-mask-display').textContent; document.getElementById('can_mask_input').value = tr.querySelector('.can-mask-display').textContent;
}); });
}); });
</script> </script>
</body> </body>
</html> </html>

218
src/main/resources/static/style.css

@ -0,0 +1,218 @@
* {margin: 0;padding: 0;box-sizing: border-box}
body {font-family: "Pretendard", sans-serif;min-height: 100vh;display: flex;background: #fff;color: #111}
/* ───── Sidebar ───── */
.sidebar {width: 260px;border-right: 1px solid #ddd;padding: 32px 24px;display: flex;flex-direction: column}
.sidebar h2 {font-size: 24px;margin-bottom: 32px}
details.nav {
margin-bottom: 24px
}
.nav > summary {
list-style: none;
cursor: pointer;
display: flex;
align-items: center;
font-size: 20px
}
.nav > summary::before {
content: "";
width: 8px;
height: 8px;
margin-right: 8px;
border-left: 2px solid #111;
border-bottom: 2px solid #111;
transform: rotate(-135deg);
transition: .2s
}
.nav[open] > summary::before {
transform: rotate(-45deg)
}
.nav-items {
display: flex;
flex-direction: column;
margin-top: 10px;
padding-left: 16px;
gap: 6px
}
.nav-items button {
background: none;
border: none;
cursor: pointer;
font-size: 16px;
text-align: left;
padding: 4px 0
}
/* ───── Top-bar ───── */
.topbar {
height: 68px;
background: #d9d9d9;
display: flex;
align-items: center;
padding: 0 32px;
font-size: 28px;
font-weight: 500;
box-shadow: 0 2px 4px rgba(0, 0, 0, .15) inset
}
/* ───── Main Board ───── */
.content {
flex: 1;
display: flex;
flex-direction: column
}
.board {
flex: 1;
overflow: auto;
padding: 40px;
display: flex;
flex-direction: column;
gap: 32px
}
.card {
background: #fff;
border: 1px solid #e5e5e5;
box-shadow: 0 2px 4px rgba(0, 0, 0, .15) inset;
padding: 32px;
border-radius: 8px
}
.card h3 {
font-size: 24px;
margin-bottom: 24px
}
/* SSID rows */
.ssid-row {
display: grid;
grid-template-columns:220px 220px 60px 120px;
gap: 16px;
margin-bottom: 12px
}
.ssid-row input, .ssid-row select {
padding: 6px 8px;
font-size: 16px;
border: 1px solid #aaa;
border-radius: 4px;
width: 100%
}
.ssid-row button {padding: 6px 10px;font-size: 14px;background: #f1f1f1;border: 1px solid #777;border-radius: 4px;cursor: pointer}
/* 3-column grids (Network / Server) */
.grid-3-col {display: grid;grid-template-columns:180px 240px 120px;align-items: center;gap: 16px}
.grid-3-col input {padding: 6px 8px;font-size: 16px;border: 1px solid #aaa;border-radius: 1px;width: 100%}
/* radio */
.radio-group {display: flex;align-items: center;gap: 24px}
.radio-label {display: inline-flex;align-items: center;gap: 4px;cursor: pointer}
.radio-label input {margin: 0}
/* I/O */
.io-top {display: grid;grid-template-columns:repeat(auto-fit, minmax(360px, 1fr));gap: 40px;margin-bottom: 32px}
.io-bot {display: grid; /* Ethernet·LTE와 같은 2열(가변) 그리드 */grid-template-columns:repeat(auto-fit, minmax(360px, 1fr));gap: 40px;}
.io-group {display: flex;flex-direction: column;gap: 8px}
.io-group h4 {font-size: 20px;font-weight: 600;margin-bottom: 16px;display: flex;align-items: center;gap: 8px}
.io-group h4::before {content: "";width: 8px;height: 8px;background: #aaa;display: inline-block}
.io-fieldset {border: 1px solid #bbb;padding: 24px 32px;border-radius: 4px;display: flex;flex-direction: column;gap: 16px;width: 100%}
.io-row {display: grid;grid-template-columns:120px 12px 1fr;align-items: center;gap: 8px}
.io-row input, .io-row select {padding: 4px 6px;border: 1px solid #aaa;border-radius: 4px;font-size: 14px;width: 50%}
.io-row span.label {font-size: 14px;color: #333;display: block;width: 100%;}
.io-row span.label-inline {font-size: 14px;color: #333;display: inline;width: auto}
.io-row span.colon {font-size: 14px;color: #333;text-align: center;}
.reg-io-grid {display: grid;grid-template-columns: 1fr 1fr;gap: 4px}
.reg-io-grid .io-fieldset .io-row {display: grid;grid-template-columns: 140px 220px;align-items: center;gap: 4px;margin-bottom: 8px;}
/* config 내부 라벨 */
.reg-io-grid .io-fieldset .io-row span.label {font-size: 14px;color: #333;}
.reg-io-grid .io-fieldset .io-row input,
.reg-io-grid .io-fieldset .io-row select {padding: 4px 6px;border: 1px solid #aaa;border-radius: 4px;font-size: 14px;width: 100%;box-sizing: border-box;}
.reg-io-grid .io-fieldset .io-row.checkbox-group {display: flex;align-items: center;gap: 16px;grid-column: 2 / 3;}
.reg-io-grid .io-fieldset .io-row .checkbox-with-text {display: flex;align-items: center;gap: 10px;width: 100%;}
.reg-io-grid .io-fieldset .io-row .checkbox-with-text input[type="checkbox"] {width: auto;margin: 0;}
/* 비활성 input 시각화 */
.disabled {background: #f5f5f5;cursor: not-allowed}
/* Footer Buttons */
.actions {display: flex;gap: 24px;justify-content: center;padding: 20px}
.actions button {min-width: 140px;padding: 10px 18px;font-size: 16px;border: 1px solid #bbb;background: #d9d9d9;border-radius: 4px;cursor: pointer}
.config-actions{display: flex;gap: 24px;justify-content: center;background: #f7f7f7;border: 1px solid #bbb;border-radius: 4px;margin: 24px 0 0 0;padding: 20px 0;}
.config-actions button{min-width:140px;padding:10px 18px;font-size:16px;border:1px solid #bbb;background:#d9d9d9;border-radius:4px;cursor:pointer}
.topbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 32px;
height: 68px;
background: #d9d9d9;
font-size: 28px;
font-weight: 500;
box-shadow: 0 2px 4px rgba(0, 0, 0, .15) inset;
}
.save-file {
font-size: 16px;
padding: 8px 16px;
background-color: #4caf50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* MODBUS 화면 심플 스타일 */
#page-modbus .card {background: #f7f7f7;border: 1px solid #bbb;box-shadow: none;padding: 24px;border-radius: 4px;display: flex;flex-direction: column;}
#page-modbus table {width: 100%;border-collapse: collapse;background: #fff;border: 1px solid #bbb;}
#page-modbus th, #page-modbus td {border: 1px solid #bbb;padding: 6px 8px;text-align: left;background: #fff;}
#page-modbus th {background: #e5e5e5;font-weight: 500;}
#page-modbus .modbus-input-area {display: grid;grid-template-columns: 200px 200px 200px 200px;gap: 20px 20px;margin: 24px auto 16px auto;background: #eaeaea;padding: 16px;border-radius: 4px;margin-left: auto;margin-right: auto;}
#page-modbus .input-row {display: contents;}
#page-modbus .modbus-input-area label.nowrap {white-space: nowrap;}
#page-modbus .modbus-input-area label {min-width: auto;color: #444;white-space: normal;}
#page-modbus .modbus-input-area input {background: #e0e0e0;border: none;border-radius: 2px;padding: 4px 8px;min-width: auto;}
#page-modbus .modbus-actions {display: flex;gap: 24px;justify-content: center;margin: 16px 0 0 0;}
#page-modbus .modbus-actions button {background: #d3d3d3;border: 1px solid #aaa;border-radius: 2px;padding: 8px 24px;font-size: 16px;cursor: pointer;}
/* 하단 버튼 영역 */
#modbus-bottom-actions {display: flex;gap: 24px;justify-content: center;background: #f7f7f7;border: 1px solid #bbb;border-radius: 4px;margin: 24px 0 0 0;padding: 20px 0;}
#modbus-bottom-actions button {background: #e0e0e0;border: 1px solid #aaa;border-radius: 2px;padding: 10px 18px;font-size: 16px;min-width: 140px;cursor: pointer;}
/* OPC_UA 화면 */
#page-opcua .card {background: #f7f7f7;border: 1px solid #bbb;box-shadow: none;padding: 24px;border-radius: 4px;display: flex;flex-direction: column;}
#page-opcua table {width: 100%;border-collapse: collapse;background: #fff;border: 1px solid #bbb;}
#page-opcua th, #page-opcua td {border: 1px solid #bbb;padding: 6px 8px;text-align: left;background: #fff;}
#page-opcua th {background: #e5e5e5;font-weight: 500;}
#page-opcua .opcua-input-area {display: grid;grid-template-columns: 200px 200px 200px 200px;gap: 20px 20px;margin: 24px auto 16px auto;background: #eaeaea;padding: 16px;border-radius: 4px;margin-left: auto;margin-right: auto;}
#page-opcua .input-row {display: contents;}
#page-opcua .opcua-input-area label.nowrap {white-space: nowrap;}
#page-opcua .opcua-input-area label {min-width: auto;color: #444;white-space: normal;}
#page-opcua .opcua-input-area input {background: #e0e0e0;border: none;border-radius: 2px;padding: 4px 8px;min-width: auto;}
#page-opcua .opcua-actions {display: flex;gap: 24px;justify-content: center;margin: 16px 0 0 0;}
#page-opcua .opcua-actions button {background: #d3d3d3;border: 1px solid #aaa;border-radius: 2px;padding: 8px 24px;font-size: 16px;cursor: pointer;}
#opcUa-bottom-actions {display: flex;gap: 24px;justify-content: center;background: #f7f7f7;border: 1px solid #bbb;border-radius: 4px;margin: 24px 0 0 0;padding: 20px 0;}
#opcUa-bottom-actions button {background: #e0e0e0;border: 1px solid #aaa;border-radius: 2px;padding: 10px 18px;font-size: 16px;min-width: 140px;cursor: pointer;}
/* CAN 화면 스타일 */
#page-can .card {background: #f7f7f7;border: 1px solid #bbb;box-shadow: none;padding: 24px;border-radius: 4px;display: flex;flex-direction: column;}
#page-can table {width: 100%;border-collapse: collapse;background: #fff;border: 1px solid #bbb;}
#page-can th, #page-can td {border: 1px solid #bbb;padding: 6px 8px;text-align: left;background: #fff;}
#page-can th {background: #e5e5e5;font-weight: 500;}
#page-can .can-input-area {display: grid;grid-template-columns: 200px 200px 200px 200px;gap: 20px 20px;margin: 24px auto 16px auto;background: #eaeaea;padding: 16px;border-radius: 4px;margin-left: auto;margin-right: auto;}
#page-can .input-row {display: contents;}
#page-can .can-input-area label.nowrap {white-space: nowrap;}
#page-can .can-input-area label {min-width: auto;color: #444;white-space: normal;}
#page-can .can-input-area input {background: #e0e0e0;border: none;border-radius: 2px;padding: 4px 8px;min-width: auto;}
#page-can .can-actions {display: flex;gap: 24px;justify-content: center;margin: 16px 0 0 0;}
#page-can .can-actions button {background: #d3d3d3;border: 1px solid #aaa;border-radius: 2px;padding: 8px 24px;font-size: 16px;cursor: pointer;}
#can-bottom-actions {display: flex;gap: 24px;justify-content: center;background: #f7f7f7;border: 1px solid #bbb;border-radius: 4px;margin: 24px 0 0 0;padding: 20px 0;}
#can-bottom-actions button {background: #e0e0e0;border: 1px solid #aaa;border-radius: 2px;padding: 10px 18px;font-size: 16px;min-width: 140px;cursor: pointer;}
.selected-row {background: #d0e6ff !important;}
.selected-row > td {background: #d0e6ff !important;}
.file-upload-section{padding: 10px;margin: 10px;text-align: center;}
Loading…
Cancel
Save