|
|
@ -6,34 +6,12 @@ |
|
|
<title>Device Configuration UI</title> |
|
|
<title>Device Configuration UI</title> |
|
|
|
|
|
|
|
|
<style> |
|
|
<style> |
|
|
* { |
|
|
* {margin: 0;padding: 0;box-sizing: border-box} |
|
|
margin: 0; |
|
|
body {font-family: "Pretendard", sans-serif;min-height: 100vh;display: flex;background: #fff;color: #111} |
|
|
padding: 0; |
|
|
|
|
|
box-sizing: border-box |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
body { |
|
|
|
|
|
font-family: "Pretendard", sans-serif; |
|
|
|
|
|
min-height: 100vh; |
|
|
|
|
|
display: flex; |
|
|
|
|
|
background: #fff; |
|
|
|
|
|
color: #111 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* ───── Sidebar ───── */ |
|
|
/* ───── Sidebar ───── */ |
|
|
.sidebar { |
|
|
.sidebar {width: 260px;border-right: 1px solid #ddd;padding: 32px 24px;display: flex;flex-direction: column} |
|
|
width: 260px; |
|
|
.sidebar h2 {font-size: 24px;margin-bottom: 32px} |
|
|
border-right: 1px solid #ddd; |
|
|
|
|
|
padding: 32px 24px; |
|
|
|
|
|
display: flex; |
|
|
|
|
|
flex-direction: column |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.sidebar h2 { |
|
|
|
|
|
font-size: 24px; |
|
|
|
|
|
margin-bottom: 32px |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
details.nav { |
|
|
details.nav { |
|
|
margin-bottom: 24px |
|
|
margin-bottom: 24px |
|
|
} |
|
|
} |
|
|
@ -135,519 +113,95 @@ |
|
|
width: 100% |
|
|
width: 100% |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.ssid-row button { |
|
|
.ssid-row button {padding: 6px 10px;font-size: 14px;background: #f1f1f1;border: 1px solid #777;border-radius: 4px;cursor: pointer} |
|
|
padding: 6px 10px; |
|
|
|
|
|
font-size: 14px; |
|
|
|
|
|
background: #f1f1f1; |
|
|
|
|
|
border: 1px solid #777; |
|
|
|
|
|
border-radius: 4px; |
|
|
|
|
|
cursor: pointer |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 3-column grids (Network / Server) */ |
|
|
/* 3-column grids (Network / Server) */ |
|
|
.grid-3-col { |
|
|
.grid-3-col {display: grid;grid-template-columns:180px 240px 120px;align-items: center;gap: 16px} |
|
|
display: grid; |
|
|
.grid-3-col input {padding: 6px 8px;font-size: 16px;border: 1px solid #aaa;border-radius: 1px;width: 100%} |
|
|
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 */ |
|
|
.radio-group { |
|
|
.radio-group {display: flex;align-items: center;gap: 24px} |
|
|
display: flex; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
gap: 24px |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.radio-label { |
|
|
|
|
|
display: inline-flex; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
gap: 4px; |
|
|
|
|
|
cursor: pointer |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.radio-label input { |
|
|
|
|
|
margin: 0 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.radio-label {display: inline-flex;align-items: center;gap: 4px;cursor: pointer} |
|
|
|
|
|
.radio-label input {margin: 0} |
|
|
/* I/O */ |
|
|
/* I/O */ |
|
|
.io-top { |
|
|
.io-top {display: grid;grid-template-columns:repeat(auto-fit, minmax(360px, 1fr));gap: 40px;margin-bottom: 32px} |
|
|
display: grid; |
|
|
.io-bot {display: grid; /* Ethernet·LTE와 같은 2열(가변) 그리드 */grid-template-columns:repeat(auto-fit, minmax(360px, 1fr));gap: 40px;} |
|
|
grid-template-columns:repeat(auto-fit, minmax(360px, 1fr)); |
|
|
.io-group {display: flex;flex-direction: column;gap: 8px} |
|
|
gap: 40px; |
|
|
.io-group h4 {font-size: 20px;font-weight: 600;margin-bottom: 16px;display: flex;align-items: center;gap: 8px} |
|
|
margin-bottom: 32px |
|
|
.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-bot { |
|
|
.io-row input, .io-row select {padding: 4px 6px;border: 1px solid #aaa;border-radius: 4px;font-size: 14px;width: 50%} |
|
|
display: grid; /* Ethernet·LTE와 같은 2열(가변) 그리드 */ |
|
|
.io-row span.label {font-size: 14px;color: #333;display: block;width: 100%;} |
|
|
grid-template-columns:repeat(auto-fit, minmax(360px, 1fr)); |
|
|
.io-row span.label-inline {font-size: 14px;color: #333;display: inline;width: auto} |
|
|
gap: 40px; |
|
|
.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;} |
|
|
.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 내부 라벨 */ |
|
|
/* config 내부 라벨 */ |
|
|
.reg-io-grid .io-fieldset .io-row span.label { |
|
|
.reg-io-grid .io-fieldset .io-row span.label {font-size: 14px;color: #333;} |
|
|
font-size: 14px; |
|
|
|
|
|
color: #333; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.reg-io-grid .io-fieldset .io-row input, |
|
|
.reg-io-grid .io-fieldset .io-row input, |
|
|
.reg-io-grid .io-fieldset .io-row select { |
|
|
.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;} |
|
|
padding: 4px 6px; |
|
|
.reg-io-grid .io-fieldset .io-row.checkbox-group {display: flex;align-items: center;gap: 16px;grid-column: 2 / 3;} |
|
|
border: 1px solid #aaa; |
|
|
.reg-io-grid .io-fieldset .io-row .checkbox-with-text {display: flex;align-items: center;gap: 10px;width: 100%;} |
|
|
border-radius: 4px; |
|
|
.reg-io-grid .io-fieldset .io-row .checkbox-with-text input[type="checkbox"] {width: auto;margin: 0;} |
|
|
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 시각화 */ |
|
|
/* 비활성 input 시각화 */ |
|
|
.disabled { |
|
|
.disabled {background: #f5f5f5;cursor: not-allowed} |
|
|
background: #f5f5f5; |
|
|
|
|
|
cursor: not-allowed |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Footer Buttons */ |
|
|
/* Footer Buttons */ |
|
|
.actions { |
|
|
.actions {display: flex;gap: 24px;justify-content: center;padding: 20px} |
|
|
display: flex; |
|
|
.actions button {min-width: 140px;padding: 10px 18px;font-size: 16px;border: 1px solid #bbb;background: #d9d9d9;border-radius: 4px;cursor: pointer} |
|
|
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{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} |
|
|
.config-actions button{min-width:140px;padding:10px 18px;font-size:16px;border:1px solid #bbb;background:#d9d9d9;border-radius:4px;cursor:pointer} |
|
|
|
|
|
|
|
|
/* MODBUS 화면 심플 스타일 */ |
|
|
/* MODBUS 화면 심플 스타일 */ |
|
|
#page-modbus .card { |
|
|
#page-modbus .card {background: #f7f7f7;border: 1px solid #bbb;box-shadow: none;padding: 24px;border-radius: 4px;display: flex;flex-direction: column;} |
|
|
background: #f7f7f7; |
|
|
#page-modbus table {width: 100%;border-collapse: collapse;background: #fff;border: 1px solid #bbb;} |
|
|
border: 1px solid #bbb; |
|
|
#page-modbus th, #page-modbus td {border: 1px solid #bbb;padding: 6px 8px;text-align: left;background: #fff;} |
|
|
box-shadow: none; |
|
|
#page-modbus th {background: #e5e5e5;font-weight: 500;} |
|
|
padding: 24px; |
|
|
#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;} |
|
|
border-radius: 4px; |
|
|
#page-modbus .input-row {display: contents;} |
|
|
display: flex; |
|
|
#page-modbus .modbus-input-area label.nowrap {white-space: nowrap;} |
|
|
flex-direction: column; |
|
|
#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 table { |
|
|
#page-modbus .modbus-actions button {background: #d3d3d3;border: 1px solid #aaa;border-radius: 2px;padding: 8px 24px;font-size: 16px;cursor: pointer;} |
|
|
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 { |
|
|
#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;} |
|
|
display: flex; |
|
|
#modbus-bottom-actions button {background: #e0e0e0;border: 1px solid #aaa;border-radius: 2px;padding: 10px 18px;font-size: 16px;min-width: 140px;cursor: pointer;} |
|
|
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 화면 */ |
|
|
/* OPC_UA 화면 */ |
|
|
#page-opcua .card { |
|
|
#page-opcua .card {background: #f7f7f7;border: 1px solid #bbb;box-shadow: none;padding: 24px;border-radius: 4px;display: flex;flex-direction: column;} |
|
|
background: #f7f7f7; |
|
|
#page-opcua table {width: 100%;border-collapse: collapse;background: #fff;border: 1px solid #bbb;} |
|
|
border: 1px solid #bbb; |
|
|
#page-opcua th, #page-opcua td {border: 1px solid #bbb;padding: 6px 8px;text-align: left;background: #fff;} |
|
|
box-shadow: none; |
|
|
#page-opcua th {background: #e5e5e5;font-weight: 500;} |
|
|
padding: 24px; |
|
|
#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;} |
|
|
border-radius: 4px; |
|
|
#page-opcua .input-row {display: contents;} |
|
|
display: flex; |
|
|
#page-opcua .opcua-input-area label.nowrap {white-space: nowrap;} |
|
|
flex-direction: column; |
|
|
#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 table { |
|
|
#page-opcua .opcua-actions button {background: #d3d3d3;border: 1px solid #aaa;border-radius: 2px;padding: 8px 24px;font-size: 16px;cursor: pointer;} |
|
|
width: 100%; |
|
|
#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;} |
|
|
border-collapse: collapse; |
|
|
#opcUa-bottom-actions button {background: #e0e0e0;border: 1px solid #aaa;border-radius: 2px;padding: 10px 18px;font-size: 16px;min-width: 140px;cursor: pointer;} |
|
|
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 화면 스타일 */ |
|
|
/* CAN 화면 스타일 */ |
|
|
#page-can .card { |
|
|
#page-can .card {background: #f7f7f7;border: 1px solid #bbb;box-shadow: none;padding: 24px;border-radius: 4px;display: flex;flex-direction: column;} |
|
|
background: #f7f7f7; |
|
|
#page-can table {width: 100%;border-collapse: collapse;background: #fff;border: 1px solid #bbb;} |
|
|
border: 1px solid #bbb; |
|
|
#page-can th, #page-can td {border: 1px solid #bbb;padding: 6px 8px;text-align: left;background: #fff;} |
|
|
box-shadow: none; |
|
|
#page-can th {background: #e5e5e5;font-weight: 500;} |
|
|
padding: 24px; |
|
|
#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;} |
|
|
border-radius: 4px; |
|
|
#page-can .input-row {display: contents;} |
|
|
display: flex; |
|
|
#page-can .can-input-area label.nowrap {white-space: nowrap;} |
|
|
flex-direction: column; |
|
|
#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 table { |
|
|
#page-can .can-actions button {background: #d3d3d3;border: 1px solid #aaa;border-radius: 2px;padding: 8px 24px;font-size: 16px;cursor: pointer;} |
|
|
width: 100%; |
|
|
#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;} |
|
|
border-collapse: collapse; |
|
|
#can-bottom-actions button {background: #e0e0e0;border: 1px solid #aaa;border-radius: 2px;padding: 10px 18px;font-size: 16px;min-width: 140px;cursor: pointer;} |
|
|
background: #fff; |
|
|
|
|
|
border: 1px solid #bbb; |
|
|
.selected-row {background: #d0e6ff !important;} |
|
|
} |
|
|
.selected-row > td {background: #d0e6ff !important;} |
|
|
|
|
|
|
|
|
#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;} |
|
|
.file-upload-section{padding: 10px;margin: 10px;text-align: center;} |
|
|
|
|
|
|
|
|
@ -899,7 +453,7 @@ |
|
|
</div> |
|
|
</div> |
|
|
</section> |
|
|
</section> |
|
|
|
|
|
|
|
|
<!-- ▽ Register PAGE (placeholder) ▽ --> |
|
|
<!-- ▽ Protocol PAGE (placeholder) ▽ --> |
|
|
<section id="page-reg" class="page" style="display:none"> |
|
|
<section id="page-reg" class="page" style="display:none"> |
|
|
<div class="card"><h3>Config</h3> |
|
|
<div class="card"><h3>Config</h3> |
|
|
<div class="io-group"> |
|
|
<div class="io-group"> |
|
|
@ -1025,10 +579,10 @@ |
|
|
</div> |
|
|
</div> |
|
|
<div class="config-actions"> |
|
|
<div class="config-actions"> |
|
|
<div class="file-upload-wrapper-protocol" |
|
|
<div class="file-upload-wrapper-protocol" |
|
|
data-file-type="register" |
|
|
data-file-type="protocol" |
|
|
data-expected-filename="registerConfig.json" |
|
|
data-expected-filename="protocolConfig.json" |
|
|
data-upload-url="http://localhost:8080/setting/register" |
|
|
data-upload-url="http://localhost:8080/setting/protocol" |
|
|
data-get-url="http://localhost:8080/setting/get-register"> |
|
|
data-get-url="http://localhost:8080/setting/get-protocol"> |
|
|
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;"> |
|
|
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;"> |
|
|
<button class="select-file-btn-protocol">Load Local Protocol File</button> |
|
|
<button class="select-file-btn-protocol">Load Local Protocol File</button> |
|
|
</div> |
|
|
</div> |
|
|
@ -1115,10 +669,10 @@ |
|
|
|
|
|
|
|
|
<div id="modbus-bottom-actions"> |
|
|
<div id="modbus-bottom-actions"> |
|
|
<div class="file-upload-wrapper-protocol" |
|
|
<div class="file-upload-wrapper-protocol" |
|
|
data-file-type="register" |
|
|
data-file-type="protocol" |
|
|
data-expected-filename="registerConfig.json" |
|
|
data-expected-filename="protocolConfig.json" |
|
|
data-upload-url="http://localhost:8080/setting/register" |
|
|
data-upload-url="http://localhost:8080/setting/protocol" |
|
|
data-get-url="http://localhost:8080/setting/get-register"> |
|
|
data-get-url="http://localhost:8080/setting/get-protocol"> |
|
|
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;"> |
|
|
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;"> |
|
|
<button class="select-file-btn-protocol">Load Local Protocol File</button> |
|
|
<button class="select-file-btn-protocol">Load Local Protocol File</button> |
|
|
</div> |
|
|
</div> |
|
|
@ -1197,10 +751,10 @@ |
|
|
|
|
|
|
|
|
<div id="opcUa-bottom-actions"> |
|
|
<div id="opcUa-bottom-actions"> |
|
|
<div class="file-upload-wrapper-protocol" |
|
|
<div class="file-upload-wrapper-protocol" |
|
|
data-file-type="register" |
|
|
data-file-type="protocol" |
|
|
data-expected-filename="registerConfig.json" |
|
|
data-expected-filename="protocolConfig.json" |
|
|
data-upload-url="http://localhost:8080/setting/register" |
|
|
data-upload-url="http://localhost:8080/setting/protocol" |
|
|
data-get-url="http://localhost:8080/setting/get-register"> |
|
|
data-get-url="http://localhost:8080/setting/get-protocol"> |
|
|
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;"> |
|
|
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;"> |
|
|
<button class="select-file-btn-protocol">Load Local Protocol File</button> |
|
|
<button class="select-file-btn-protocol">Load Local Protocol File</button> |
|
|
</div> |
|
|
</div> |
|
|
@ -1272,10 +826,10 @@ |
|
|
</div> |
|
|
</div> |
|
|
<div id="can-bottom-actions"> |
|
|
<div id="can-bottom-actions"> |
|
|
<div class="file-upload-wrapper-protocol" |
|
|
<div class="file-upload-wrapper-protocol" |
|
|
data-file-type="register" |
|
|
data-file-type="protocol" |
|
|
data-expected-filename="registerConfig.json" |
|
|
data-expected-filename="protocolConfig.json" |
|
|
data-upload-url="http://localhost:8080/setting/register" |
|
|
data-upload-url="http://localhost:8080/setting/protocol" |
|
|
data-get-url="http://localhost:8080/setting/get-register"> |
|
|
data-get-url="http://localhost:8080/setting/get-protocol"> |
|
|
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;"> |
|
|
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;"> |
|
|
<button class="select-file-btn-protocol">Load Local Protocol File</button> |
|
|
<button class="select-file-btn-protocol">Load Local Protocol File</button> |
|
|
</div> |
|
|
</div> |
|
|
@ -1424,6 +978,37 @@ |
|
|
URL.revokeObjectURL(url); |
|
|
URL.revokeObjectURL(url); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 페이지 로드 시 자동으로 JSON 파일들 로드 |
|
|
|
|
|
async function loadInitialData() { |
|
|
|
|
|
try { |
|
|
|
|
|
// Device 설정 로드 |
|
|
|
|
|
const deviceResponse = await fetch('http://localhost:8080/setting/get-device'); |
|
|
|
|
|
if (deviceResponse.ok) { |
|
|
|
|
|
const deviceData = await deviceResponse.json(); |
|
|
|
|
|
if (deviceData && deviceData.body) { |
|
|
|
|
|
loadFromData(deviceData.body); |
|
|
|
|
|
console.log('Device 설정 자동 로드 완료'); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.log('Device 설정 파일이 없거나 로드 실패, 기본값 사용:', error.message); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
// Protocol 설정 로드 |
|
|
|
|
|
const protocolResponse = await fetch('http://localhost:8080/setting/get-protocol'); |
|
|
|
|
|
if (protocolResponse.ok) { |
|
|
|
|
|
const protocolData = await protocolResponse.json(); |
|
|
|
|
|
if (protocolData && protocolData.body) { |
|
|
|
|
|
loadFromRegisterJson(protocolData.body); |
|
|
|
|
|
console.log('Protocol 설정 자동 로드 완료'); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.log('Protocol 설정 파일이 없거나 로드 실패, 기본값 사용:', error.message); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/* (A) 고정 data 객체 그대로 저장 */ |
|
|
/* (A) 고정 data 객체 그대로 저장 */ |
|
|
document.getElementById('downloadDeviceBtn')?.addEventListener('click', () => { |
|
|
document.getElementById('downloadDeviceBtn')?.addEventListener('click', () => { |
|
|
downloadDeviceJson(collectData(data)); |
|
|
downloadDeviceJson(collectData(data)); |
|
|
@ -1838,6 +1423,8 @@ |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
|
|
|
// 페이지 로드 시 자동으로 JSON 파일들 로드 |
|
|
|
|
|
loadInitialData(); |
|
|
|
|
|
|
|
|
// Device 파일 업로드 |
|
|
// Device 파일 업로드 |
|
|
// 모든 파일 업로드 래퍼 섹션을 가져옵니다. |
|
|
// 모든 파일 업로드 래퍼 섹션을 가져옵니다. |
|
|
@ -2603,7 +2190,7 @@ |
|
|
document.getElementById('saveProtocolBtn').addEventListener('click', () => { |
|
|
document.getElementById('saveProtocolBtn').addEventListener('click', () => { |
|
|
console.log('버튼 클릭 됨'); |
|
|
console.log('버튼 클릭 됨'); |
|
|
console.log(collectConfigData()); |
|
|
console.log(collectConfigData()); |
|
|
fetch('http://localhost:8080/setting/register', { |
|
|
fetch('http://localhost:8080/setting/protocol', { |
|
|
method: 'POST', |
|
|
method: 'POST', |
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
body: JSON.stringify(collectConfigData()) |
|
|
body: JSON.stringify(collectConfigData()) |
|
|
@ -2624,7 +2211,7 @@ |
|
|
|
|
|
|
|
|
document.getElementById('saveModbusBtn').addEventListener('click', () => { |
|
|
document.getElementById('saveModbusBtn').addEventListener('click', () => { |
|
|
console.log('버튼 클릭 됨'); |
|
|
console.log('버튼 클릭 됨'); |
|
|
fetch('http://localhost:8080/setting/register', { |
|
|
fetch('http://localhost:8080/setting/protocol', { |
|
|
method: 'POST', |
|
|
method: 'POST', |
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
body: JSON.stringify(collectConfigData()) |
|
|
body: JSON.stringify(collectConfigData()) |
|
|
@ -2645,7 +2232,7 @@ |
|
|
|
|
|
|
|
|
document.getElementById('saveOpcUaFileBtn').addEventListener('click', () => { |
|
|
document.getElementById('saveOpcUaFileBtn').addEventListener('click', () => { |
|
|
console.log('버튼 클릭 됨'); |
|
|
console.log('버튼 클릭 됨'); |
|
|
fetch('http://localhost:8080/setting/register', { |
|
|
fetch('http://localhost:8080/setting/protocol', { |
|
|
method: 'POST', |
|
|
method: 'POST', |
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
body: JSON.stringify(collectConfigData()) |
|
|
body: JSON.stringify(collectConfigData()) |
|
|
@ -2693,7 +2280,7 @@ |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('saveCanBtn').addEventListener('click', () => { |
|
|
document.getElementById('saveCanBtn').addEventListener('click', () => { |
|
|
fetch('http://localhost:8080/setting/register', { |
|
|
fetch('http://localhost:8080/setting/protocol', { |
|
|
method: 'POST', |
|
|
method: 'POST', |
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
body: JSON.stringify(collectConfigData()) |
|
|
body: JSON.stringify(collectConfigData()) |
|
|
|