diff --git a/src/main/java/org/mobidgim/mobidigimproject/config/objectmapper/ObjectMapperConfig.java b/src/main/java/org/mobidgim/mobidigimproject/config/objectmapper/ObjectMapperConfig.java index 9809e1e..0bd2235 100644 --- a/src/main/java/org/mobidgim/mobidigimproject/config/objectmapper/ObjectMapperConfig.java +++ b/src/main/java/org/mobidgim/mobidigimproject/config/objectmapper/ObjectMapperConfig.java @@ -21,7 +21,7 @@ public class ObjectMapperConfig { objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false); objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.SnakeCaseStrategy()); +// objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.SnakeCaseStrategy()); return objectMapper; } diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/RegisterDTO.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/RegisterDTO.java index 78193f4..cf3c353 100644 --- a/src/main/java/org/mobidgim/mobidigimproject/model/register/RegisterDTO.java +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/RegisterDTO.java @@ -18,7 +18,7 @@ import java.util.List; @JsonInclude(JsonInclude.Include.NON_NULL) public class RegisterDTO { - private AnalogInputLevel AnalogInputLevel; + private AnalogInputLevel analogInputLevel; private AnalogPort ai0; @@ -48,10 +48,6 @@ public class RegisterDTO { private FourByte fourByte; - private InputDataType inputDataType; - - private OutputDataType outputDataType; - private Protocol protocol; private SpeedDataSource speedDataSource; @@ -62,52 +58,19 @@ public class RegisterDTO { private String meid; + private HeadingOn headingOn; + + private HeadingImuOn headingImuOn; + + private FixmodeOn fixmodeOn; + @JsonProperty("MODBUS") - private List modbusFields; + private List MODBUS; @JsonProperty("OPC_UA") - private List opcUaFields; + private List OPC_UA; @JsonProperty("CAN") - private List canFields; - -// @JsonProperty("ai0") -// public String getAi0AsString(){ -// return BoolToString.convertToAnalogPortValue(ai0); -// } -// -// @JsonProperty("ai1") -// public String getAi1AsString(){ -// return BoolToString.convertToAnalogPortValue(ai1); -// } -// -// @JsonProperty("ai2") -// public String getAi2AsString(){ -// return BoolToString.convertToAnalogPortValue(ai2); -// } -// -// @JsonProperty("ai3") -// public String getAi3AsString(){ -// return BoolToString.convertToAnalogPortValue(ai3); -// } -// -// @JsonProperty("di0") -// public String getDi0AsString(){ -// return BoolToString.convertToAnalogPortValue(di0); -// } -// -// @JsonProperty("di1") -// public String getDi1AsString(){ -// return BoolToString.convertToAnalogPortValue(di1); -// } -// -// @JsonProperty("di2") -// public String getDi2AsString(){ -// return BoolToString.convertToAnalogPortValue(di2); -// } -// -// @JsonProperty("di3") -// public String getDi3AsString(){ -// return BoolToString.convertToAnalogPortValue(di3); -// } + private List CAN; + } 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 97bed9d..5cb72a0 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 @@ -1,5 +1,6 @@ package org.mobidgim.mobidigimproject.model.register.enums; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import lombok.AllArgsConstructor; @@ -16,4 +17,15 @@ public enum AnalogInputLevel { public String getAnalogInput() { return analogInput; } + + @JsonCreator + public static AnalogInputLevel fromValue(String value) { + for (AnalogInputLevel level : AnalogInputLevel.values()) { + if (level.analogInput.equals(value)) { + return level; + } + } + // 만약 유효하지 않은 값이 들어왔을 때 처리 (예: 예외 발생 또는 기본값 반환) + throw new IllegalArgumentException("Unknown AnalogInputLevel value: " + value); + } } diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/AnalogPort.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/AnalogPort.java index 9d79a2d..a8e0774 100644 --- a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/AnalogPort.java +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/AnalogPort.java @@ -22,7 +22,6 @@ public enum AnalogPort { return AnalogPort.NOT_USED; } - for(AnalogPort analogPort : AnalogPort.values()) { if(analogPort.portValue.equals(portValue)) { return analogPort; diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/CanInput.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/CanInput.java index b149948..24fb46a 100644 --- a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/CanInput.java +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/CanInput.java @@ -1,16 +1,27 @@ package org.mobidgim.mobidigimproject.model.register.enums; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import lombok.AllArgsConstructor; @AllArgsConstructor public enum CanInput { - On("on"), - Off("off"); + ON("on"), + OFF("off"); private final String canInput; @JsonValue public String getCanInput() { return canInput; } + + @JsonCreator + public static CanInput fromValue(String value) { + for (CanInput ci : CanInput.values()) { + if (ci.canInput.equalsIgnoreCase(value)) { // 대소문자 무시를 위해 equalsIgnoreCase 사용 권장 + return ci; + } + } + throw new IllegalArgumentException("Unknown CanInput value: " + value); + } } diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/DR.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/DR.java index 3500468..195520d 100644 --- a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/DR.java +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/DR.java @@ -1,17 +1,27 @@ package org.mobidgim.mobidigimproject.model.register.enums; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import lombok.AllArgsConstructor; @AllArgsConstructor public enum DR { - On("on"), - Off("off"); + ON("on"), + OFF("off"); - private final String DR; + private final String dr; @JsonValue public String getDR() { - return DR; + return dr; + } + @JsonCreator // ⭐ 추가 ⭐ + public static DR fromValue(String value) { + for (DR drEnum : DR.values()) { + if (drEnum.dr.equalsIgnoreCase(value)) { // 대소문자 무시 (JSON 값에 따라) + return drEnum; + } + } + throw new IllegalArgumentException("Unknown DR value: " + value); } } diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/FixmodeOn.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/FixmodeOn.java new file mode 100644 index 0000000..0225d16 --- /dev/null +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/FixmodeOn.java @@ -0,0 +1,27 @@ +package org.mobidgim.mobidigimproject.model.register.enums; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public enum FixmodeOn { + ON("on"), + OFF("off"); + private final String fixmodeOn; + + @JsonValue + public String getFixmodeOn() { + return fixmodeOn; + } + @JsonCreator // ⭐ 추가 ⭐ + public static org.mobidgim.mobidigimproject.model.register.enums.FixmodeOn fromValue(String value) { + for (org.mobidgim.mobidigimproject.model.register.enums.FixmodeOn fixmodeOnEnum : org.mobidgim.mobidigimproject.model.register.enums.FixmodeOn.values()) { + if (fixmodeOnEnum.fixmodeOn.equalsIgnoreCase(value)) { // 대소문자 무시 (JSON 값에 따라) + return fixmodeOnEnum; + } + } + throw new IllegalArgumentException("Unknown DR value: " + value); + } + +} diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/HeadingImuOn.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/HeadingImuOn.java new file mode 100644 index 0000000..4d9708c --- /dev/null +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/HeadingImuOn.java @@ -0,0 +1,27 @@ +package org.mobidgim.mobidigimproject.model.register.enums; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public enum HeadingImuOn { + ON("on"), + OFF("off"); + private final String headingImuOn; + + @JsonValue + public String getHeadingImuOn() { + return headingImuOn; + } + @JsonCreator // ⭐ 추가 ⭐ + public static HeadingImuOn fromValue(String value) { + for (HeadingImuOn headingImuOnEnum : HeadingImuOn.values()) { + if (headingImuOnEnum.headingImuOn.equalsIgnoreCase(value)) { // 대소문자 무시 (JSON 값에 따라) + return headingImuOnEnum; + } + } + throw new IllegalArgumentException("Unknown HeadingImuOn value: " + value); + } +} + diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/HeadingOn.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/HeadingOn.java new file mode 100644 index 0000000..e4f31ef --- /dev/null +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/enums/HeadingOn.java @@ -0,0 +1,28 @@ +package org.mobidgim.mobidigimproject.model.register.enums; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public enum HeadingOn { + ON("on"), + OFF("off"); + private final String headingOn; + + @JsonValue + public String getHeadingOn() { + return headingOn; + } + @JsonCreator // ⭐ 추가 ⭐ + public static HeadingOn fromValue(String value) { + for (HeadingOn headingOnEnum : HeadingOn.values()) { + if (headingOnEnum.headingOn.equalsIgnoreCase(value)) { // 대소문자 무시 (JSON 값에 따라) + return headingOnEnum; + } + } + throw new IllegalArgumentException("Unknown DR value: " + value); + } +} + + diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/CanField.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/CanField.java index b61ebb2..d52a099 100644 --- a/src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/CanField.java +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/CanField.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.Setter; +import org.mobidgim.mobidigimproject.model.register.enums.OutputDataType; @Getter @Setter @@ -15,7 +16,7 @@ public class CanField { private String id; @JsonProperty("odt") // output data type - private String outputDataType; + private OutputDataType outputDataType; @JsonProperty("dv") // default value private String defaultValue; diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/ModbusField.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/ModbusField.java index 73db015..191d33f 100644 --- a/src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/ModbusField.java +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/ModbusField.java @@ -4,6 +4,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.Setter; +import org.mobidgim.mobidigimproject.model.register.enums.InputDataType; +import org.mobidgim.mobidigimproject.model.register.enums.OutputDataType; @Getter @Setter @@ -13,12 +15,12 @@ public class ModbusField { private String field; @JsonProperty("idt") // input data type - private String inputDataType; + private InputDataType inputDataType; private String addr; // address @JsonProperty("odt") // output data type - private String outputDataType; + private OutputDataType outputDataType; @JsonProperty("dv") // default value private String defaultValue; diff --git a/src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/OpcUaField.java b/src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/OpcUaField.java index ffa961b..690a832 100644 --- a/src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/OpcUaField.java +++ b/src/main/java/org/mobidgim/mobidigimproject/model/register/sebDTO/OpcUaField.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.Setter; +import org.mobidgim.mobidigimproject.model.register.enums.OutputDataType; @Getter @Setter @@ -17,7 +18,7 @@ public class OpcUaField { private String addr; // address @JsonProperty("odt") // output data type - private String outputDataType; + private OutputDataType outputDataType; @JsonProperty("dv") // default value private String defaultValue; diff --git a/src/main/java/org/mobidgim/mobidigimproject/service/SettingService.java b/src/main/java/org/mobidgim/mobidigimproject/service/SettingService.java index cada5fd..ec0329f 100644 --- a/src/main/java/org/mobidgim/mobidigimproject/service/SettingService.java +++ b/src/main/java/org/mobidgim/mobidigimproject/service/SettingService.java @@ -1,6 +1,7 @@ package org.mobidgim.mobidigimproject.service; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import org.mobidgim.mobidigimproject.model.device.DeviceDTO; @@ -31,6 +32,8 @@ public class SettingService { @Autowired public SettingService(ObjectMapper mapper) { this.mapper = mapper; + this.mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); + } @@ -59,15 +62,15 @@ public class SettingService { public RegisterDTO saveFileWithRegister(RegisterDTO request) throws IOException { if (request.getProtocol() != Protocol.OPC_UA) { - request.setOpcUaFields(null); + request.setOPC_UA(null); } if (request.getProtocol() != Protocol.MODBUS) { - request.setModbusFields(null); + request.setMODBUS(null); } - if (request.getCanInput() != CanInput.On) { - request.setCanFields(null); + if (request.getCanInput() != CanInput.ON) { + request.setCAN(null); } @@ -90,7 +93,11 @@ public class SettingService { String filename = "./temp/registerConfig.json"; Path filePath = Paths.get(filename); String jsonString = Files.readString(filePath); - return this.mapper.readValue(jsonString, RegisterDTO.class); + ObjectMapper mapper = new ObjectMapper(); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + RegisterDTO registerDTO = mapper.readValue(jsonString, RegisterDTO.class); + return registerDTO; } } diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 9b404ad..7b34564 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -32,7 +32,7 @@ .card h3{font-size:24px;margin-bottom:24px} /* SSID rows */ - .ssid-row{display:grid;grid-template-columns:1fr 1fr 1fr 72px;gap:16px;margin-bottom:12px} + .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} @@ -54,13 +54,30 @@ .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} + .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} @@ -76,8 +93,12 @@ @@ -95,18 +116,21 @@
+ -
- + +
+ +
@@ -136,6 +160,10 @@
DGPS Server
OPC-UA
+
+ + +
@@ -146,11 +174,11 @@

