diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/AnalogInputLevel.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/AnalogInputLevel.java index 5cb72a0..8e0be0e 100644 --- a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/AnalogInputLevel.java +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/AnalogInputLevel.java @@ -6,9 +6,9 @@ import lombok.AllArgsConstructor; @AllArgsConstructor public enum AnalogInputLevel { - two("2"), - four("4"), - six("6"), + TWO("2"), + FOUR("4"), + SIX("6"), ; private final String analogInput; diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/FourByte.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/FourByte.java index f504b55..b184860 100644 --- a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/FourByte.java +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/FourByte.java @@ -5,10 +5,10 @@ import lombok.AllArgsConstructor; @AllArgsConstructor public enum FourByte { - little("little"), - littleSwap("littleSwap"), - bigSwap("bigSwap"), - big("big"); + LITTLE("little"), + LITTLE_SWAP("littleSwap"), + BIG_SWAP("bigSwap"), + BIG("big"); private final String fourByte; @JsonValue diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/InputDataType.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/InputDataType.java index 64ffa95..6e07ec7 100644 --- a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/InputDataType.java +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/InputDataType.java @@ -6,7 +6,7 @@ import lombok.AllArgsConstructor; @AllArgsConstructor public enum InputDataType { integer("integer"), - flo("float"), + flo("float64"), bool("boolean"), unsignedInteger("unsignedInteger"), shor("short"), diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/TwoByte.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/TwoByte.java index 8c27ad7..6651d43 100644 --- a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/TwoByte.java +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/TwoByte.java @@ -5,8 +5,8 @@ import lombok.AllArgsConstructor; @AllArgsConstructor public enum TwoByte { - little("little"), - big("big"); + LITTLE("little"), + BIG("big"); private final String twoByte; @JsonValue diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 6a6c628..ed24fcf 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -80,101 +80,54 @@ .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; - } + #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; - } + #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;} - .selected-row{ - background: #d0e6ff !important; - } - .selected-row > td { - background: #d0e6ff !important; - } @@ -195,6 +148,7 @@ + @@ -446,8 +400,8 @@ @@ -538,11 +492,25 @@ - + - + @@ -586,11 +554,14 @@ - - - - - + + + + + + + + @@ -599,22 +570,28 @@
-
- - -
-
- - -
-
- - -
-
- - -
+ + + + + + + + + + + + + + + +
@@ -624,12 +601,78 @@
-
+
+ + +
fieldaddrnsodtdvFieldAddressNSODTDefault ValueShiftExpressionMask
+ + + + + + + + + + + + + +
FieldIDODTDefault ValueShiftExpressionMask
+ +
+ + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + +
+ + @@ -733,7 +776,8 @@ io : document.getElementById('page-io'), reg : document.getElementById('page-reg'), modbus : document.getElementById('page-modbus'), - opcua : document.getElementById('page-opcua') + opcua : document.getElementById('page-opcua'), + can : document.getElementById('page-can') }; const titleEl = document.getElementById('page-title'); @@ -872,18 +916,22 @@ const selectProtocol = regProtocolSelect.value; const modbusSidebarBtn = document.getElementById('modbusSidebarBtn'); const opcUaSidebarBtn = document.getElementById('opcUaSidebarBtn'); - + const canSidebarBtn = document.getElementById('canSidebarBtn'); if(modbusSidebarBtn) modbusSidebarBtn.style.display = 'none'; if(opcUaSidebarBtn) opcUaSidebarBtn.style.display = 'none'; - + if(canSidebarBtn) canSidebarBtn.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 메뉴를 자동으로 펼치기 + // CAN Input이 on일 때만 CAN 메뉴 보이기 + const canInputEl = document.getElementById('reg_can_input'); + if(canInputEl && canInputEl.value === 'on'){ + if(canSidebarBtn) canSidebarBtn.style.display = 'block'; + } + // Protocol configuration 메뉴 자동 펼치기 const protocolDetails = document.querySelectorAll('details.nav')[1]; if (protocolDetails) protocolDetails.open = true; } @@ -961,28 +1009,53 @@ } } - 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 필드 표시 로직 ... + + // OPC_UA UI 테이블 렌더링 + const opcUaFieldList = document.getElementById('opcua-field-list'); + const opcUaRowTemplate = document.getElementById('opcua-row-template'); + if (opcUaFieldList && opcUaRowTemplate) { + opcUaFieldList.innerHTML = ''; + if (Array.isArray(register.OPC_UA) && register.OPC_UA.length > 0) { + register.OPC_UA.forEach(field => { + const newRow = opcUaRowTemplate.content.cloneNode(true); + newRow.querySelector('.opcua-field-display').textContent = field.field ?? ''; + newRow.querySelector('.opcua-addr-display').textContent = field.addr ?? ''; + newRow.querySelector('.opcua-ns-display').textContent = field.idt ?? ''; + newRow.querySelector('.opcua-odt-display').textContent = field.odt ?? ''; + newRow.querySelector('.opcua-dv-display').textContent = field.dv ?? ''; + newRow.querySelector('.opcua-shift-display').textContent = field.shift ?? ''; + newRow.querySelector('.opcua-expr-display').textContent = field.expr ?? ''; + newRow.querySelector('.opcua-mask-display').textContent = field.mask ?? ''; + opcUaFieldList.appendChild(newRow); }); } else { - opcUaContainer.textContent = 'No OPC_UA fields configured.'; + // OPC_UA 데이터가 없으면 빈 행 하나 추가 + const newRow = opcUaRowTemplate.content.cloneNode(true); + opcUaFieldList.appendChild(newRow); } } - 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.'; - } + + // CAN 테이블 렌더링 + const canFieldList = document.getElementById('can-field-list'); + const canRowTemplate = document.getElementById('can-row-template'); + canFieldList.innerHTML = ''; + if (Array.isArray(register.CAN) && register.CAN.length > 0) { + register.CAN.forEach(field => { + const newRow = canRowTemplate.content.cloneNode(true); + newRow.querySelector('.can-field-display').textContent = field.field ?? ''; + newRow.querySelector('.can-id-display').textContent = field.id ?? ''; + newRow.querySelector('.can-odt-display').textContent = field.odt ?? ''; + newRow.querySelector('.can-dv-display').textContent = field.dv ?? ''; + newRow.querySelector('.can-shift-display').textContent = field.shift ?? ''; + newRow.querySelector('.can-expr-display').textContent = field.expr ?? ''; + newRow.querySelector('.can-mask-display').textContent = field.mask ?? ''; + canFieldList.appendChild(newRow); + }); + } else { + const newRow = canRowTemplate.content.cloneNode(true); + canFieldList.appendChild(newRow); } + } @@ -1011,19 +1084,16 @@ } function collectConfigData() { - const collectedRegister = {...lastLoadedRegisterData}; // DTO 필드명(camelCase)으로 값을 수집할 객체 - + const collectedRegister = {...lastLoadedRegisterData}; // configIdMap을 순회하며 HTML ID로부터 값을 가져와 DTO 필드명에 매핑 for (const htmlId in configIdMap) { - const dtoKey = configIdMap[htmlId]; // HTML ID에 해당하는 DTO 필드명 (camelCase) + const dtoKey = configIdMap[htmlId]; 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') { @@ -1034,6 +1104,79 @@ } } }); + // MODBUS 테이블 수집 (빈 값은 필드에서 제외) + const modbusRows = document.querySelectorAll('#modbus-field-list tr'); + collectedRegister.MODBUS = []; + modbusRows.forEach(row => { + const field = row.querySelector('.field-display')?.textContent.trim(); + if (field) { + const obj = {}; + obj.field = field; + const addr = row.querySelector('.addr-display')?.textContent.trim(); + const idt = row.querySelector('.idt-display')?.textContent.trim(); + const odt = row.querySelector('.odt-display')?.textContent.trim(); + const dv = row.querySelector('.dv-display')?.textContent.trim(); + const shift = row.querySelector('.shift-display')?.textContent.trim(); + const expr = row.querySelector('.expr-display')?.textContent.trim(); + const mask = row.querySelector('.mask-display')?.textContent.trim(); + if (addr) obj.addr = addr; + if (idt) obj.idt = idt; + if (odt) obj.odt = odt; + if (dv) obj.dv = dv; + if (shift) obj.shift = shift; + if (expr) obj.expr = expr; + if (mask) obj.mask = mask; + collectedRegister.MODBUS.push(obj); + } + }); + // OPC_UA 테이블 수집 (빈 값은 필드에서 제외) + const opcuaRows = document.querySelectorAll('#opcua-field-list tr'); + collectedRegister.OPC_UA = []; + opcuaRows.forEach(row => { + const field = row.querySelector('.opcua-field-display')?.textContent.trim(); + if (field) { + const obj = {}; + obj.field = field; + const addr = row.querySelector('.opcua-addr-display')?.textContent.trim(); + const ns = row.querySelector('.opcua-ns-display')?.textContent.trim(); + const odt = row.querySelector('.opcua-odt-display')?.textContent.trim(); + const dv = row.querySelector('.opcua-dv-display')?.textContent.trim(); + const shift = row.querySelector('.opcua-shift-display')?.textContent.trim(); + const expr = row.querySelector('.opcua-expr-display')?.textContent.trim(); + const mask = row.querySelector('.opcua-mask-display')?.textContent.trim(); + if (addr) obj.addr = addr; + if (ns) obj.ns = ns; + if (odt) obj.odt = odt; + if (dv) obj.dv = dv; + if (shift) obj.shift = shift; + if (expr) obj.expr = expr; + if (mask) obj.mask = mask; + collectedRegister.OPC_UA.push(obj); + } + }); + // CAN 테이블 수집 (빈 값은 필드에서 제외) + const canRows = document.querySelectorAll('#can-field-list tr'); + collectedRegister.CAN = []; + canRows.forEach(row => { + const field = row.querySelector('.can-field-display')?.textContent.trim(); + if (field) { + const obj = {}; + obj.field = field; + const id = row.querySelector('.can-id-display')?.textContent.trim(); + const odt = row.querySelector('.can-odt-display')?.textContent.trim(); + const dv = row.querySelector('.can-dv-display')?.textContent.trim(); + const shift = row.querySelector('.can-shift-display')?.textContent.trim(); + const expr = row.querySelector('.can-expr-display')?.textContent.trim(); + const mask = row.querySelector('.can-mask-display')?.textContent.trim(); + if (id) obj.id = id; + if (odt) obj.odt = odt; + if (dv) obj.dv = dv; + if (shift) obj.shift = shift; + if (expr) obj.expr = expr; + if (mask) obj.mask = mask; + collectedRegister.CAN.push(obj); + } + }); return collectedRegister; } @@ -1066,6 +1209,33 @@ document.getElementById('modbus_mask_input').value = tr.querySelector('.mask-display').textContent; }); + const opcUalist = document.getElementById('opcua-field-list'); + // 처음에 행이 없으면 빈 행을 하나 추가 + if(opcUalist.children.length === 0) { + const opcUaRowTemplate = document.getElementById('opcua-row-template'); + if(opcUaRowTemplate) { + const newRow = opcUaRowTemplate.content.cloneNode(true); + opcUalist.appendChild(newRow); + } + } + document.getElementById('opcua-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('opcua_field_input').value = tr.querySelector('.opcua-field-display').textContent; + document.getElementById('opcua_addr_input').value = tr.querySelector('.opcua-addr-display').textContent; + document.getElementById('opcua_ns_input').value = tr.querySelector('.opcua-ns-display').textContent; + document.getElementById('opcua_odt_input').value = tr.querySelector('.opcua-odt-display').textContent; + document.getElementById('opcua_dv_input').value = tr.querySelector('.opcua-dv-display').textContent; + document.getElementById('opcua_shift_input').value = tr.querySelector('.opcua-shift-display').textContent; + document.getElementById('opcua_expr_input').value = tr.querySelector('.opcua-expr-display').textContent; + document.getElementById('opcua_mask_input').value = tr.querySelector('.opcua-mask-display').textContent; + }); + @@ -1128,6 +1298,110 @@ document.querySelectorAll('.modbus-input-area input').forEach(i => i.value = ''); }; + const opcUaAddBtn = document.getElementById('opcua_add_btn'); + const opcUaFieldList = document.getElementById('opcua-field-list'); + const opcUaRowTemplate = document.getElementById('opcua-row-template'); + + if(opcUaAddBtn && opcUaFieldList && opcUaRowTemplate){ + opcUaAddBtn.addEventListener('click',()=>{ + const field = document.getElementById('opcua_field_input').value; + const addr = document.getElementById('opcua_addr_input').value; + const ns = document.getElementById('opcua_ns_input').value; + const odt = document.getElementById('opcua_odt_input').value; + const defaultValue = document.getElementById('opcua_dv_input').value; + const shift = document.getElementById('opcua_shift_input').value; + const expr = document.getElementById('opcua_expr_input').value; + const mask = document.getElementById('opcua_mask_input').value; + + const newRow = opcUaRowTemplate.content.cloneNode(true); + newRow.querySelector('.opcua-field-display').textContent = field; + newRow.querySelector('.opcua-addr-display').textContent = addr; + newRow.querySelector('.opcua-ns-display').textContent = ns; + newRow.querySelector('.opcua-odt-display').textContent = odt; + newRow.querySelector('.opcua-dv-display').textContent = defaultValue; + newRow.querySelector('.opcua-shift-display').textContent = shift; + newRow.querySelector('.opcua-expr-display').textContent = expr; + newRow.querySelector('.opcua-mask-display').textContent = mask; + + opcUaFieldList.appendChild(newRow); + + document.getElementById('opcua_field_input').value = ''; + document.getElementById('opcua_addr_input').value = ''; + document.getElementById('opcua_ns_input').value = ''; + document.getElementById('opcua_odt_input').value = ''; + document.getElementById('opcua_dv_input').value = ''; + document.getElementById('opcua_shift_input').value = ''; + document.getElementById('opcua_expr_input').value = ''; + document.getElementById('opcua_mask_input').value = ''; + }); + } + + document.getElementById('opcua_modify_btn').onclick = function() { + if (!selectedRow) return alert('수정할 행을 선택하세요!'); + selectedRow.querySelector('.opcua-field-display').textContent = document.getElementById('opcua_field_input').value; + selectedRow.querySelector('.opcua-addr-display').textContent = document.getElementById('opcua_addr_input').value; + selectedRow.querySelector('.opcua-ns-display').textContent = document.getElementById('opcua_ns_input').value; + selectedRow.querySelector('.opcua-odt-display').textContent = document.getElementById('opcua_odt_input').value; + selectedRow.querySelector('.opcua-dv-display').textContent = document.getElementById('opcua_dv_input').value; + selectedRow.querySelector('.opcua-shift-display').textContent = document.getElementById('opcua_shift_input').value; + selectedRow.querySelector('.opcua-expr-display').textContent = document.getElementById('opcua_expr_input').value; + selectedRow.querySelector('.opcua-mask-display').textContent = document.getElementById('opcua_mask_input').value; + }; + + // 삭제 버튼 + document.getElementById('opcua_delete_btn').onclick = function() { + if (!selectedRow) return alert('삭제할 행을 선택하세요!'); + const list = document.getElementById('opcua-field-list'); + selectedRow.remove(); + selectedRow = null; + document.querySelectorAll('.opcua-input-area input').forEach(i => i.value = ''); + }; + + // CAN 테이블 + document.getElementById('can_add_btn').addEventListener('click',()=>{ + const field = document.getElementById('can_field_input').value; + const id = document.getElementById('can_id_input').value; + const odt = document.getElementById('can_odt_input').value; + const dv = document.getElementById('can_dv_input').value; + const shift = document.getElementById('can_shift_input').value; + const expr = document.getElementById('can_expr_input').value; + const mask = document.getElementById('can_mask_input').value; + const canRowTemplate = document.getElementById('can-row-template'); + const newRow = canRowTemplate.content.cloneNode(true); + newRow.querySelector('.can-field-display').textContent = field; + newRow.querySelector('.can-id-display').textContent = id; + newRow.querySelector('.can-odt-display').textContent = odt; + newRow.querySelector('.can-dv-display').textContent = dv; + newRow.querySelector('.can-shift-display').textContent = shift; + newRow.querySelector('.can-expr-display').textContent = expr; + newRow.querySelector('.can-mask-display').textContent = mask; + canFieldList.appendChild(newRow); + document.getElementById('can_field_input').value = ''; + document.getElementById('can_id_input').value = ''; + document.getElementById('can_odt_input').value = ''; + document.getElementById('can_dv_input').value = ''; + document.getElementById('can_shift_input').value = ''; + document.getElementById('can_expr_input').value = ''; + document.getElementById('can_mask_input').value = ''; + }); + document.getElementById('can_modify_btn').onclick = function() { + if (!selectedRow) return alert('수정할 행을 선택하세요!'); + selectedRow.querySelector('.can-field-display').textContent = document.getElementById('can_field_input').value; + selectedRow.querySelector('.can-id-display').textContent = document.getElementById('can_id_input').value; + selectedRow.querySelector('.can-odt-display').textContent = document.getElementById('can_odt_input').value; + selectedRow.querySelector('.can-dv-display').textContent = document.getElementById('can_dv_input').value; + selectedRow.querySelector('.can-shift-display').textContent = document.getElementById('can_shift_input').value; + selectedRow.querySelector('.can-expr-display').textContent = document.getElementById('can_expr_input').value; + selectedRow.querySelector('.can-mask-display').textContent = document.getElementById('can_mask_input').value; + }; + document.getElementById('can_delete_btn').onclick = function() { + if (!selectedRow) return alert('삭제할 행을 선택하세요!'); + const list = document.getElementById('can-field-list'); + selectedRow.remove(); + selectedRow = null; + document.querySelectorAll('.can-input-area input').forEach(i => i.value = ''); + }; + regProtocolSelect = document.getElementById('reg_protocol'); if(regProtocolSelect) { @@ -1377,6 +1651,58 @@ alert('Register 설정 전송 중 통신 오류 발생'); }); }); + + document.getElementById('reg_can_input')?.addEventListener('change', toggleProtocolMenus);//-------------------------------- + + // DOMContentLoaded 내부에 추가: CAN 테이블도 처음에 빈 행이 없으면 하나 추가 + const canFieldList = document.getElementById('can-field-list'); + if(canFieldList.children.length === 0) { + const canRowTemplate = document.getElementById('can-row-template'); + if(canRowTemplate) { + const newRow = canRowTemplate.content.cloneNode(true); + canFieldList.appendChild(newRow); + } + } + // CAN row 클릭 시 선택 표시 및 입력폼에 값 반영 + canFieldList.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('can_field_input').value = tr.querySelector('.can-field-display').textContent; + document.getElementById('can_id_input').value = tr.querySelector('.can-id-display').textContent; + document.getElementById('can_odt_input').value = tr.querySelector('.can-odt-display').textContent; + document.getElementById('can_dv_input').value = tr.querySelector('.can-dv-display').textContent; + document.getElementById('can_shift_input').value = tr.querySelector('.can-shift-display').textContent; + document.getElementById('can_expr_input').value = tr.querySelector('.can-expr-display').textContent; + document.getElementById('can_mask_input').value = tr.querySelector('.can-mask-display').textContent; + }); + }); + + document.getElementById('loadCanBtn').addEventListener('click', () => { + fetch('http://localhost:8080/setting/get-register') + .then(res => res.json()) + .then(apiResponseData => { + if (apiResponseData && apiResponseData.body) { + loadFromRegisterJson(apiResponseData.body); + alert('Protocol 설정 파일 로드 완료'); + } + }); + }); + document.getElementById('saveCanBtn').addEventListener('click', () => { + 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); + } + }); });