Browse Source

load local file 버튼 로직 수정

v2
gudae01 1 year ago
parent
commit
76392631f2
  1. 141
      src/main/java/org/mobidgim/mobidigimproject/service/SettingService.java
  2. 186
      src/main/resources/static/index.html

141
src/main/java/org/mobidgim/mobidigimproject/service/SettingService.java

@ -1,7 +1,6 @@
package org.mobidgim.mobidigimproject.service; package org.mobidgim.mobidigimproject.service;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.PropertyNamingStrategies;
@ -9,51 +8,100 @@ import org.mobidgim.mobidigimproject.model.device.DeviceDTO;
import org.mobidgim.mobidigimproject.model.register.RegisterDTO; import org.mobidgim.mobidigimproject.model.register.RegisterDTO;
import org.mobidgim.mobidigimproject.model.register.enums.CanInput; import org.mobidgim.mobidigimproject.model.register.enums.CanInput;
import org.mobidgim.mobidigimproject.model.register.enums.Protocol; import org.mobidgim.mobidigimproject.model.register.enums.Protocol;
import org.mobidgim.mobidigimproject.model.register.sebDTO.CanField;
import org.mobidgim.mobidigimproject.model.register.sebDTO.ModbusField;
import org.mobidgim.mobidigimproject.model.register.sebDTO.OpcUaField;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.swing.text.html.parser.Parser;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@Service @Service
public class SettingService { public class SettingService {
private final ObjectMapper mapper; private final ObjectMapper mapper;
private static final String DEFAULT_DEVICE_FILENAME = "deviceConfig.json";
private static final String DEFAULT_PROTOCOL_FILENAME = "protocolConfig.json";
private static final String TEMP_DIRECTORY = "./temp";
@Autowired @Autowired
public SettingService(ObjectMapper mapper) { public SettingService(ObjectMapper mapper) {
this.mapper = mapper; this.mapper = mapper;
this.mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); this.mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
}
// 디렉토리 생성 메서드
private void ensureDirectoryExists() throws IOException {
Path dirPath = Paths.get(TEMP_DIRECTORY);
if (!Files.exists(dirPath)) {
Files.createDirectory(dirPath);
}
} }
// Device 파일 저장 (기본 파일명 사용)
public void saveFileWithDevice(DeviceDTO request) throws IOException { public void saveFileWithDevice(DeviceDTO request) throws IOException {
saveFileWithDevice(request, DEFAULT_DEVICE_FILENAME);
}
// Device 파일 저장 (사용자 지정 파일명)
public void saveFileWithDevice(DeviceDTO request, String filename) throws IOException {
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(request); String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(request);
String directoryPath = "./temp"; ensureDirectoryExists();
Path dirPath = Paths.get(directoryPath);
if(!Files.exists(dirPath)){ String filePath = TEMP_DIRECTORY + "/" + filename;
Files.createDirectory(dirPath); Files.writeString(Paths.get(filePath), json);
} }
String filename = "./temp/deviceConfig.json"; // Device 파일 읽기 (기본 파일명 사용)
Files.writeString(Paths.get(filename), json); public DeviceDTO readFileWithDevice() throws IOException {
return readFileWithDevice(DEFAULT_DEVICE_FILENAME);
} }
public DeviceDTO readFileWithDevice() throws IOException { // Device 파일 읽기 (사용자 지정 파일명)
public DeviceDTO readFileWithDevice(String filename) throws IOException {
String filePath = TEMP_DIRECTORY + "/" + filename;
Path path = Paths.get(filePath);
if (!Files.exists(path)) {
throw new IOException("Device configuration file not found: " + filename);
}
String jsonString = Files.readString(path);
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
return mapper.readValue(new File("./temp/deviceConfig.json"), DeviceDTO.class); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper.readValue(jsonString, DeviceDTO.class);
}
// Device 파일 존재 여부 확인
public boolean deviceFileExists() {
return deviceFileExists(DEFAULT_DEVICE_FILENAME);
}
public boolean deviceFileExists(String filename) {
String filePath = TEMP_DIRECTORY + "/" + filename;
return Files.exists(Paths.get(filePath));
} }
// Protocol 파일 저장 (기본 파일명 사용)
public RegisterDTO saveFileWithRegister(RegisterDTO request) throws IOException { public RegisterDTO saveFileWithRegister(RegisterDTO request) throws IOException {
return saveFileWithRegister(request, DEFAULT_PROTOCOL_FILENAME);
}
// Protocol 파일 저장 (사용자 지정 파일명)
public RegisterDTO saveFileWithRegister(RegisterDTO request, String filename) throws IOException {
// 프로토콜에 따른 데이터 정리
if (request.getProtocol() != Protocol.OPC_UA) { if (request.getProtocol() != Protocol.OPC_UA) {
request.setOPC_UA(null); request.setOPC_UA(null);
} }
@ -66,26 +114,31 @@ public class SettingService {
request.setCAN(null); request.setCAN(null);
} }
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(request); String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(request);
String directoryPath = "./temp"; ensureDirectoryExists();
Path dirPath = Paths.get(directoryPath);
if(!Files.exists(dirPath)){ String filePath = TEMP_DIRECTORY + "/" + filename;
Files.createDirectory(dirPath); Files.writeString(Paths.get(filePath), json);
}
String filename = "./temp/protocolConfig.json";
Files.writeString(Paths.get(filename), json);
return request; return request;
} }
// Protocol 파일 읽기 (기본 파일명 사용)
public RegisterDTO readFileWithRegister() throws IOException { public RegisterDTO readFileWithRegister() throws IOException {
String filename = "./temp/protocolConfig.json"; return readFileWithRegister(DEFAULT_PROTOCOL_FILENAME);
Path filePath = Paths.get(filename); }
String jsonString = Files.readString(filePath);
// Protocol 파일 읽기 (사용자 지정 파일명)
public RegisterDTO readFileWithRegister(String filename) throws IOException {
String filePath = TEMP_DIRECTORY + "/" + filename;
Path path = Paths.get(filePath);
if (!Files.exists(path)) {
throw new IOException("Protocol configuration file not found: " + filename);
}
String jsonString = Files.readString(path);
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
@ -93,4 +146,46 @@ public class SettingService {
return registerDTO; return registerDTO;
} }
// Protocol 파일 존재 여부 확인
public boolean protocolFileExists() {
return protocolFileExists(DEFAULT_PROTOCOL_FILENAME);
}
public boolean protocolFileExists(String filename) {
String filePath = TEMP_DIRECTORY + "/" + filename;
return Files.exists(Paths.get(filePath));
}
// temp 디렉토리의 모든 JSON 파일 목록 조회
public List<String> getAvailableDeviceFiles() throws IOException {
return getAvailableFiles("device");
}
public List<String> getAvailableProtocolFiles() throws IOException {
return getAvailableFiles("protocol");
}
private List<String> getAvailableFiles(String type) throws IOException {
ensureDirectoryExists();
Path dirPath = Paths.get(TEMP_DIRECTORY);
return Files.list(dirPath)
.filter(path -> path.toString().endsWith(".json"))
.map(path -> path.getFileName().toString())
.filter(filename -> {
try {
if ("device".equals(type)) {
readFileWithDevice(filename);
return true;
} else if ("protocol".equals(type)) {
readFileWithRegister(filename);
return true;
}
return false;
} catch (Exception e) {
return false; // 파일이 해당 타입에 맞지 않으면 제외
}
})
.toList();
}
} }

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