Ethernet

-
IP:
-
SUBNET:
-
GATEWAY:
-
SERVER IP:
-
SERVER PORT:
+
IP:
+
SUBNET:
+
GATEWAY:
+
SERVER IP:
+
SERVER PORT:
@@ -158,10 +186,10 @@

LTE

-
IP:
-
SUBNET:
-
GATEWAY:
-
Port:
+
IP:
+
SUBNET:
+
GATEWAY:
+
Port:
@@ -170,17 +198,17 @@

MODBUS 422 / 485

-
Mode: +
Mode:
-
Speed:
-
Parity: @@ -190,14 +218,14 @@

CAN

-
Type: +
Type:
-
Speed: +
Speed: @@ -220,7 +248,7 @@
-
Y: @@ -228,7 +256,7 @@
-
Z: @@ -240,18 +268,265 @@
+
+ + +
+ + + + + + + + - -
- - -
@@ -286,23 +561,104 @@ imu_remap_x_sign:'', imu_remap_y_sign:'', imu_remap_z_sign:'', /* SSID 목록 */ - WIFI_SSID:[{wifi_ssid:'',wifi_password:'',wifi_security:'none'}] + 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') + 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; + // Object.values(pages).forEach(p=>p.style.display='none'); + // pages[btn.dataset.page].style.display='block'; + // titleEl.textContent = btn.textContent; + showPage(btn.dataset.page); }); }); @@ -407,40 +763,470 @@ } loadFromData(data); - function collectData(){ - const res = {...data}; - res.wifi_static = document.querySelector('input[name="ipMode"]:checked')?.value === 'static' - ? 'on' : 'off'; + function loadFromRegisterJson(register) { // register.json 데이터 로드 + if (!register) { + console.error("Register data is null or undefined."); + return; + } - /* 일반 input/select */ - for(const k in res){ - const el = document.getElementById(k); - if(el) res[k] = el.value.trim(); + // 백엔드에서 받은 DTO 객체를 lastLoadedRegisterData에 저장합니다. + // 이 객체는 이미 camelCase 필드명을 가집니다. + 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}'.`); + } } + } - /* 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; + // 예시: MODBUS 필드를 UI에 표시하는 가상의 로직 (HTML 컨테이너 필요) + const modbusContainer = document.getElementById('modbus_fields_container'); // HTML에 이 ID의 div가 있다고 가정 + if (modbusContainer) { + modbusContainer.innerHTML = ''; // 기존 내용 지우기 + if (lastLoadedRegisterData.MODBUS && lastLoadedRegisterData.MODBUS.length > 0) { + lastLoadedRegisterData.MODBUS.forEach(field => { + const fieldDiv = document.createElement('div'); + fieldDiv.textContent = `Field: ${field.field}, Addr: ${field.addr}, IDT: ${field.idt}`; // 예시 + modbusContainer.appendChild(fieldDiv); + }); + } else { + modbusContainer.textContent = 'No MODBUS fields configured.'; + } + } + + 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"; + } + } + }); + + // MODBUS, OPC_UA, CAN 리스트 필드 처리 + if (lastLoadedRegisterData.MODBUS) { + collectedRegister.MODBUS = lastLoadedRegisterData.MODBUS; + } else { + collectedRegister.MODBUS = []; // 데이터가 없으면 빈 배열로 초기화 + } + if (lastLoadedRegisterData.OPC_UA) { + collectedRegister.OPC_UA = lastLoadedRegisterData.OPC_UA; + } else { + collectedRegister.OPC_UA = []; + } + if (lastLoadedRegisterData.CAN) { + collectedRegister.CAN = lastLoadedRegisterData.CAN; + } else { + collectedRegister.CAN = []; + } + + // input_data_type, output_data_type과 같은 필드 처리 (DTO 필드명은 camelCase로 가정) + // DTO의 필드명은 `inputDataType`, `outputDataType`일 것이므로 이에 맞춰야 합니다. + // if (lastLoadedRegisterData.inputDataType !== undefined) { + // collectedRegister.inputDataType = lastLoadedRegisterData.inputDataType; + // } else { + // // configData에 inputDataType이 없으면 빈 문자열 사용 (초기 configData에는 snake_case로 되어있었음) + // collectedRegister.inputDataType = collectedRegister.inputDataType || ''; + // } + // + // if (lastLoadedRegisterData.outputDataType !== undefined) { + // collectedRegister.outputDataType = lastLoadedRegisterData.outputDataType; + // } else { + // // configData에 outputDataType이 없으면 빈 문자열 사용 + // collectedRegister.outputDataType = collectedRegister.outputDataType || ''; + // } + + + // 5. 현재 UI에 선택된 프로토콜과 CAN Input 값에 따라 리스트 필드를 `null`로 만들지 여부 결정 (선택 사항) + // 백엔드 `SettingService`의 `saveFileWithRegister` 메서드에서 이미 이 로직을 처리하고 있으므로, + // 프론트엔드에서 굳이 이 로직을 중복해서 넣지 않아도 됩니다. + // 다만, 프론트엔드에서 정확한 데이터를 보내고 싶다면 유지해도 무방합니다. + // 여기서는 UI 값에 따라 `null`로 설정하는 로직을 유지하면서, + // 'canInput'의 대소문자 문제만 수정합니다. + + const currentProtocolFromUI = collectedRegister.protocol; + // ⭐ 중요: UI의 option value="ON"과 일치하도록 'ON' 사용 + const currentCanInputFromUI = collectedRegister.canInput; + + if (currentProtocolFromUI !== 'OPC_UA') { + collectedRegister.opcUaFields = null; + } + if (currentProtocolFromUI !== 'MODBUS') { + collectedRegister.modbusFields = null; + } + // ⭐ 수정: 'On' -> 'ON' (UI select option 값과 일치) + if (currentCanInputFromUI !== 'ON') { + collectedRegister.canFields = null; + } + + // 디버깅을 위해 전송 직전의 데이터 로깅 + console.log("Collected Register Data before sending:", collectedRegister); + + return collectedRegister; + } + + document.addEventListener('DOMContentLoaded', () => { + + + 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('loadModbusFileBtn').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('버튼 클릭 됨'); + 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('saveModbusFileBtn').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 설정 전송 중 통신 오류 발생'); + }); + }); + + const regProtocolSelect = document.getElementById('reg_protocol'); + const modbusSidebarBtn = document.getElementById('modbusSidebarBtn'); + const opcUaSidebarBtn = document.getElementById('opcUaSidebarBtn'); + + function toggleProtocolMenus() { + const selectProtocol = regProtocolSelect.value; + + 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'; + } + + const currentPageId = Object.keys(pages).find(key => pages[key].style.display === 'block'); + if (currentPageId === 'modbus' && selectProtocol !== 'MODBUS'){ + showPage('reg'); + } + } + toggleProtocolMenus(); + + regProtocolSelect.addEventListener('change', toggleProtocolMenus); }); - /* SSID 배열 */ - res.WIFI_SSID = gatherSsidRows(); - return res; - } - document.getElementById('save-device').addEventListener('click', ()=>{ - fetch('http://localhost:8080/setting/device',{ - method :'POST', - headers:{'Content-Type':'application/json'}, - body : JSON.stringify(collectData()) - }) - .then(()=>alert('전송 완료')) - .catch(console.error); - }); + \ No newline at end of file