|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html lang="ko">
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="UTF-8" />
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
|
|
|
<title>Device Configuration UI</title>
|
|
|
|
|
|
|
|
|
|
<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:flex; gap: 40px ;flex-wrap:wrap;}
|
|
|
|
|
.io-group{flex:1 1 0;min-width:320px;}
|
|
|
|
|
.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;padding: 20px}
|
|
|
|
|
.config-actions button{min-width:140px;padding:10px 18px;font-size:16px;border:1px solid #bbb;background:#d9d9d9;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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.selected-row{
|
|
|
|
|
background: #d0e6ff !important;
|
|
|
|
|
}
|
|
|
|
|
.selected-row > td {
|
|
|
|
|
background: #d0e6ff !important;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<!-- ───── Sidebar ───── -->
|
|
|
|
|
<aside class="sidebar">
|
|
|
|
|
<h2>Configuration</h2>
|
|
|
|
|
<details class="nav" open>
|
|
|
|
|
<summary>Device configuration</summary>
|
|
|
|
|
<div class="nav-items">
|
|
|
|
|
<button data-page="ssid">SSID</button>
|
|
|
|
|
<button data-page="network">Network & Server</button>
|
|
|
|
|
<button data-page="io">I/O</button>
|
|
|
|
|
</div>
|
|
|
|
|
</details>
|
|
|
|
|
<details class="nav">
|
|
|
|
|
<summary>Protocol configuration</summary>
|
|
|
|
|
<div class="nav-items">
|
|
|
|
|
<button data-page="reg">Config</button>
|
|
|
|
|
<button data-page="modbus" id="modbusSidebarBtn" style="display: none">MODBUS</button>
|
|
|
|
|
<button data-page="opcua" id="opcUaSidebarBtn" style="display: none">OPC_UA</button>
|
|
|
|
|
</div>
|
|
|
|
|
</details>
|
|
|
|
|
</aside>
|
|
|
|
|
|
|
|
|
|
<!-- ───── Content ───── -->
|
|
|
|
|
<div class="content">
|
|
|
|
|
<header class="topbar" id="page-title">SSID</header>
|
|
|
|
|
<main class="board">
|
|
|
|
|
|
|
|
|
|
<!-- ▽ SSID PAGE ▽ -->
|
|
|
|
|
<section id="page-ssid" class="page">
|
|
|
|
|
<div class="card">
|
|
|
|
|
<h3>SSID List</h3>
|
|
|
|
|
|
|
|
|
|
<template id="ssid-template">
|
|
|
|
|
<div class="ssid-row">
|
|
|
|
|
<input class="ssid-name" placeholder="Name" />
|
|
|
|
|
<input class="ssid-pw" type="password" placeholder="Password" />
|
|
|
|
|
<button type="button" class="toggle-pw">show</button>
|
|
|
|
|
<select>
|
|
|
|
|
<option value="none">NONE</option>
|
|
|
|
|
<option value="wpa/wpa2">WPA/WPA2</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<div id="ssid-list"></div>
|
|
|
|
|
<button id="add-ssid" style="margin-top:12px">Add SSID</button>
|
|
|
|
|
<button id="delete-ssid" style="margin-top:12px">Delete SSID</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="actions">
|
|
|
|
|
<button id="loadDeviceBtn_ssid">Load Device File</button>
|
|
|
|
|
<button id="saveDeviceBtn_ssid">Save to Device</button>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<!-- ▽ NETWORK PAGE ▽ -->
|
|
|
|
|
<section id="page-network" class="page" style="display:none">
|
|
|
|
|
<div class="card" style="margin-bottom:32px">
|
|
|
|
|
<h3>Network</h3>
|
|
|
|
|
<div class="grid-3-col" style="margin-bottom:16px">
|
|
|
|
|
<div class="radio-group">
|
|
|
|
|
<label class="radio-label"><input type="radio" name="ipMode" value="static"> Static</label>
|
|
|
|
|
<label class="radio-label"><input type="radio" name="ipMode" value="dhcp"> DHCP</label>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="grid-3-col"><span>IP</span><input id="wifi_ip" placeholder="000.000.000.000" /></div>
|
|
|
|
|
<div class="grid-3-col"><span>SUBNET</span><input id="wifi_netmask" placeholder="255.255.255.0" /></div>
|
|
|
|
|
<div class="grid-3-col"><span>GATEWAY</span><input id="wifi_gateway" placeholder="000.000.000.001" /></div>
|
|
|
|
|
<div class="grid-3-col"><span>DNS #1</span><input id="wifi_dns1" placeholder="008.008.008.008" /></div>
|
|
|
|
|
<div class="grid-3-col"><span>DNS #2</span><input id="wifi_dns2" placeholder="008.008.004.004" /></div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="card">
|
|
|
|
|
<h3>Server</h3>
|
|
|
|
|
<div class="grid-3-col"><strong></strong><span style="text-align:center">IP</span><span style="text-align:center">Port</span></div>
|
|
|
|
|
<div class="grid-3-col"><span>TIoT Server</span> <input id="rtcm_server_ip"/><input id="rtcm_server_port"/></div>
|
|
|
|
|
<div class="grid-3-col"><span>Update Server</span><input id="update_server_ip" /><input id="update_server_port" /></div>
|
|
|
|
|
<div class="grid-3-col"><span>DGPS Server</span> <input id="modbus_server_ip" /><input id="modbus_server_port" /></div>
|
|
|
|
|
<div class="grid-3-col"><span>OPC-UA</span> <input id="opc_ua_server_ip" /><input id="opc_ua_server_port"/></div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="actions">
|
|
|
|
|
<button id="loadDeviceBtn_network">Load Device File</button>
|
|
|
|
|
<button id="saveDeviceBtn_network">Save to Device</button>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<!-- ▽ I/O PAGE ▽ -->
|
|
|
|
|
<section id="page-io" class="page" style="display:none">
|
|
|
|
|
<div class="card">
|
|
|
|
|
<div class="io-top">
|
|
|
|
|
<!-- Ethernet -->
|
|
|
|
|
<div class="io-group">
|
|
|
|
|
<h4>Ethernet</h4>
|
|
|
|
|
<div class="io-fieldset">
|
|
|
|
|
<div class="io-row"><span class="label-inline">IP</span><span class="colon">:</span><input id="eth_ip"></div>
|
|
|
|
|
<div class="io-row"><span class="label-inline">SUBNET</span><span class="colon">:</span><input id="eth_netmask"></div>
|
|
|
|
|
<div class="io-row"><span class="label-inline">GATEWAY</span><span class="colon">:</span><input id="eth_gateway"></div>
|
|
|
|
|
<div class="io-row"><span class="label-inline">SERVER IP</span><span class="colon">:</span><input id="protocol_server_ip"></div>
|
|
|
|
|
<div class="io-row"><span class="label-inline">SERVER PORT</span><span class="colon">:</span><input id="protocol_server_port"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- LTE -->
|
|
|
|
|
<div class="io-group">
|
|
|
|
|
<h4>LTE</h4>
|
|
|
|
|
<div class="io-fieldset">
|
|
|
|
|
<div class="io-row"><span class="label-inline">IP</span><span class="colon">:</span><input id="lte_ip"></div>
|
|
|
|
|
<div class="io-row"><span class="label-inline">SUBNET</span><span class="colon">:</span><input id="lte_netmask"></div>
|
|
|
|
|
<div class="io-row"><span class="label-inline">GATEWAY</span><span class="colon">:</span><input id="lte_gateway"></div>
|
|
|
|
|
<div class="io-row"><span class="label-inline">Port</span><span class="colon">:</span><input id="lte_port"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="io-bot">
|
|
|
|
|
<!-- RS-485 -->
|
|
|
|
|
<div class="io-group">
|
|
|
|
|
<h4>MODBUS 422 / 485</h4>
|
|
|
|
|
<div class="io-row"><span class="label-inline">Mode</span><span class="colon">:</span>
|
|
|
|
|
<select id="rs485_mode">
|
|
|
|
|
<option value="half">half</option>
|
|
|
|
|
<option value="full">full</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row"><span class="label-inline">Speed</span><span class="colon">:</span><select id="rs485_baudrate">
|
|
|
|
|
<option value="9600">9600</option>
|
|
|
|
|
<option value="115200">115200</option>
|
|
|
|
|
</select></div>
|
|
|
|
|
<div class="io-row"><span class="label-inline">Parity</span><span class="colon">:</span><select id="rs485_parity">
|
|
|
|
|
<option value="no">NONE</option>
|
|
|
|
|
<option value="odd">ODD</option>
|
|
|
|
|
<option value="EVEN">EVEN</option>
|
|
|
|
|
</select></div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- CAN -->
|
|
|
|
|
<div class="io-group">
|
|
|
|
|
<h4>CAN</h4>
|
|
|
|
|
<div class="io-row"><span class="label-inline">Type</span><span class="colon">:</span>
|
|
|
|
|
<select id="can_bus_type">
|
|
|
|
|
<option value="extended">extended</option>
|
|
|
|
|
<option value="standard">standard</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="io-row"><span class="label-inline">Speed</span><span class="colon">:</span>
|
|
|
|
|
<select id="can_baudrate">
|
|
|
|
|
<option value="1000">100kb/s</option>
|
|
|
|
|
<option value="2500">250kb/s</option>
|
|
|
|
|
<option value="5000">500kb/s</option>
|
|
|
|
|
<option value="10000">1M/s</option>
|
|
|
|
|
</select>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- IMU -->
|
|
|
|
|
<div class="io-group">
|
|
|
|
|
<h4>IMU Re-map</h4>
|
|
|
|
|
<div class="io-row"><span class="label-inline">X</span><span class="colon">:</span>
|
|
|
|
|
<select id="imu_remap_x">
|
|
|
|
|
<option value="x">x</option>
|
|
|
|
|
<option value="y">y</option>
|
|
|
|
|
<option value="z">z</option>
|
|
|
|
|
<option value="-x">-x</option>
|
|
|
|
|
<option value="-y">-y</option>
|
|
|
|
|
<option value="-z">-z</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row"><span class="label-inline">Y</span><span class="colon">:</span><select id="imu_remap_y">
|
|
|
|
|
<option value="x">x</option>
|
|
|
|
|
<option value="y">y</option>
|
|
|
|
|
<option value="z">z</option>
|
|
|
|
|
<option value="-x">-x</option>
|
|
|
|
|
<option value="-y">-y</option>
|
|
|
|
|
<option value="-z">-z</option>
|
|
|
|
|
</select></div>
|
|
|
|
|
<div class="io-row"><span class="label-inline">Z</span><span class="colon">:</span><select id="imu_remap_z">
|
|
|
|
|
<option value="x">x</option>
|
|
|
|
|
<option value="y">y</option>
|
|
|
|
|
<option value="z">z</option>
|
|
|
|
|
<option value="-x">-x</option>
|
|
|
|
|
<option value="-y">-y</option>
|
|
|
|
|
<option value="-z">-z</option>
|
|
|
|
|
</select></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="actions">
|
|
|
|
|
<button id="loadDeviceBtn_io">Load Device File</button>
|
|
|
|
|
<button id="saveDeviceBtn_io">Save to Device</button>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<!-- ▽ Register PAGE (placeholder) ▽ -->
|
|
|
|
|
<section id="page-reg" class="page" style="display:none">
|
|
|
|
|
<div class="card"><h3>Config</h3>
|
|
|
|
|
<div class = "io-group">
|
|
|
|
|
<div class="reg-io-grid">
|
|
|
|
|
<div class="io-fieldset">
|
|
|
|
|
<div class="io-row"><span class="label">Device type</span>
|
|
|
|
|
<select id="reg_device_type">
|
|
|
|
|
<option selected disabled hidden="hidden"></option>
|
|
|
|
|
<option value="TIOT">TIOT</option>
|
|
|
|
|
<option value="RTLS">RTLS</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row"><span class="label">Version</span><input id="reg_version"></div>
|
|
|
|
|
<div class="io-row"><span class="label">Equipment</span>
|
|
|
|
|
<select id="reg_equipment">
|
|
|
|
|
<option selected disabled hidden="hidden"></option>
|
|
|
|
|
<option value="ITV">ITV</option>
|
|
|
|
|
<option value="RS">RS</option>
|
|
|
|
|
<option value="ECH">ECH</option>
|
|
|
|
|
<option value="RTG">RTG</option>
|
|
|
|
|
<option value="RMG">RMG</option>
|
|
|
|
|
<option value="MHC">MHC</option>
|
|
|
|
|
<option value="STS">STS</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row"><span class="label">Equipment ID</span><input id="reg_equipment_id"></div>
|
|
|
|
|
<div class="io-row"><span class="label">MEID</span><input id="reg_meid"></div>
|
|
|
|
|
<div class="io-row"><span class="label">Protocol</span>
|
|
|
|
|
<select id="reg_protocol">
|
|
|
|
|
<option selected disabled hidden="hidden"></option>
|
|
|
|
|
<option value="MODBUS">MODBUS</option>
|
|
|
|
|
<option value="OPC_UA">OPC_UA</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row"><span class="label">heading_on</span>
|
|
|
|
|
<select id="reg_heading_on">
|
|
|
|
|
<option selected disabled hidden="hidden"></option>
|
|
|
|
|
<option value="on">on</option>
|
|
|
|
|
<option value="off">off</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row"><span class="label">heading_imu_on</span>
|
|
|
|
|
<select id="reg_heading_imu_on">
|
|
|
|
|
<option selected disabled hidden="hidden"></option>
|
|
|
|
|
<option value="on">on</option>
|
|
|
|
|
<option value="off">off</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-fieldset"> <div class="io-row"><span class="label">DR On/Off</span>
|
|
|
|
|
<select id="reg_dr_on_off">
|
|
|
|
|
<option selected disabled hidden="hidden"></option>
|
|
|
|
|
<option value="on">on</option>
|
|
|
|
|
<option value="off">off</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row"><span class="label">Speed Data Source</span>
|
|
|
|
|
<select id="reg_speed_data_source">
|
|
|
|
|
<option selected disabled hidden="hidden"></option>
|
|
|
|
|
<option value="CAN">CAN</option>
|
|
|
|
|
<option value="GPS">GPS</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row"><span class="label">2Byte Order</span>
|
|
|
|
|
<select id="reg_two_byte_order">
|
|
|
|
|
<option selected disabled hidden="hidden"></option>
|
|
|
|
|
<option value="little">little</option>
|
|
|
|
|
<option value="big">big</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row"><span class="label">4Byte Order</span>
|
|
|
|
|
<select id="reg_four_byte_order">
|
|
|
|
|
<option selected disabled hidden="hidden"></option>
|
|
|
|
|
<option value="little">little</option>
|
|
|
|
|
<option value="little_swap">little swap</option>
|
|
|
|
|
<option value="big_swap">big swap</option>
|
|
|
|
|
<option value="big">big</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row"><span class="label">CAN Input</span>
|
|
|
|
|
<select id="reg_can_input">
|
|
|
|
|
<option selected disabled hidden="hidden"></option>
|
|
|
|
|
<option value="on">on</option>
|
|
|
|
|
<option value="off">off</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row"><span class="label">fixmode_on</span>
|
|
|
|
|
<select id="reg_fixmode_on">
|
|
|
|
|
<option selected disabled hidden="hidden"></option>
|
|
|
|
|
<option value="on">on</option>
|
|
|
|
|
<option value="off">off</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row"><span class="label">Analog Input Level</span>
|
|
|
|
|
<select id="reg_analog_input_level">
|
|
|
|
|
<option selected disabled hidden="hidden"></option>
|
|
|
|
|
<option value="2">2</option>
|
|
|
|
|
<option value="4">4</option>
|
|
|
|
|
<option value="6">6</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row checkbox-group">
|
|
|
|
|
<label class="checkbox-with-text">
|
|
|
|
|
<input type="checkbox" id="ai0"/>AI0
|
|
|
|
|
<input type="checkbox" id="ai1"/>AI1
|
|
|
|
|
<input type="checkbox" id="ai2"/>AI2
|
|
|
|
|
<input type="checkbox" id="ai3"/>AI3
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="io-row checkbox-group">
|
|
|
|
|
<label class="checkbox-with-text">
|
|
|
|
|
<input type="checkbox" id="di0"/>DI0
|
|
|
|
|
<input type="checkbox" id="di1"/>DI1
|
|
|
|
|
<input type="checkbox" id="di2"/>DI2
|
|
|
|
|
<input type="checkbox" id="di3"/>DI3
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="config-actions">
|
|
|
|
|
<button id="loadProtocolBtn">Load Protocol File</button>
|
|
|
|
|
<button id="saveProtocolBtn">Save to Protocol</button>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<template id="modbus-row-template">
|
|
|
|
|
<tr>
|
|
|
|
|
<td><span class="field-display"></span></td>
|
|
|
|
|
<td><span class="addr-display"></span></td>
|
|
|
|
|
<td><span class="idt-display"></span></td>
|
|
|
|
|
<td><span class="odt-display"></span></td>
|
|
|
|
|
<td><span class="dv-display"></span></td>
|
|
|
|
|
<td><span class="shift-display"></span></td>
|
|
|
|
|
<td><span class="expr-display"></span></td>
|
|
|
|
|
<td><span class="mask-display"></span></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<section id="page-modbus" class="page" style="display:none;">
|
|
|
|
|
<div class="card">
|
|
|
|
|
<h3>Register_Configuration Modbus화면</h3>
|
|
|
|
|
<div class="modbus-table-container">
|
|
|
|
|
<table>
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th>Field</th>
|
|
|
|
|
<th>Address</th>
|
|
|
|
|
<th>IDT</th>
|
|
|
|
|
<th>ODT</th>
|
|
|
|
|
<th>Default Value</th>
|
|
|
|
|
<th>Shift</th>
|
|
|
|
|
<th>Expression</th>
|
|
|
|
|
<th>Mask</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody id="modbus-field-list">
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="modbus-input-area">
|
|
|
|
|
<label>Field</label>
|
|
|
|
|
<input type="text" id="modbus_field_input" placeholder="Enter field name">
|
|
|
|
|
<label class="nowrap">idt (Input Data Type)</label>
|
|
|
|
|
<input type="text" id="modbus_idt_input" placeholder="Enter IDT">
|
|
|
|
|
<label>addr</label>
|
|
|
|
|
<input type="text" id="modbus_addr_input" placeholder="Enter address">
|
|
|
|
|
<label class="nowrap">odt (Output Data Type)</label>
|
|
|
|
|
<input type="text" id="modbus_odt_input" placeholder="Enter ODT">
|
|
|
|
|
<label>shift</label>
|
|
|
|
|
<input type="text" id="modbus_shift_input" placeholder="Enter shift value">
|
|
|
|
|
<label class="nowrap">dv</label>
|
|
|
|
|
<input type="text" id="modbus_dv_input" placeholder="Enter default value">
|
|
|
|
|
<label>expr</label>
|
|
|
|
|
<input type="text" id="modbus_expr_input" placeholder="Enter expression">
|
|
|
|
|
<label class="nowrap">mask</label>
|
|
|
|
|
<input type="text" id="modbus_mask_input" placeholder="Enter mask">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="modbus-actions">
|
|
|
|
|
<button id="modbus_add_btn">Add</button>
|
|
|
|
|
<button id="modbus_modify_btn">Modify</button>
|
|
|
|
|
<button id="modbus_delete_btn">Delete</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div id="modbus-bottom-actions">
|
|
|
|
|
<button id="loadModbusBtn">Load Protocol File</button>
|
|
|
|
|
<button id="saveModbusBtn">Save Protocol File</button>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<template id="opcua-row-template">
|
|
|
|
|
<tr class="opcua-row">
|
|
|
|
|
<td><span class="opcua-field-display"></span></td>
|
|
|
|
|
<td><span class="opcua-addr-display"></span></td>
|
|
|
|
|
<td><span class="opcua-ns-display"></span></td>
|
|
|
|
|
<td><span class="opcua-odt-display"></span></td>
|
|
|
|
|
<td><span class="opcua-dv-display"></span></td>
|
|
|
|
|
<td><span class="opcua-shift-display"></span></td>
|
|
|
|
|
<td><span class="opcua-mask-display"></span></td>
|
|
|
|
|
<td><span class="opcua-expr-display"></span></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<section id="page-opcua" class="page" style="display:none;">
|
|
|
|
|
<div class="card">
|
|
|
|
|
<h3>OPC_UA Configuration</h3>
|
|
|
|
|
<div class="opcua-table-container">
|
|
|
|
|
<table>
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th>field</th>
|
|
|
|
|
<th>addr</th>
|
|
|
|
|
<th>ns</th>
|
|
|
|
|
<th>odt</th>
|
|
|
|
|
<th>dv</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody id="opcua-field-list">
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="opcua-input-area">
|
|
|
|
|
<div class="input-row">
|
|
|
|
|
<label>field</label><input type="text" id="opcua_field_input">
|
|
|
|
|
<label>ns(NameSpace)</label><input type="text" id="opcua_ns_input">
|
|
|
|
|
</div>
|
|
|
|
|
<div class="input-row">
|
|
|
|
|
<label>addr(address)</label><input type="text" id="opcua_addr_input">
|
|
|
|
|
<label>odt(Output Data Type)</label><input type="text" id="opcua_odt_input">
|
|
|
|
|
</div>
|
|
|
|
|
<div class="input-row">
|
|
|
|
|
<label>shift</label><input type="text" id="opcua_shift_input">
|
|
|
|
|
<label>dv(Default Value)</label><input type="text" id="opcua_dv_input">
|
|
|
|
|
</div>
|
|
|
|
|
<div class="input-row">
|
|
|
|
|
<label>expr</label><input type="text" id="opcua_expr_input">
|
|
|
|
|
<label>mask</label><input type="text" id="opcua_mask_input">
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="opcua-actions">
|
|
|
|
|
<button id="opcua_add_btn">Add</button>
|
|
|
|
|
<button id="opcua_modify_btn">Modify</button>
|
|
|
|
|
<button id="opcua_delete_btn">Delete</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="actions">
|
|
|
|
|
<button id="loadOpcUaFileBtn">Load Protocol File</button>
|
|
|
|
|
<button id="saveOpcUaFileBtn">Save to Protocol</button>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
</main>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- ───── Script ───── -->
|
|
|
|
|
<script>
|
|
|
|
|
|
|
|
|
|
const data = {
|
|
|
|
|
/* Wi-Fi */
|
|
|
|
|
wifi_static : 'off', wifi_ip:'', wifi_netmask:'', wifi_gateway:'', wifi_dns1:'', wifi_dns2:'',
|
|
|
|
|
|
|
|
|
|
/* Ethernet */
|
|
|
|
|
eth_ip:'', eth_netmask:'', eth_gateway:'',
|
|
|
|
|
|
|
|
|
|
/* 서버들 */
|
|
|
|
|
protocol_server_ip:'', protocol_server_port:null,
|
|
|
|
|
update_server_ip :'',
|
|
|
|
|
update_server_port:null,
|
|
|
|
|
rtcm_server_ip :'',
|
|
|
|
|
rtcm_server_port :null,
|
|
|
|
|
opc_ua_server_ip :'',
|
|
|
|
|
opc_ua_server_port:null,
|
|
|
|
|
modbus_server_ip :'',
|
|
|
|
|
modbus_server_port:null,
|
|
|
|
|
|
|
|
|
|
/* CAN / RS-485 */
|
|
|
|
|
can_bus_type:'', can_baudrate:null,
|
|
|
|
|
rs485_mode:'', rs485_baudrate:null, rs485_databits:null,
|
|
|
|
|
rs485_parity:'', rs485_stopbits:null,
|
|
|
|
|
|
|
|
|
|
/* IMU (축 + 부호) */
|
|
|
|
|
imu_remap_x:'', imu_remap_y:'', imu_remap_z:'',
|
|
|
|
|
imu_remap_x_sign:'', imu_remap_y_sign:'', imu_remap_z_sign:'',
|
|
|
|
|
|
|
|
|
|
/* SSID 목록 */
|
|
|
|
|
WIFI_SSID:[{wifi_ssid:'',wifi_password:'',wifi_security:'none'}],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const configData = {
|
|
|
|
|
analog_input_level: '',
|
|
|
|
|
ai0: 0,
|
|
|
|
|
ai1: 0,
|
|
|
|
|
ai2: 0,
|
|
|
|
|
ai3: 0,
|
|
|
|
|
di0: 0,
|
|
|
|
|
di1: 0,
|
|
|
|
|
di2: 0,
|
|
|
|
|
di3: 0,
|
|
|
|
|
can_input: '',
|
|
|
|
|
device_type: '',
|
|
|
|
|
dr_on: '',
|
|
|
|
|
equipment: '',
|
|
|
|
|
two_byte: '',
|
|
|
|
|
four_byte: '',
|
|
|
|
|
input_data_type: '',
|
|
|
|
|
output_data_type: '',
|
|
|
|
|
protocol: '',
|
|
|
|
|
speed_data_source: '',
|
|
|
|
|
version: '',
|
|
|
|
|
equipment_id: '',
|
|
|
|
|
meid: '',
|
|
|
|
|
heading_on: '',
|
|
|
|
|
heading_imu_on: '',
|
|
|
|
|
fixmode_on: '',
|
|
|
|
|
MODBUS: [],
|
|
|
|
|
OPC_UA: [],
|
|
|
|
|
CAN: []
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const configIdMap={
|
|
|
|
|
'reg_analog_input_level': 'analog_input_level',
|
|
|
|
|
'ai0': 'ai0',
|
|
|
|
|
'ai1': 'ai1',
|
|
|
|
|
'ai2': 'ai2',
|
|
|
|
|
'ai3': 'ai3',
|
|
|
|
|
'di0': 'di0',
|
|
|
|
|
'di1': 'di1',
|
|
|
|
|
'di2': 'di2',
|
|
|
|
|
'di3': 'di3',
|
|
|
|
|
'reg_can_input': 'can_input',
|
|
|
|
|
'reg_device_type': 'device_type',
|
|
|
|
|
'reg_dr_on_off': 'dr_on',
|
|
|
|
|
'reg_equipment': 'equipment',
|
|
|
|
|
'reg_two_byte_order': 'two_byte',
|
|
|
|
|
'reg_four_byte_order': 'four_byte',
|
|
|
|
|
'reg_protocol': 'protocol',
|
|
|
|
|
'reg_speed_data_source': 'speed_data_source',
|
|
|
|
|
'reg_version': 'version',
|
|
|
|
|
'reg_equipment_id': 'equipment_id',
|
|
|
|
|
'reg_meid': 'meid',
|
|
|
|
|
'reg_heading_on': 'heading_on',
|
|
|
|
|
'reg_heading_imu_on': 'heading_imu_on',
|
|
|
|
|
'reg_fixmode_on': 'fixmode_on'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let lastLoadedRegisterData = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const pages = {
|
|
|
|
|
ssid : document.getElementById('page-ssid'),
|
|
|
|
|
network: document.getElementById('page-network'),
|
|
|
|
|
io : document.getElementById('page-io'),
|
|
|
|
|
reg : document.getElementById('page-reg'),
|
|
|
|
|
modbus : document.getElementById('page-modbus'),
|
|
|
|
|
opcua : document.getElementById('page-opcua')
|
|
|
|
|
};
|
|
|
|
|
const titleEl = document.getElementById('page-title');
|
|
|
|
|
|
|
|
|
|
function showPage(pageName) {
|
|
|
|
|
Object.values(pages).forEach(p => p.style.display = 'none');
|
|
|
|
|
pages[pageName].style.display = 'block';
|
|
|
|
|
// 버튼 텍스트를 페이지 제목으로 설정
|
|
|
|
|
const btn = document.querySelector(`.nav-items button[data-page="${pageName}"]`);
|
|
|
|
|
if (btn) {
|
|
|
|
|
titleEl.textContent = btn.textContent;
|
|
|
|
|
} else {
|
|
|
|
|
titleEl.textContent = pageName.charAt(0).toUpperCase() + pageName.slice(1); // 기본값
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 페이지 전환 시 항상 마지막으로 로드된 전체 데이터를 UI에 반영
|
|
|
|
|
// if (lastLoadedRegisterData && Object.keys(lastLoadedRegisterData).length > 0) {
|
|
|
|
|
// loadFromRegisterJson(lastLoadedRegisterData);
|
|
|
|
|
// }
|
|
|
|
|
toggleNetInputs(); // Network 페이지 로드 시 IP 모드에 따른 입력 필드 활성화/비활성화
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.querySelectorAll('.nav-items button').forEach(btn=>{
|
|
|
|
|
btn.addEventListener('click',()=>{
|
|
|
|
|
// Object.values(pages).forEach(p=>p.style.display='none');
|
|
|
|
|
// pages[btn.dataset.page].style.display='block';
|
|
|
|
|
// titleEl.textContent = btn.textContent;
|
|
|
|
|
showPage(btn.dataset.page);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const ssidList = document.getElementById('ssid-list');
|
|
|
|
|
const ssidTmpl = document.getElementById('ssid-template');
|
|
|
|
|
|
|
|
|
|
function createSsidRow(obj={wifi_ssid:'',wifi_password:'',wifi_security:'none'}){
|
|
|
|
|
const node = ssidTmpl.content.firstElementChild.cloneNode(true);
|
|
|
|
|
node.querySelector('.ssid-name').value = obj.wifi_ssid ?? '';
|
|
|
|
|
node.querySelector('.ssid-pw' ).value = obj.wifi_password ?? '';
|
|
|
|
|
node.querySelector('select').value = (obj.wifi_security ?? 'none').toLowerCase();
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
function addSsidRow(){ ssidList.appendChild(createSsidRow()); }
|
|
|
|
|
document.getElementById('add-ssid').addEventListener('click', addSsidRow);
|
|
|
|
|
|
|
|
|
|
document.getElementById('delete-ssid').addEventListener('click', () => {
|
|
|
|
|
if(ssidList.children.length > 1){
|
|
|
|
|
ssidList.removeChild(ssidList.lastElementChild);
|
|
|
|
|
}
|
|
|
|
|
else if(ssidList.children.length == 1){
|
|
|
|
|
const firstRow = ssidList.firstElementChild;
|
|
|
|
|
firstRow.querySelector('.ssid-name').value = '';
|
|
|
|
|
firstRow.querySelector('.ssid-pw').value = '';
|
|
|
|
|
firstRow.querySelector('select').value = 'none';
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ssidList.addEventListener('click',e=>{
|
|
|
|
|
if(e.target.classList.contains('toggle-pw')){
|
|
|
|
|
const pw = e.target.closest('.ssid-row').querySelector('.ssid-pw');
|
|
|
|
|
pw.type = pw.type==='password' ? 'text' : 'password';
|
|
|
|
|
e.target.textContent = pw.type==='password' ? 'show' : 'hide';
|
|
|
|
|
}
|
|
|
|
|
else if(e.target.dataset.action === 'delete-ssid'){
|
|
|
|
|
const ssidRow = e.target.closest('.ssid-row');
|
|
|
|
|
if(ssidRow){
|
|
|
|
|
removeSsidRow(ssidRow);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
function gatherSsidRows(){
|
|
|
|
|
const arr=[];
|
|
|
|
|
ssidList.querySelectorAll('.ssid-row').forEach(row=>{
|
|
|
|
|
const ssid = row.querySelector('.ssid-name').value.trim();
|
|
|
|
|
const pw = row.querySelector('.ssid-pw' ).value.trim();
|
|
|
|
|
const sec = row.querySelector('select' ).value;
|
|
|
|
|
if(ssid) arr.push({wifi_ssid:ssid, wifi_password:pw, wifi_security:sec});
|
|
|
|
|
});
|
|
|
|
|
return arr.length ? arr : [{wifi_ssid:'',wifi_password:'',wifi_security:'none'}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ───────────── 4. DHCP ↔ Static 전환 입력 잠금 ───────────── */
|
|
|
|
|
const netFields = ['wifi_ip','wifi_netmask','wifi_gateway','wifi_dns1','wifi_dns2']
|
|
|
|
|
.map(id=>document.getElementById(id));
|
|
|
|
|
|
|
|
|
|
function toggleNetInputs(){
|
|
|
|
|
const isStatic = document.querySelector('input[name="ipMode"]:checked')?.value === 'static';
|
|
|
|
|
netFields.forEach(el=>{
|
|
|
|
|
el.disabled = !isStatic;
|
|
|
|
|
el.classList.toggle('disabled', !isStatic);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
document.querySelectorAll('input[name="ipMode"]').forEach(r=>r.addEventListener('change', toggleNetInputs));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function axisValueToPair(val){
|
|
|
|
|
return val.startsWith('-')
|
|
|
|
|
? {axis:val.slice(1), sign:'minus'}
|
|
|
|
|
: {axis:val, sign:'plus'};
|
|
|
|
|
}
|
|
|
|
|
function pairToAxisValue(axis, sign){
|
|
|
|
|
return sign==='minus' ? `-${axis}` : axis;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ───────────── 6. 데이터 → 화면 로딩 ───────────── */
|
|
|
|
|
function loadFromData(obj){
|
|
|
|
|
/* Wi-Fi 모드 radio */
|
|
|
|
|
document.querySelector(`input[name="ipMode"][value="${obj.wifi_static==='on'?'static':'dhcp'}"]`).checked = true;
|
|
|
|
|
|
|
|
|
|
/* 일반 input/select */
|
|
|
|
|
for(const k in obj){
|
|
|
|
|
const el = document.getElementById(k);
|
|
|
|
|
if(el) el.value = obj[k] ?? '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* IMU select 값 재구성 */
|
|
|
|
|
['x','y','z'].forEach(a=>{
|
|
|
|
|
const axis = obj[`imu_remap_${a}`] || 'x';
|
|
|
|
|
const sign = obj[`imu_remap_${a}_sign`] || 'plus';
|
|
|
|
|
document.getElementById(`imu_remap_${a}`).value = pairToAxisValue(axis, sign);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* SSID */
|
|
|
|
|
ssidList.innerHTML='';
|
|
|
|
|
(obj.WIFI_SSID||[]).forEach(o=>ssidList.appendChild(createSsidRow(o)));
|
|
|
|
|
if(!ssidList.children.length) addSsidRow();
|
|
|
|
|
|
|
|
|
|
toggleNetInputs(); // 초기 잠금 상태
|
|
|
|
|
}
|
|
|
|
|
loadFromData(data);
|
|
|
|
|
|
|
|
|
|
let regProtocolSelect;
|
|
|
|
|
function toggleProtocolMenus() {
|
|
|
|
|
if(!regProtocolSelect) return;
|
|
|
|
|
const selectProtocol = regProtocolSelect.value;
|
|
|
|
|
const modbusSidebarBtn = document.getElementById('modbusSidebarBtn');
|
|
|
|
|
const opcUaSidebarBtn = document.getElementById('opcUaSidebarBtn');
|
|
|
|
|
|
|
|
|
|
if(modbusSidebarBtn) modbusSidebarBtn.style.display = 'none';
|
|
|
|
|
if(opcUaSidebarBtn) opcUaSidebarBtn.style.display = 'none';
|
|
|
|
|
|
|
|
|
|
if(selectProtocol === 'MODBUS'){
|
|
|
|
|
if (modbusSidebarBtn) modbusSidebarBtn.style.display = 'block';
|
|
|
|
|
}
|
|
|
|
|
else if(selectProtocol === 'OPC_UA'){
|
|
|
|
|
if(opcUaSidebarBtn) opcUaSidebarBtn.style.display = 'block';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Protocol configuration 메뉴를 자동으로 펼치기
|
|
|
|
|
const protocolDetails = document.querySelectorAll('details.nav')[1];
|
|
|
|
|
if (protocolDetails) protocolDetails.open = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function loadFromRegisterJson(register) { // register.json 데이터 로드
|
|
|
|
|
if (!register) {
|
|
|
|
|
console.error("Register data is null or undefined.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
lastLoadedRegisterData = {...register};
|
|
|
|
|
console.log("Extracted Body Data:", lastLoadedRegisterData); // DTO 객체 로깅
|
|
|
|
|
|
|
|
|
|
// 1. configIdMap을 순회하며 DTO의 camelCase 필드명과 HTML ID 매핑을 사용하여 UI 업데이트
|
|
|
|
|
for (const htmlId in configIdMap) {
|
|
|
|
|
const dtoKey = configIdMap[htmlId]; // DTO의 camelCase 필드명
|
|
|
|
|
const el = document.getElementById(htmlId);
|
|
|
|
|
|
|
|
|
|
if (el && lastLoadedRegisterData.hasOwnProperty(dtoKey)) {
|
|
|
|
|
const value = lastLoadedRegisterData[dtoKey];
|
|
|
|
|
|
|
|
|
|
if (el.type === 'checkbox') {
|
|
|
|
|
// AnalogPort Enum은 "0", "1" 문자열 또는 boolean으로 파싱될 수 있습니다.
|
|
|
|
|
// UI 체크박스는 boolean 값을 필요로 하므로, "1"이면 체크, "0"이면 체크 해제.
|
|
|
|
|
el.checked = (value === "1" || value === true);
|
|
|
|
|
} else {
|
|
|
|
|
// 기타 입력 필드 (select, text input)
|
|
|
|
|
// null 또는 undefined 값에 대비하여 빈 문자열로 처리
|
|
|
|
|
el.value = value !== null && value !== undefined ? value : '';
|
|
|
|
|
}
|
|
|
|
|
} else if (el) {
|
|
|
|
|
// DTO에 해당 필드가 없으면 UI 요소 초기화 (선택 사항)
|
|
|
|
|
if (el.type === 'checkbox') {
|
|
|
|
|
el.checked = false;
|
|
|
|
|
} else {
|
|
|
|
|
el.value = '';
|
|
|
|
|
}
|
|
|
|
|
console.warn(`DTO field '${dtoKey}' (mapped from HTML ID '${htmlId}') not found in loaded data.`);
|
|
|
|
|
} else {
|
|
|
|
|
console.warn(`HTML element with ID '${htmlId}' not found for DTO key '${dtoKey}'.`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// protocol 값이 있으면 select 값을 변경 (데이터 로드 시에만)
|
|
|
|
|
if (register.protocol && regProtocolSelect) {
|
|
|
|
|
regProtocolSelect.value = register.protocol;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 프로토콜 값에 따라 사이드바 버튼 토글
|
|
|
|
|
toggleProtocolMenus();
|
|
|
|
|
|
|
|
|
|
// MODBUS UI 테이블 렌더링
|
|
|
|
|
const modbusFieldList = document.getElementById('modbus-field-list');
|
|
|
|
|
const modbusRowTemplate = document.getElementById('modbus-row-template');
|
|
|
|
|
if (modbusFieldList && modbusRowTemplate) {
|
|
|
|
|
modbusFieldList.innerHTML = '';
|
|
|
|
|
if (Array.isArray(register.MODBUS) && register.MODBUS.length > 0) {
|
|
|
|
|
register.MODBUS.forEach(field => {
|
|
|
|
|
const newRow = modbusRowTemplate.content.cloneNode(true);
|
|
|
|
|
newRow.querySelector('.field-display').textContent = field.field ?? '';
|
|
|
|
|
newRow.querySelector('.addr-display').textContent = field.addr ?? '';
|
|
|
|
|
newRow.querySelector('.idt-display').textContent = field.idt ?? '';
|
|
|
|
|
newRow.querySelector('.odt-display').textContent = field.odt ?? '';
|
|
|
|
|
newRow.querySelector('.dv-display').textContent = field.dv ?? '';
|
|
|
|
|
newRow.querySelector('.shift-display').textContent = field.shift ?? '';
|
|
|
|
|
newRow.querySelector('.expr-display').textContent = field.expr ?? '';
|
|
|
|
|
newRow.querySelector('.mask-display').textContent = field.mask ?? '';
|
|
|
|
|
modbusFieldList.appendChild(newRow);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// MODBUS 데이터가 없으면 빈 행 하나 추가
|
|
|
|
|
const newRow = modbusRowTemplate.content.cloneNode(true);
|
|
|
|
|
modbusFieldList.appendChild(newRow);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const opcUaContainer = document.getElementById('opc_ua_fields_container');
|
|
|
|
|
if (opcUaContainer) {
|
|
|
|
|
opcUaContainer.innerHTML = '';
|
|
|
|
|
if (lastLoadedRegisterData.OPC_UA && lastLoadedRegisterData.OPC_UA.length > 0) {
|
|
|
|
|
lastLoadedRegisterData.OPC_UA.forEach(field => {
|
|
|
|
|
// ... OPC_UA 필드 표시 로직 ...
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
opcUaContainer.textContent = 'No OPC_UA fields configured.';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const canContainer = document.getElementById('can_fields_container');
|
|
|
|
|
if (canContainer) {
|
|
|
|
|
canContainer.innerHTML = '';
|
|
|
|
|
if (lastLoadedRegisterData.CAN && lastLoadedRegisterData.CAN.length > 0) {
|
|
|
|
|
lastLoadedRegisterData.CAN.forEach(field => {
|
|
|
|
|
// ... CAN 필드 표시 로직 ...
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
canContainer.textContent = 'No CAN fields configured.';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function collectData() {
|
|
|
|
|
const res = {...data};
|
|
|
|
|
|
|
|
|
|
res.wifi_static = document.querySelector('input[name="ipMode"]:checked')?.value === 'static'
|
|
|
|
|
? 'on' : 'off';
|
|
|
|
|
|
|
|
|
|
/* 일반 input/select */
|
|
|
|
|
for (const k in res) {
|
|
|
|
|
const el = document.getElementById(k);
|
|
|
|
|
if (el) res[k] = el.value.trim();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* IMU select → 축·부호 분리 */
|
|
|
|
|
['x', 'y', 'z'].forEach(a => {
|
|
|
|
|
const {axis, sign} = axisValueToPair(document.getElementById(`imu_remap_${a}`).value);
|
|
|
|
|
res[`imu_remap_${a}`] = axis;
|
|
|
|
|
res[`imu_remap_${a}_sign`] = sign;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* SSID 배열 */
|
|
|
|
|
res.WIFI_SSID = gatherSsidRows();
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function collectConfigData() {
|
|
|
|
|
const collectedRegister = {...lastLoadedRegisterData}; // DTO 필드명(camelCase)으로 값을 수집할 객체
|
|
|
|
|
|
|
|
|
|
// configIdMap을 순회하며 HTML ID로부터 값을 가져와 DTO 필드명에 매핑
|
|
|
|
|
for (const htmlId in configIdMap) {
|
|
|
|
|
const dtoKey = configIdMap[htmlId]; // HTML ID에 해당하는 DTO 필드명 (camelCase)
|
|
|
|
|
const el = document.getElementById(htmlId);
|
|
|
|
|
if (el) {
|
|
|
|
|
collectedRegister[dtoKey] = el.value !== null && el.value !== undefined ? el.value.trim() : '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 체크박스 필드 (HTML ID와 DTO 필드명이 같음)
|
|
|
|
|
// AnalogPort 매핑: true -> "1", false -> "0"
|
|
|
|
|
['ai0', 'ai1', 'ai2', 'ai3', 'di0', 'di1', 'di2', 'di3'].forEach(id => {
|
|
|
|
|
const el = document.getElementById(id);
|
|
|
|
|
if (el && el.type === 'checkbox') {
|
|
|
|
|
collectedRegister[id] = el.checked ? "1" : "0";
|
|
|
|
|
} else {
|
|
|
|
|
if (collectedRegister[id] === undefined) {
|
|
|
|
|
collectedRegister[id] = "0";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return collectedRegister;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
|
const list = document.getElementById('modbus-field-list');
|
|
|
|
|
// 처음에 행이 없으면 빈 행을 하나 추가
|
|
|
|
|
if(list.children.length === 0) {
|
|
|
|
|
const modbusRowTemplate = document.getElementById('modbus-row-template');
|
|
|
|
|
if(modbusRowTemplate) {
|
|
|
|
|
const newRow = modbusRowTemplate.content.cloneNode(true);
|
|
|
|
|
list.appendChild(newRow);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let selectedRow = null;
|
|
|
|
|
document.getElementById('modbus-field-list').addEventListener('click', function(e){
|
|
|
|
|
let tr = e.target.closest('tr');
|
|
|
|
|
if (!tr) return;
|
|
|
|
|
|
|
|
|
|
Array.from(this.children).forEach(row => row.classList.remove('selected-row'));
|
|
|
|
|
tr.classList.add('selected-row');
|
|
|
|
|
selectedRow = tr;
|
|
|
|
|
|
|
|
|
|
document.getElementById('modbus_field_input').value = tr.querySelector('.field-display').textContent;
|
|
|
|
|
document.getElementById('modbus_addr_input').value = tr.querySelector('.addr-display').textContent;
|
|
|
|
|
document.getElementById('modbus_idt_input').value = tr.querySelector('.idt-display').textContent;
|
|
|
|
|
document.getElementById('modbus_odt_input').value = tr.querySelector('.odt-display').textContent;
|
|
|
|
|
document.getElementById('modbus_dv_input').value = tr.querySelector('.dv-display').textContent;
|
|
|
|
|
document.getElementById('modbus_shift_input').value = tr.querySelector('.shift-display').textContent;
|
|
|
|
|
document.getElementById('modbus_expr_input').value = tr.querySelector('.expr-display').textContent;
|
|
|
|
|
document.getElementById('modbus_mask_input').value = tr.querySelector('.mask-display').textContent;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const modbusAddBtn = document.getElementById('modbus_add_btn');
|
|
|
|
|
const modbusFieldList = document.getElementById('modbus-field-list');
|
|
|
|
|
const modbusRowTemplate = document.getElementById('modbus-row-template');
|
|
|
|
|
|
|
|
|
|
if(modbusAddBtn && modbusFieldList && modbusRowTemplate){
|
|
|
|
|
modbusAddBtn.addEventListener('click',()=>{
|
|
|
|
|
const field = document.getElementById('modbus_field_input').value;
|
|
|
|
|
const addr = document.getElementById('modbus_addr_input').value;
|
|
|
|
|
const idt = document.getElementById('modbus_idt_input').value;
|
|
|
|
|
const odt = document.getElementById('modbus_odt_input').value;
|
|
|
|
|
const defaultValue = document.getElementById('modbus_dv_input').value;
|
|
|
|
|
const shift = document.getElementById('modbus_shift_input').value;
|
|
|
|
|
const expr = document.getElementById('modbus_expr_input').value;
|
|
|
|
|
const mask = document.getElementById('modbus_mask_input').value;
|
|
|
|
|
|
|
|
|
|
const newRow = modbusRowTemplate.content.cloneNode(true);
|
|
|
|
|
newRow.querySelector('.field-display').textContent = field;
|
|
|
|
|
newRow.querySelector('.addr-display').textContent = addr;
|
|
|
|
|
newRow.querySelector('.idt-display').textContent = idt;
|
|
|
|
|
newRow.querySelector('.odt-display').textContent = odt;
|
|
|
|
|
newRow.querySelector('.dv-display').textContent = defaultValue;
|
|
|
|
|
newRow.querySelector('.shift-display').textContent = shift;
|
|
|
|
|
newRow.querySelector('.expr-display').textContent = expr;
|
|
|
|
|
newRow.querySelector('.mask-display').textContent = mask;
|
|
|
|
|
|
|
|
|
|
modbusFieldList.appendChild(newRow);
|
|
|
|
|
|
|
|
|
|
document.getElementById('modbus_field_input').value = '';
|
|
|
|
|
document.getElementById('modbus_addr_input').value = '';
|
|
|
|
|
document.getElementById('modbus_idt_input').value = '';
|
|
|
|
|
document.getElementById('modbus_odt_input').value = '';
|
|
|
|
|
document.getElementById('modbus_dv_input').value = '';
|
|
|
|
|
document.getElementById('modbus_shift_input').value = '';
|
|
|
|
|
document.getElementById('modbus_expr_input').value = '';
|
|
|
|
|
document.getElementById('modbus_mask_input').value = '';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.getElementById('modbus_modify_btn').onclick = function() {
|
|
|
|
|
if (!selectedRow) return alert('수정할 행을 선택하세요!');
|
|
|
|
|
selectedRow.querySelector('.field-display').textContent = document.getElementById('modbus_field_input').value;
|
|
|
|
|
selectedRow.querySelector('.addr-display').textContent = document.getElementById('modbus_addr_input').value;
|
|
|
|
|
selectedRow.querySelector('.idt-display').textContent = document.getElementById('modbus_idt_input').value;
|
|
|
|
|
selectedRow.querySelector('.odt-display').textContent = document.getElementById('modbus_odt_input').value;
|
|
|
|
|
selectedRow.querySelector('.dv-display').textContent = document.getElementById('modbus_dv_input').value;
|
|
|
|
|
selectedRow.querySelector('.shift-display').textContent = document.getElementById('modbus_shift_input').value;
|
|
|
|
|
selectedRow.querySelector('.expr-display').textContent = document.getElementById('modbus_expr_input').value;
|
|
|
|
|
selectedRow.querySelector('.mask-display').textContent = document.getElementById('modbus_mask_input').value;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 삭제 버튼
|
|
|
|
|
document.getElementById('modbus_delete_btn').onclick = function() {
|
|
|
|
|
if (!selectedRow) return alert('삭제할 행을 선택하세요!');
|
|
|
|
|
const list = document.getElementById('modbus-field-list');
|
|
|
|
|
selectedRow.remove();
|
|
|
|
|
selectedRow = null;
|
|
|
|
|
document.querySelectorAll('.modbus-input-area input').forEach(i => i.value = '');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
regProtocolSelect = document.getElementById('reg_protocol');
|
|
|
|
|
if(regProtocolSelect) {
|
|
|
|
|
regProtocolSelect.addEventListener('change', () => {
|
|
|
|
|
console.log('regProtocolSelect 변경 감지',regProtocolSelect.value);
|
|
|
|
|
toggleProtocolMenus();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
toggleProtocolMenus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.getElementById('loadDeviceBtn_ssid')?.addEventListener('click', () => {
|
|
|
|
|
fetch('http://localhost:8080/setting/get-device')
|
|
|
|
|
.then(res => {
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
console.error(`HTTP error! status: ${res.status}`);
|
|
|
|
|
throw new Error('Device file not found or server error');
|
|
|
|
|
}
|
|
|
|
|
return res.json();
|
|
|
|
|
})
|
|
|
|
|
.then(deviceJsonData => {
|
|
|
|
|
loadFromData(deviceJsonData);
|
|
|
|
|
alert('Device 설정 파일 로드 완료');
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.error('Device 설정 파일 로드 중 오류 발생:', error);
|
|
|
|
|
alert('Device 설정 파일 로드 실패! 기본값으로 시작합니다.');
|
|
|
|
|
loadFromData(data); // 오류 발생 시 data의 초기값으로 UI 초기화
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('loadDeviceBtn_network')?.addEventListener('click', () => {
|
|
|
|
|
fetch('http://localhost:8080/setting/get-device')
|
|
|
|
|
.then(res => {
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
console.error(`HTTP error! status: ${res.status}`);
|
|
|
|
|
throw new Error('Device file not found or server error');
|
|
|
|
|
}
|
|
|
|
|
return res.json();
|
|
|
|
|
})
|
|
|
|
|
.then(deviceJsonData => {
|
|
|
|
|
loadFromData(deviceJsonData);
|
|
|
|
|
alert('Device 설정 파일 로드 완료');
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.error('Device 설정 파일 로드 중 오류 발생:', error);
|
|
|
|
|
alert('Device 설정 파일 로드 실패! 기본값으로 시작합니다.');
|
|
|
|
|
loadFromData(data); // 오류 발생 시 data의 초기값으로 UI 초기화
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('loadDeviceBtn_io')?.addEventListener('click', () => {
|
|
|
|
|
fetch('http://localhost:8080/setting/get-device')
|
|
|
|
|
.then(res => {
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
console.error(`HTTP error! status: ${res.status}`);
|
|
|
|
|
throw new Error('Device file not found or server error');
|
|
|
|
|
}
|
|
|
|
|
return res.json();
|
|
|
|
|
})
|
|
|
|
|
.then(deviceJsonData => {
|
|
|
|
|
loadFromData(deviceJsonData);
|
|
|
|
|
alert('Device 설정 파일 로드 완료');
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.error('Device 설정 파일 로드 중 오류 발생:', error);
|
|
|
|
|
alert('Device 설정 파일 로드 실패! 기본값으로 시작합니다.');
|
|
|
|
|
loadFromData(data); // 오류 발생 시 data의 초기값으로 UI 초기화
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('saveDeviceBtn_ssid').addEventListener('click', () => {
|
|
|
|
|
fetch('http://localhost:8080/setting/device', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
|
|
body: JSON.stringify(collectData())
|
|
|
|
|
})
|
|
|
|
|
.then(() => alert('전송 완료'))
|
|
|
|
|
.catch(console.error);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('saveDeviceBtn_network').addEventListener('click', () => {
|
|
|
|
|
fetch('http://localhost:8080/setting/device', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
|
|
body: JSON.stringify(collectData())
|
|
|
|
|
})
|
|
|
|
|
.then(() => alert('전송 완료'))
|
|
|
|
|
.catch(console.error);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('saveDeviceBtn_io').addEventListener('click', () => {
|
|
|
|
|
fetch('http://localhost:8080/setting/device', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
|
|
body: JSON.stringify(collectData())
|
|
|
|
|
})
|
|
|
|
|
.then(() => alert('전송 완료'))
|
|
|
|
|
.catch(console.error);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('loadProtocolBtn').addEventListener('click', () => {
|
|
|
|
|
console.log('버튼 클릭 됨');
|
|
|
|
|
|
|
|
|
|
fetch('http://localhost:8080/setting/get-register')
|
|
|
|
|
.then(res => {
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
console.error(`HTTP error! status: ${res.status}`);
|
|
|
|
|
throw new Error('Register file not found or server error');
|
|
|
|
|
}
|
|
|
|
|
return res.json();
|
|
|
|
|
})
|
|
|
|
|
.then(apiResponseData => {
|
|
|
|
|
if (apiResponseData && apiResponseData.body) {
|
|
|
|
|
loadFromRegisterJson(apiResponseData.body);
|
|
|
|
|
alert('Protocol 설정 파일 로드 완료');
|
|
|
|
|
} else {
|
|
|
|
|
console.error('API 응답에 body 필드가 없습니다.', apiResponseData);
|
|
|
|
|
alert('Protocol 설정 파일 로드 실패! 유효한 응답 형식이 아닙니다.');
|
|
|
|
|
loadFromRegisterJson(configData);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.error('Protocol 설정 파일 로드 중 오류 발생:', error);
|
|
|
|
|
alert('Protocol 설정 파일 로드 실패! ' + error.message + ' 기본값으로 시작합니다.');
|
|
|
|
|
loadFromRegisterJson(configData); // 오류 발생 시 configData의 초기값으로 UI 초기화
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('loadModbusBtn').addEventListener('click', () => {
|
|
|
|
|
console.log('버튼 클릭 됨');
|
|
|
|
|
|
|
|
|
|
fetch('http://localhost:8080/setting/get-register')
|
|
|
|
|
.then(res => {
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
console.error(`HTTP error! status: ${res.status}`);
|
|
|
|
|
throw new Error('Register file not found or server error');
|
|
|
|
|
}
|
|
|
|
|
return res.json();
|
|
|
|
|
})
|
|
|
|
|
.then(apiResponseData => {
|
|
|
|
|
if (apiResponseData && apiResponseData.body) {
|
|
|
|
|
loadFromRegisterJson(apiResponseData.body);
|
|
|
|
|
alert('Protocol 설정 파일 로드 완료');
|
|
|
|
|
} else {
|
|
|
|
|
console.error('API 응답에 body 필드가 없습니다.', apiResponseData);
|
|
|
|
|
alert('Protocol 설정 파일 로드 실패! 유효한 응답 형식이 아닙니다.');
|
|
|
|
|
loadFromRegisterJson(configData);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.error('Protocol 설정 파일 로드 중 오류 발생:', error);
|
|
|
|
|
alert('Protocol 설정 파일 로드 실패! ' + error.message + ' 기본값으로 시작합니다.');
|
|
|
|
|
loadFromRegisterJson(configData); // 오류 발생 시 configData의 초기값으로 UI 초기화
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('loadOpcUaFileBtn').addEventListener('click', () => {
|
|
|
|
|
console.log('버튼 클릭 됨');
|
|
|
|
|
|
|
|
|
|
fetch('http://localhost:8080/setting/get-register')
|
|
|
|
|
.then(res => {
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
console.error(`HTTP error! status: ${res.status}`);
|
|
|
|
|
throw new Error('Register file not found or server error');
|
|
|
|
|
}
|
|
|
|
|
return res.json();
|
|
|
|
|
})
|
|
|
|
|
.then(apiResponseData => {
|
|
|
|
|
if (apiResponseData && apiResponseData.body) {
|
|
|
|
|
loadFromRegisterJson(apiResponseData.body);
|
|
|
|
|
alert('Protocol 설정 파일 로드 완료');
|
|
|
|
|
} else {
|
|
|
|
|
console.error('API 응답에 body 필드가 없습니다.', apiResponseData);
|
|
|
|
|
alert('Protocol 설정 파일 로드 실패! 유효한 응답 형식이 아닙니다.');
|
|
|
|
|
loadFromRegisterJson(configData);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.error('Protocol 설정 파일 로드 중 오류 발생:', error);
|
|
|
|
|
alert('Protocol 설정 파일 로드 실패! ' + error.message + ' 기본값으로 시작합니다.');
|
|
|
|
|
loadFromRegisterJson(configData); // 오류 발생 시 configData의 초기값으로 UI 초기화
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('saveProtocolBtn').addEventListener('click', () => {
|
|
|
|
|
console.log('버튼 클릭 됨');
|
|
|
|
|
console.log(collectConfigData());
|
|
|
|
|
fetch('http://localhost:8080/setting/register', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
|
|
body: JSON.stringify(collectConfigData())
|
|
|
|
|
})
|
|
|
|
|
.then(res => {
|
|
|
|
|
if (res.ok) {
|
|
|
|
|
alert('Register 설정 전송 완료');
|
|
|
|
|
} else {
|
|
|
|
|
alert('Register 설정 전송 실패: ' + res.statusText);
|
|
|
|
|
console.error('Register 설정 전송 실패:', res); // 응답 객체 자체를 로깅
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.error('Register 설정 전송 중 오류 발생:', error); // 네트워크 오류 로깅
|
|
|
|
|
alert('Register 설정 전송 중 통신 오류 발생');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('saveModbusBtn').addEventListener('click', () => {
|
|
|
|
|
console.log('버튼 클릭 됨');
|
|
|
|
|
fetch('http://localhost:8080/setting/register', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
|
|
body: JSON.stringify(collectConfigData())
|
|
|
|
|
})
|
|
|
|
|
.then(res => {
|
|
|
|
|
if (res.ok) {
|
|
|
|
|
alert('Register 설정 전송 완료');
|
|
|
|
|
} else {
|
|
|
|
|
alert('Register 설정 전송 실패: ' + res.statusText);
|
|
|
|
|
console.error('Register 설정 전송 실패:', res); // 응답 객체 자체를 로깅
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.error('Register 설정 전송 중 오류 발생:', error); // 네트워크 오류 로깅
|
|
|
|
|
alert('Register 설정 전송 중 통신 오류 발생');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('saveOpcUaFileBtn').addEventListener('click', () => {
|
|
|
|
|
console.log('버튼 클릭 됨');
|
|
|
|
|
fetch('http://localhost:8080/setting/register', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
|
|
body: JSON.stringify(collectConfigData())
|
|
|
|
|
})
|
|
|
|
|
.then(res => {
|
|
|
|
|
if (res.ok) {
|
|
|
|
|
alert('Register 설정 전송 완료');
|
|
|
|
|
} else {
|
|
|
|
|
alert('Register 설정 전송 실패: ' + res.statusText);
|
|
|
|
|
console.error('Register 설정 전송 실패:', res); // 응답 객체 자체를 로깅
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.error('Register 설정 전송 중 오류 발생:', error); // 네트워크 오류 로깅
|
|
|
|
|
alert('Register 설정 전송 중 통신 오류 발생');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|