@ -55,6 +55,7 @@
<div id="ssid-list"></div> <div id="ssid-list"></div>
<button id="add-ssid" class="icon-button" title="add SSID"></button> <button id="add-ssid" class="icon-button" title="add SSID"></button>
<button id="delete-ssid" class="icon-button" title="delete SSID"></button>
</div> </div>
<div class="actions"> <div class="actions">
<div class="file-upload-wrapper-device" <div class="file-upload-wrapper-device"
@ -772,13 +773,6 @@
const selectedDeviceFile = event.target.files[0]; const selectedDeviceFile = event.target.files[0];
const DeviceFileName = selectedDeviceFile.name; const DeviceFileName = selectedDeviceFile.name;
// 파일명 검증 (data-expected-filename 사용)
if (DeviceFileName !== expectedDeviceFileName){
alert(`Invalid file.'${expectedDeviceFileName}' Please select a file. Current file: ${DeviceFileName} `);
uploadDeviceFileInput.value = ''; // 잘못된 파일 선택 시 input 초기화
return;
}
const reader = new FileReader(); const reader = new FileReader();
reader.onload = async (e) => { reader.onload = async (e) => {
@ -885,13 +879,6 @@
return; return;
} }
// 파일명 검증 (data-expected-filename 사용)
if (fileName !== expectedFileName){
alert(`Invalid file. '${expectedFileName}' Please select a file. Current file: ${fileName} `);
uploadFileInput.value = ''; // 잘못된 파일 선택 시 input 초기화
return;
}
reader.onload = async (e) => { reader.onload = async (e) => {
try { try {
const fileContent = e.target.result; const fileContent = e.target.result;
@ -1041,6 +1028,20 @@
const expr = document.getElementById('modbus_expr_input').value; const expr = document.getElementById('modbus_expr_input').value;
const mask = document.getElementById('modbus_mask_input').value; const mask = document.getElementById('modbus_mask_input').value;
const firstRow = modbusFieldList.querySelector('tr');
// 첫 행이 비어있다면 그 자리에 값을 넣기
if (firstRow && firstRow.querySelector('.field-display').textContent.trim() === '') {
firstRow.querySelector('.field-display').textContent = field;
firstRow.querySelector('.addr-display').textContent = addr;
firstRow.querySelector('.idt-display').textContent = idt;
firstRow.querySelector('.odt-display').textContent = odt;
firstRow.querySelector('.dv-display').textContent = defaultValue;
firstRow.querySelector('.shift-display').textContent = shift;
firstRow.querySelector('.expr-display').textContent = expr;
firstRow.querySelector('.mask-display').textContent = mask;
} else {
// 새로운 행을 추가
const newRow = modbusRowTemplate.content.cloneNode(true); const newRow = modbusRowTemplate.content.cloneNode(true);
newRow.querySelector('.field-display').textContent = field; newRow.querySelector('.field-display').textContent = field;
newRow.querySelector('.addr-display').textContent = addr; newRow.querySelector('.addr-display').textContent = addr;
@ -1052,7 +1053,9 @@
newRow.querySelector('.mask-display').textContent = mask; newRow.querySelector('.mask-display').textContent = mask;
modbusFieldList.appendChild(newRow); modbusFieldList.appendChild(newRow);
}
// 입력 필드 초기화
document.getElementById('modbus_field_input').value = ''; document.getElementById('modbus_field_input').value = '';
document.getElementById('modbus_addr_input').value = ''; document.getElementById('modbus_addr_input').value = '';
document.getElementById('modbus_idt_input').value = ''; document.getElementById('modbus_idt_input').value = '';
@ -1095,6 +1098,20 @@
const expr = document.getElementById('opcua_expr_input').value; const expr = document.getElementById('opcua_expr_input').value;
const mask = document.getElementById('opcua_mask_input').value; const mask = document.getElementById('opcua_mask_input').value;
const firstRow = opcUaFieldList.querySelector('tr');
// 첫 행이 비어있다면 그 자리에 값을 넣기
if (firstRow && firstRow.querySelector('.opcua-field-display').textContent.trim() === '') {
firstRow.querySelector('.opcua-field-display').textContent = field;
firstRow.querySelector('.opcua-addr-display').textContent = addr;
firstRow.querySelector('.opcua-ns-display').textContent = ns;
firstRow.querySelector('.opcua-odt-display').textContent = odt;
firstRow.querySelector('.opcua-dv-display').textContent = defaultValue;
firstRow.querySelector('.opcua-shift-display').textContent = shift;
firstRow.querySelector('.opcua-expr-display').textContent = expr;
firstRow.querySelector('.opcua-mask-display').textContent = mask;
} else {
// 새로운 행을 추가
const newRow = opcUaRowTemplate.content.cloneNode(true); const newRow = opcUaRowTemplate.content.cloneNode(true);
newRow.querySelector('.opcua-field-display').textContent = field; newRow.querySelector('.opcua-field-display').textContent = field;
newRow.querySelector('.opcua-addr-display').textContent = addr; newRow.querySelector('.opcua-addr-display').textContent = addr;
@ -1106,18 +1123,21 @@
newRow.querySelector('.opcua-mask-display').textContent = mask; newRow.querySelector('.opcua-mask-display').textContent = mask;
opcUaFieldList.appendChild(newRow); opcUaFieldList.appendChild(newRow);
}
document.getElementById('opcua_field_input').value = ''; // 입력 필드 초기화
document.getElementById('opcua_addr_input').value = ''; document.getElementById('modbus_field_input').value = '';
document.getElementById('opcua_ns_input').value = ''; document.getElementById('modbus_addr_input').value = '';
document.getElementById('opcua_odt_input').value = ''; document.getElementById('modbus_idt_input').value = '';
document.getElementById('opcua_dv_input').value = ''; document.getElementById('modbus_odt_input').value = '';
document.getElementById('opcua_shift_input').value = ''; document.getElementById('modbus_dv_input').value = '';
document.getElementById('opcua_expr_input').value = ''; document.getElementById('modbus_shift_input').value = '';
document.getElementById('opcua_mask_input').value = ''; document.getElementById('modbus_expr_input').value = '';
document.getElementById('modbus_mask_input').value = '';
}); });
document.getElementById('opcua_modify_btn').onclick = function () { document.getElementById('opcua_modify_btn').onclick = function () {
if (!selectedRow) return alert('Please select the row you want to modify'); if (!selectedRow) return alert('Please select the row you want to modify');
selectedRow.querySelector('.opcua-field-display').textContent = document.getElementById('opcua_field_input').value; selectedRow.querySelector('.opcua-field-display').textContent = document.getElementById('opcua_field_input').value;
@ -1437,6 +1457,65 @@
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
/*-----------------------------------------------------*/ /*-----------------------------------------------------*/
// localStorage 자동 저장 (Device)
function autoSaveToLocal() {
const data = collectDeviceData();
localStorage.setItem('deviceConfigAutoSave', JSON.stringify(data));
}
// localStorage 자동 저장 (Protocol)
function autoSaveProtocolToLocal() {
const config = collectProtocolData();
localStorage.setItem('protocolConfigAutoSave', JSON.stringify(config));
}
// localStorage에서 불러오기
function loadFromLocalIfExists() {
const saved = localStorage.getItem('deviceConfigAutoSave');
if (saved) {
try {
loadFromData(JSON.parse(saved));
console.log('localStorage에서 자동 저장된 Device 설정을 불러왔습니다.');
return true;
} catch (e) {
console.warn('localStorage Device 설정 파싱 실패:', e);
}
}
return false;
}
// localStorage에서 불러오기 (Protocol)
function loadProtocolFromLocalIfExists() {
const saved = localStorage.getItem('protocolConfigAutoSave');
if (saved) {
try {
loadFromRegisterJson(JSON.parse(saved));
console.log('localStorage에서 자동 저장된 Protocol 설정을 불러왔습니다.');
return true;
} catch (e) {
console.warn('localStorage Protocol 설정 파싱 실패:', e);
}
}
return false;
}
// input, select 등 값이 바뀔 때마다 자동 저장 (Device)
function bindAutoSaveEvents() {
document.querySelectorAll('input, select').forEach(el => {
el.addEventListener('change', autoSaveToLocal);
});
}
// input, select 등 값이 바뀔 때마다 자동 저장 (Protocol)
function bindAutoSaveProtocolEvents() {
// reg, modbus, opcua, can 페이지 내의 input/select만
const protocolSections = ['#page-reg', '#page-modbus', '#page-opcua', '#page-can'];
protocolSections.forEach(section => {
document.querySelectorAll(section + ' input, ' + section + ' select').forEach(el => {
el.addEventListener('change', autoSaveProtocolToLocal);
});
});
}
/*-----protocol에 설정 데이터를 protocolConfig.json 파일로 변경하여 유저가 다운로드 할수 있게 하는 함수-----*/ /*-----protocol에 설정 데이터를 protocolConfig.json 파일로 변경하여 유저가 다운로드 할수 있게 하는 함수-----*/
function downloadProtocolJson(obj, filename='protocolConfig.json') { function downloadProtocolJson(obj, filename='protocolConfig.json') {
@ -1456,30 +1535,85 @@
/*-------페이지 로드시 서버에 있는 데이터를 불러오는 함수---------*/ /*-------페이지 로드시 서버에 있는 데이터를 불러오는 함수---------*/
async function loadInitialData() { async function loadInitialData() {
// 1. localStorage 우선 적용 (Device)
let deviceLoadedFromLocal = loadFromLocalIfExists();
// 2. 서버에서 파일 목록 조회 및 로드 (Device)
if (!deviceLoadedFromLocal) {
try { try {
// Device 설정 로드 // 사용 가능한 Device 파일 목록 조회
const deviceFilesResponse = await fetch('http://localhost:8080/setting/device-files');
if (deviceFilesResponse.ok) {
const deviceFilesData = await deviceFilesResponse.json();
if (deviceFilesData && deviceFilesData.body && deviceFilesData.body.length > 0) {
// 첫 번째 유효한 Device 파일 로드
const firstDeviceFile = deviceFilesData.body[0];
console.log(`Device 파일 발견: ${firstDeviceFile}`);
const deviceResponse = await fetch(`http://localhost:8080/setting/get-device/${firstDeviceFile}`);
if (deviceResponse.ok) {
const deviceData = await deviceResponse.json();
if (deviceData && deviceData.body) {
loadFromData(deviceData.body);
console.log(`Device 설정 자동 로드 완료: ${firstDeviceFile}`);
}
}
} else {
// 기본 파일명으로 시도
const deviceResponse = await fetch('http://localhost:8080/setting/get-device'); const deviceResponse = await fetch('http://localhost:8080/setting/get-device');
if (deviceResponse.ok) { if (deviceResponse.ok) {
const deviceData = await deviceResponse.json(); const deviceData = await deviceResponse.json();
if (deviceData && deviceData.body) { if (deviceData && deviceData.body) {
loadFromData(deviceData.body); loadFromData(deviceData.body);
console.log('Device 설정 자동 로드 완료 (기본 파일)');
}
}
} }
} }
} catch (error) { } catch (error) {
alert('Device configuration file missing or loading failed'); console.log('Device 설정 파일이 없거나 로드 실패, 기본값 사용:', error.message);
} }
}
bindAutoSaveEvents();
// 1. localStorage 우선 적용 (Protocol)
let protocolLoadedFromLocal = loadProtocolFromLocalIfExists();
// 2. 서버에서 파일 목록 조회 및 로드 (Protocol)
if (!protocolLoadedFromLocal) {
try { try {
// Protocol 설정 로드 // 사용 가능한 Protocol 파일 목록 조회
const protocolFilesResponse = await fetch('http://localhost:8080/setting/protocol-files');
if (protocolFilesResponse.ok) {
const protocolFilesData = await protocolFilesResponse.json();
if (protocolFilesData && protocolFilesData.body && protocolFilesData.body.length > 0) {
// 첫 번째 유효한 Protocol 파일 로드
const firstProtocolFile = protocolFilesData.body[0];
console.log(`Protocol 파일 발견: ${firstProtocolFile}`);
const protocolResponse = await fetch(`http://localhost:8080/setting/get-protocol/${firstProtocolFile}`);
if (protocolResponse.ok) {
const protocolData = await protocolResponse.json();
if (protocolData && protocolData.body) {
loadFromRegisterJson(protocolData.body);
console.log(`Protocol 설정 자동 로드 완료: ${firstProtocolFile}`);
}
}
} else {
// 기본 파일명으로 시도
const protocolResponse = await fetch('http://localhost:8080/setting/get-protocol'); const protocolResponse = await fetch('http://localhost:8080/setting/get-protocol');
if (protocolResponse.ok) { if (protocolResponse.ok) {
const protocolData = await protocolResponse.json(); const protocolData = await protocolResponse.json();
if (protocolData && protocolData.body) { if (protocolData && protocolData.body) {
loadFromRegisterJson(protocolData.body); loadFromRegisterJson(protocolData.body);
console.log('Protocol 설정 자동 로드 완료 (기본 파일)');
}
}
} }
} }
} catch (error) { } catch (error) {
alert('Protocol configuration file missing or loading failed'); } console.log('Protocol 설정 파일이 없거나 로드 실패, 기본값 사용:', error.message);
}
}
bindAutoSaveProtocolEvents();
} }
/*-----------------------------------------------------*/ /*-----------------------------------------------------*/

Loading…
Cancel
Save