|
|
@ -334,22 +334,8 @@ |
|
|
cursor: pointer |
|
|
cursor: pointer |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.config-actions { |
|
|
.config-actions{display: flex;gap: 24px;justify-content: center;background: #f7f7f7;border: 1px solid #bbb;border-radius: 4px;margin: 24px 0 0 0;padding: 20px 0;} |
|
|
display: flex; |
|
|
.config-actions button{min-width:140px;padding:10px 18px;font-size:16px;border:1px solid #bbb;background:#d9d9d9;border-radius:4px;cursor:pointer} |
|
|
gap: 24px; |
|
|
|
|
|
justify-content: center; |
|
|
|
|
|
padding: 20px |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.config-actions button { |
|
|
|
|
|
min-width: 140px; |
|
|
|
|
|
padding: 10px 18px; |
|
|
|
|
|
font-size: 16px; |
|
|
|
|
|
border: 1px solid #bbb; |
|
|
|
|
|
background: #d9d9d9; |
|
|
|
|
|
border-radius: 4px; |
|
|
|
|
|
cursor: pointer |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* MODBUS 화면 심플 스타일 */ |
|
|
/* MODBUS 화면 심플 스타일 */ |
|
|
#page-modbus .card { |
|
|
#page-modbus .card { |
|
|
@ -663,6 +649,8 @@ |
|
|
background: #d0e6ff !important; |
|
|
background: #d0e6ff !important; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.file-upload-section{padding: 10px;margin: 10px;text-align: center;} |
|
|
|
|
|
|
|
|
</style> |
|
|
</style> |
|
|
</head> |
|
|
</head> |
|
|
<body> |
|
|
<body> |
|
|
@ -715,7 +703,14 @@ |
|
|
<button id="delete-ssid" style="margin-top:12px">Delete SSID</button> |
|
|
<button id="delete-ssid" style="margin-top:12px">Delete SSID</button> |
|
|
</div> |
|
|
</div> |
|
|
<div class="actions"> |
|
|
<div class="actions"> |
|
|
<button id="loadDeviceBtn_ssid">Load Device File</button> |
|
|
<div class="file-upload-wrapper-device" |
|
|
|
|
|
data-file-type="device" |
|
|
|
|
|
data-expected-filename="deviceConfig.json" |
|
|
|
|
|
data-upload-url="http://localhost:8080/setting/device" |
|
|
|
|
|
data-get-url="http://localhost:8080/setting/get-device"> |
|
|
|
|
|
<input type="file" class="upload-input-device" accept=".json" style="display: none;"> |
|
|
|
|
|
<button class="select-file-btn-device">Load Local Device File</button> |
|
|
|
|
|
</div> |
|
|
<button id="saveDeviceBtn_ssid">Save to Device</button> |
|
|
<button id="saveDeviceBtn_ssid">Save to Device</button> |
|
|
<button id="downloadDeviceBtn">Download Device File</button> |
|
|
<button id="downloadDeviceBtn">Download Device File</button> |
|
|
</div> |
|
|
</div> |
|
|
@ -754,7 +749,14 @@ |
|
|
id="opc_ua_server_port"/></div> |
|
|
id="opc_ua_server_port"/></div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="actions"> |
|
|
<div class="actions"> |
|
|
<button id="loadDeviceBtn_network">Load Device File</button> |
|
|
<div class="file-upload-wrapper-device" |
|
|
|
|
|
data-file-type="device" |
|
|
|
|
|
data-expected-filename="deviceConfig.json" |
|
|
|
|
|
data-upload-url="http://localhost:8080/setting/device" |
|
|
|
|
|
data-get-url="http://localhost:8080/setting/get-device"> |
|
|
|
|
|
<input type="file" class="upload-input-device" accept=".json" style="display: none;"> |
|
|
|
|
|
<button class="select-file-btn-device">Load Local Device File</button> |
|
|
|
|
|
</div> |
|
|
<button id="saveDeviceBtn_network">Save to Device</button> |
|
|
<button id="saveDeviceBtn_network">Save to Device</button> |
|
|
<button id="downloadDeviceBtn">Download Device File</button> |
|
|
<button id="downloadDeviceBtn">Download Device File</button> |
|
|
</div> |
|
|
</div> |
|
|
@ -884,7 +886,14 @@ |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="actions"> |
|
|
<div class="actions"> |
|
|
<button id="loadDeviceBtn_io">Load Device File</button> |
|
|
<div class="file-upload-wrapper-device" |
|
|
|
|
|
data-file-type="device" |
|
|
|
|
|
data-expected-filename="deviceConfig.json" |
|
|
|
|
|
data-upload-url="http://localhost:8080/setting/device" |
|
|
|
|
|
data-get-url="http://localhost:8080/setting/get-device"> |
|
|
|
|
|
<input type="file" class="upload-input-device" accept=".json" style="display: none;"> |
|
|
|
|
|
<button class="select-file-btn-device">Load Local Device File</button> |
|
|
|
|
|
</div> |
|
|
<button id="saveDeviceBtn_io">Save to Device</button> |
|
|
<button id="saveDeviceBtn_io">Save to Device</button> |
|
|
<button id="downloadDeviceBtn">Download Device File</button> |
|
|
<button id="downloadDeviceBtn">Download Device File</button> |
|
|
</div> |
|
|
</div> |
|
|
@ -921,7 +930,7 @@ |
|
|
<div class="io-row"><span class="label">MEID</span><input id="reg_meid"></div> |
|
|
<div class="io-row"><span class="label">MEID</span><input id="reg_meid"></div> |
|
|
<div class="io-row"><span class="label">Protocol</span> |
|
|
<div class="io-row"><span class="label">Protocol</span> |
|
|
<select id="reg_protocol"> |
|
|
<select id="reg_protocol"> |
|
|
<option selected disabled hidden="hidden"></option> |
|
|
<option value="NONE">NONE</option> |
|
|
<option value="MODBUS">MODBUS</option> |
|
|
<option value="MODBUS">MODBUS</option> |
|
|
<option value="OPC_UA">OPC_UA</option> |
|
|
<option value="OPC_UA">OPC_UA</option> |
|
|
</select> |
|
|
</select> |
|
|
@ -1015,11 +1024,18 @@ |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="config-actions"> |
|
|
<div class="config-actions"> |
|
|
<button id="loadProtocolBtn">Load Protocol File</button> |
|
|
<div class="file-upload-wrapper-protocol" |
|
|
<button id="saveProtocolBtn">Save to Protocol</button> |
|
|
data-file-type="register" |
|
|
<button id="uploadProtocolBtn">Upload to Device</button> |
|
|
data-expected-filename="registerConfig.json" |
|
|
<button id="downloadProtocolBtn">Download Device File</button> |
|
|
data-upload-url="http://localhost:8080/setting/register" |
|
|
|
|
|
data-get-url="http://localhost:8080/setting/get-register"> |
|
|
|
|
|
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;"> |
|
|
|
|
|
<button class="select-file-btn-protocol">Load Local Protocol File</button> |
|
|
|
|
|
</div> |
|
|
|
|
|
<button id="saveProtocolBtn">Save to Protocol</button> |
|
|
|
|
|
<button id="downloadProtocolBtn">Download Device File</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
</section> |
|
|
</section> |
|
|
|
|
|
|
|
|
<template id="modbus-row-template"> |
|
|
<template id="modbus-row-template"> |
|
|
@ -1037,7 +1053,7 @@ |
|
|
|
|
|
|
|
|
<section id="page-modbus" class="page" style="display:none;"> |
|
|
<section id="page-modbus" class="page" style="display:none;"> |
|
|
<div class="card"> |
|
|
<div class="card"> |
|
|
<h3>Register_Configuration Modbus화면</h3> |
|
|
<h3>Modbus Configuration</h3> |
|
|
<div class="modbus-table-container"> |
|
|
<div class="modbus-table-container"> |
|
|
<table> |
|
|
<table> |
|
|
<thead> |
|
|
<thead> |
|
|
@ -1098,11 +1114,18 @@ |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="modbus-bottom-actions"> |
|
|
<div id="modbus-bottom-actions"> |
|
|
<button id="loadModbusBtn">Load Protocol File</button> |
|
|
<div class="file-upload-wrapper-protocol" |
|
|
|
|
|
data-file-type="register" |
|
|
|
|
|
data-expected-filename="registerConfig.json" |
|
|
|
|
|
data-upload-url="http://localhost:8080/setting/register" |
|
|
|
|
|
data-get-url="http://localhost:8080/setting/get-register"> |
|
|
|
|
|
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;"> |
|
|
|
|
|
<button class="select-file-btn-protocol">Load Local Protocol File</button> |
|
|
|
|
|
</div> |
|
|
<button id="saveModbusBtn">Save Protocol File</button> |
|
|
<button id="saveModbusBtn">Save Protocol File</button> |
|
|
<button id="uploadModbusBtn">Upload to Device</button> |
|
|
|
|
|
<button id="downloadProtocolBtn">Download Device File</button> |
|
|
<button id="downloadProtocolBtn">Download Device File</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
</section> |
|
|
</section> |
|
|
|
|
|
|
|
|
<template id="opcua-row-template"> |
|
|
<template id="opcua-row-template"> |
|
|
@ -1173,11 +1196,18 @@ |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="opcUa-bottom-actions"> |
|
|
<div id="opcUa-bottom-actions"> |
|
|
<button id="loadOpcUaFileBtn">Load Protocol File</button> |
|
|
<div class="file-upload-wrapper-protocol" |
|
|
|
|
|
data-file-type="register" |
|
|
|
|
|
data-expected-filename="registerConfig.json" |
|
|
|
|
|
data-upload-url="http://localhost:8080/setting/register" |
|
|
|
|
|
data-get-url="http://localhost:8080/setting/get-register"> |
|
|
|
|
|
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;"> |
|
|
|
|
|
<button class="select-file-btn-protocol">Load Local Protocol File</button> |
|
|
|
|
|
</div> |
|
|
<button id="saveOpcUaFileBtn">Save to Protocol</button> |
|
|
<button id="saveOpcUaFileBtn">Save to Protocol</button> |
|
|
<button id="uploadOpcuaFileBtn">Upload to Device</button> |
|
|
|
|
|
<button id="downloadProtocolBtn">Download Device File</button> |
|
|
<button id="downloadProtocolBtn">Download Device File</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
</section> |
|
|
</section> |
|
|
|
|
|
|
|
|
<template id="can-row-template"> |
|
|
<template id="can-row-template"> |
|
|
@ -1241,11 +1271,18 @@ |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div id="can-bottom-actions"> |
|
|
<div id="can-bottom-actions"> |
|
|
<button id="loadCanBtn">Load Protocol File</button> |
|
|
<div class="file-upload-wrapper-protocol" |
|
|
|
|
|
data-file-type="register" |
|
|
|
|
|
data-expected-filename="registerConfig.json" |
|
|
|
|
|
data-upload-url="http://localhost:8080/setting/register" |
|
|
|
|
|
data-get-url="http://localhost:8080/setting/get-register"> |
|
|
|
|
|
<input type="file" class="upload-input-protocol" accept=".json" style="display: none;"> |
|
|
|
|
|
<button class="select-file-btn-protocol">Load Local Protocol File</button> |
|
|
|
|
|
</div> |
|
|
<button id="saveCanBtn">Save Protocol File</button> |
|
|
<button id="saveCanBtn">Save Protocol File</button> |
|
|
<button id="uploadCanBtn">Upload to Device</button> |
|
|
|
|
|
<button id="downloadProtocolBtn">Download Device File</button> |
|
|
<button id="downloadProtocolBtn">Download Device File</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
</section> |
|
|
</section> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1801,6 +1838,417 @@ |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
|
|
|
|
|
|
|
|
|
// Device 파일 업로드 |
|
|
|
|
|
// 모든 파일 업로드 래퍼 섹션을 가져옵니다. |
|
|
|
|
|
const fileUploadWrappersDevice = document.querySelectorAll('.file-upload-wrapper-device'); |
|
|
|
|
|
|
|
|
|
|
|
fileUploadWrappersDevice.forEach(wrapper => { |
|
|
|
|
|
// 현재 래퍼 내에서 input[type="file"]과 버튼을 찾습니다. |
|
|
|
|
|
const uploadDeviceFileInput = wrapper.querySelector('.upload-input-device'); |
|
|
|
|
|
const selectDeviceFileBtn = wrapper.querySelector('.select-file-btn-device'); |
|
|
|
|
|
|
|
|
|
|
|
// 해당 래퍼의 data 속성들을 가져옵니다. |
|
|
|
|
|
const expectedDeviceFileName = wrapper.dataset.expectedFilename; |
|
|
|
|
|
const uploadDeviceUrl = wrapper.dataset.uploadUrl; |
|
|
|
|
|
const getDeviceUrl = wrapper.dataset.getUrl; |
|
|
|
|
|
const DeviceFileType = wrapper.dataset.fileType; |
|
|
|
|
|
|
|
|
|
|
|
// null 체크 (요소가 존재하지 않을 수도 있는 경우를 대비) |
|
|
|
|
|
if (!uploadDeviceFileInput || !selectDeviceFileBtn) { |
|
|
|
|
|
console.warn(`File upload elements not found in wrapper for type: ${DeviceFileType}`); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// "파일 선택" 버튼 클릭 시 숨겨진 input[type="file"] 클릭 이벤트 발생 |
|
|
|
|
|
selectDeviceFileBtn.addEventListener('click', () => { |
|
|
|
|
|
uploadDeviceFileInput.value = ''; // 이전에 선택된 파일 초기화 |
|
|
|
|
|
uploadDeviceFileInput.click(); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// 파일 선택 시 (change 이벤트 발생) 바로 업로드 로직 시작 |
|
|
|
|
|
uploadDeviceFileInput.addEventListener('change', async (event) => { |
|
|
|
|
|
if (event.target.files.length === 0) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const selectedDeviceFile = event.target.files[0]; |
|
|
|
|
|
const DeviceFileName = selectedDeviceFile.name; |
|
|
|
|
|
|
|
|
|
|
|
// 파일명 검증 (data-expected-filename 사용) |
|
|
|
|
|
if (DeviceFileName !== expectedDeviceFileName){ |
|
|
|
|
|
alert(`잘못된 파일입니다. '${expectedDeviceFileName}' 파일을 선택해주세요. 현재 파일: ${DeviceFileName} `); |
|
|
|
|
|
uploadDeviceFileInput.value = ''; // 잘못된 파일 선택 시 input 초기화 |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const reader = new FileReader(); |
|
|
|
|
|
|
|
|
|
|
|
reader.onload = async (e) => { |
|
|
|
|
|
try { |
|
|
|
|
|
const DeviceFileContent = e.target.result; |
|
|
|
|
|
const jsonData = JSON.parse(DeviceFileContent); |
|
|
|
|
|
|
|
|
|
|
|
// JSON 내용 유효성 검증 |
|
|
|
|
|
// 모든 파일이 Register 설정이라면 isValidRegisterConfig를 그대로 사용 |
|
|
|
|
|
// 만약 파일 타입에 따라 다른 유효성 검사가 필요하다면, |
|
|
|
|
|
// 여기에 조건부 로직을 추가해야 합니다. |
|
|
|
|
|
// if (!isValidDeviceConfig(jsonData)){ // 예를 들어, isValidModbusConfig(jsonData) 등 |
|
|
|
|
|
// alert(`선택된 파일의 내용이 유효한 ${DeviceFileType} 설정 파일 형식이 아닙니다.`); |
|
|
|
|
|
// uploadDeviceFileInput.value = ''; |
|
|
|
|
|
// return; |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// 백엔드 저장 API 호출 (data-upload-url 사용) |
|
|
|
|
|
const response = await fetch(uploadDeviceUrl, { |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
headers: { |
|
|
|
|
|
'Content-Type': 'application/json' |
|
|
|
|
|
}, |
|
|
|
|
|
body: JSON.stringify(jsonData) |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (!response.ok) { |
|
|
|
|
|
const errorText = await response.text(); |
|
|
|
|
|
throw new Error(`파일 업로드 실패: ${response.status} ${response.statusText} - ${errorText}`); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const result = await response.json(); |
|
|
|
|
|
|
|
|
|
|
|
if (result.result && result.result.result_code === 200) { |
|
|
|
|
|
alert(`파일이 Device에 성공적으로 업로드되었습니다! (${DeviceFileType})`); |
|
|
|
|
|
console.log(`Upload successful for ${DeviceFileType}:`, result); |
|
|
|
|
|
|
|
|
|
|
|
// 업로드 성공 후 최신 데이터를 Device에서 다시 불러와 UI 업데이트 (data-get-url 사용) |
|
|
|
|
|
// loadFromRegisterJson은 Register 데이터만 처리하는 함수이므로, |
|
|
|
|
|
// 만약 Modbus/OPCUA/CAN 데이터 로드 함수가 별도로 있다면 여기서 조건부 호출 필요 |
|
|
|
|
|
fetch(getDeviceUrl) |
|
|
|
|
|
.then(res => res.json()) |
|
|
|
|
|
.then(apiResponseData => { |
|
|
|
|
|
if (apiResponseData && apiResponseData.body) { |
|
|
|
|
|
// 여기도 fileType에 따라 다른 load 함수를 호출해야 할 수 있습니다. |
|
|
|
|
|
// 현재는 모든 파일이 Register 설정이라고 가정하여 loadFromRegisterJson 호출 |
|
|
|
|
|
loadFromData(apiResponseData.body); |
|
|
|
|
|
console.log(`UI updated with newly uploaded ${DeviceFileType} data.`); |
|
|
|
|
|
} else { |
|
|
|
|
|
console.warn(`업로드 후 ${DeviceFileType} 데이터 재로드 실패: 응답 body 없음`); |
|
|
|
|
|
} |
|
|
|
|
|
}) |
|
|
|
|
|
.catch(err => console.error(`업로드 후 ${DeviceFileType} 데이터 재로드 중 오류 발생:`, err)); |
|
|
|
|
|
|
|
|
|
|
|
uploadDeviceFileInput.value = ''; // input 초기화 (다음 업로드 위함) |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
alert(`파일 업로드 실패: ${result.result ? result.result.result_message : '알 수 없는 오류'} (${DeviceFileType})`); |
|
|
|
|
|
console.error(`Upload failed for ${DeviceFileType} with backend error:`, result); |
|
|
|
|
|
uploadDeviceFileInput.value = ''; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error(`파일 업로드 중 오류 발생 (${DeviceFileType}):`, error); |
|
|
|
|
|
alert(`파일 업로드 중 오류 발생: ${error.message} (${DeviceFileType})`); |
|
|
|
|
|
uploadDeviceFileInput.value = ''; |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
reader.onerror = () => { |
|
|
|
|
|
alert(`파일을 읽는 도중 오류가 발생했습니다. (${DeviceFileType})`); |
|
|
|
|
|
console.error(`FileReader error for ${DeviceFileType}:`, reader.error); |
|
|
|
|
|
uploadDeviceFileInput.value = ''; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
reader.readAsText(selectedDeviceFile); |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Protocol 파일 업로드 |
|
|
|
|
|
// 모든 파일 업로드 래퍼 섹션을 가져옵니다. |
|
|
|
|
|
const fileUploadWrappers = document.querySelectorAll('.file-upload-wrapper-protocol'); |
|
|
|
|
|
|
|
|
|
|
|
fileUploadWrappers.forEach(wrapper => { |
|
|
|
|
|
// 현재 래퍼 내에서 input[type="file"]과 버튼을 찾습니다. |
|
|
|
|
|
const uploadFileInput = wrapper.querySelector('.upload-input-protocol'); |
|
|
|
|
|
const selectFileBtn = wrapper.querySelector('.select-file-btn-protocol'); |
|
|
|
|
|
|
|
|
|
|
|
// 해당 래퍼의 data 속성들을 가져옵니다. |
|
|
|
|
|
const expectedFileName = wrapper.dataset.expectedFilename; |
|
|
|
|
|
const uploadUrl = wrapper.dataset.uploadUrl; |
|
|
|
|
|
const getUrl = wrapper.dataset.getUrl; |
|
|
|
|
|
const fileType = wrapper.dataset.fileType; // 'protocol', 'modbus', 'opcua', 'can' |
|
|
|
|
|
|
|
|
|
|
|
// null 체크 (요소가 존재하지 않을 수도 있는 경우를 대비) |
|
|
|
|
|
if (!uploadFileInput || !selectFileBtn) { |
|
|
|
|
|
console.warn(`File upload elements not found in wrapper for type: ${fileType}`); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// "파일 선택" 버튼 클릭 시 숨겨진 input[type="file"] 클릭 이벤트 발생 |
|
|
|
|
|
selectFileBtn.addEventListener('click', () => { |
|
|
|
|
|
uploadFileInput.value = ''; // 이전에 선택된 파일 초기화 |
|
|
|
|
|
uploadFileInput.click(); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// 파일 선택 시 (change 이벤트 발생) 바로 업로드 로직 시작 |
|
|
|
|
|
uploadFileInput.addEventListener('change', async (event) => { |
|
|
|
|
|
if (event.target.files.length === 0) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const selectedFile = event.target.files[0]; |
|
|
|
|
|
const fileName = selectedFile.name; |
|
|
|
|
|
|
|
|
|
|
|
// 파일명 검증 (data-expected-filename 사용) |
|
|
|
|
|
if (fileName !== expectedFileName){ |
|
|
|
|
|
alert(`잘못된 파일입니다. '${expectedFileName}' 파일을 선택해주세요. 현재 파일: ${fileName} `); |
|
|
|
|
|
uploadFileInput.value = ''; // 잘못된 파일 선택 시 input 초기화 |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const reader = new FileReader(); |
|
|
|
|
|
|
|
|
|
|
|
reader.onload = async (e) => { |
|
|
|
|
|
try { |
|
|
|
|
|
const fileContent = e.target.result; |
|
|
|
|
|
const jsonData = JSON.parse(fileContent); |
|
|
|
|
|
|
|
|
|
|
|
// JSON 내용 유효성 검증 |
|
|
|
|
|
// 모든 파일이 Register 설정이라면 isValidRegisterConfig를 그대로 사용 |
|
|
|
|
|
// 만약 파일 타입에 따라 다른 유효성 검사가 필요하다면, |
|
|
|
|
|
// 여기에 조건부 로직을 추가해야 합니다. |
|
|
|
|
|
if (!isValidRegisterConfig(jsonData)){ // 예를 들어, isValidModbusConfig(jsonData) 등 |
|
|
|
|
|
alert(`선택된 파일의 내용이 유효한 ${fileType} 설정 파일 형식이 아닙니다.`); |
|
|
|
|
|
uploadFileInput.value = ''; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 백엔드 저장 API 호출 (data-upload-url 사용) |
|
|
|
|
|
const response = await fetch(uploadUrl, { |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
headers: { |
|
|
|
|
|
'Content-Type': 'application/json' |
|
|
|
|
|
}, |
|
|
|
|
|
body: JSON.stringify(jsonData) |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (!response.ok) { |
|
|
|
|
|
const errorText = await response.text(); |
|
|
|
|
|
throw new Error(`파일 업로드 실패: ${response.status} ${response.statusText} - ${errorText}`); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const result = await response.json(); |
|
|
|
|
|
|
|
|
|
|
|
if (result.result && result.result.result_code === 200) { |
|
|
|
|
|
alert(`파일이 Device에 성공적으로 업로드되었습니다! (${fileType})`); |
|
|
|
|
|
console.log(`Upload successful for ${fileType}:`, result); |
|
|
|
|
|
|
|
|
|
|
|
// 업로드 성공 후 최신 데이터를 Device에서 다시 불러와 UI 업데이트 (data-get-url 사용) |
|
|
|
|
|
// loadFromRegisterJson은 Register 데이터만 처리하는 함수이므로, |
|
|
|
|
|
// 만약 Modbus/OPCUA/CAN 데이터 로드 함수가 별도로 있다면 여기서 조건부 호출 필요 |
|
|
|
|
|
fetch(getUrl) |
|
|
|
|
|
.then(res => res.json()) |
|
|
|
|
|
.then(apiResponseData => { |
|
|
|
|
|
if (apiResponseData && apiResponseData.body) { |
|
|
|
|
|
// 여기도 fileType에 따라 다른 load 함수를 호출해야 할 수 있습니다. |
|
|
|
|
|
// 현재는 모든 파일이 Register 설정이라고 가정하여 loadFromRegisterJson 호출 |
|
|
|
|
|
loadFromRegisterJson(apiResponseData.body); |
|
|
|
|
|
console.log(`UI updated with newly uploaded ${fileType} data.`); |
|
|
|
|
|
} else { |
|
|
|
|
|
console.warn(`업로드 후 ${fileType} 데이터 재로드 실패: 응답 body 없음`); |
|
|
|
|
|
} |
|
|
|
|
|
}) |
|
|
|
|
|
.catch(err => console.error(`업로드 후 ${fileType} 데이터 재로드 중 오류 발생:`, err)); |
|
|
|
|
|
|
|
|
|
|
|
uploadFileInput.value = ''; // input 초기화 (다음 업로드 위함) |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
alert(`파일 업로드 실패: ${result.result ? result.result.result_message : '알 수 없는 오류'} (${fileType})`); |
|
|
|
|
|
console.error(`Upload failed for ${fileType} with backend error:`, result); |
|
|
|
|
|
uploadFileInput.value = ''; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error(`파일 업로드 중 오류 발생 (${fileType}):`, error); |
|
|
|
|
|
alert(`파일 업로드 중 오류 발생: ${error.message} (${fileType})`); |
|
|
|
|
|
uploadFileInput.value = ''; |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
reader.onerror = () => { |
|
|
|
|
|
alert(`파일을 읽는 도중 오류가 발생했습니다. (${fileType})`); |
|
|
|
|
|
console.error(`FileReader error for ${fileType}:`, reader.error); |
|
|
|
|
|
uploadFileInput.value = ''; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
reader.readAsText(selectedFile); |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
function isValidRegisterConfig(data) { |
|
|
|
|
|
// 1. data가 객체이고 null이 아닌지 확인 |
|
|
|
|
|
if (typeof data !== 'object' || data === null) { |
|
|
|
|
|
console.error("Validation Error: Data is not an object or is null."); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// RegisterDTO의 필수 필드 (백엔드 snake_case와 일치시키세요) |
|
|
|
|
|
const topLevelRequiredFields = [ |
|
|
|
|
|
'protocol', |
|
|
|
|
|
'analog_input_level', |
|
|
|
|
|
'can_input', |
|
|
|
|
|
'device_type', |
|
|
|
|
|
'dr_on', |
|
|
|
|
|
'equipment', |
|
|
|
|
|
'two_byte', |
|
|
|
|
|
'four_byte', |
|
|
|
|
|
'speed_data_source', |
|
|
|
|
|
'version', |
|
|
|
|
|
'equipment_id', |
|
|
|
|
|
'meid', |
|
|
|
|
|
'heading_on', |
|
|
|
|
|
'heading_imu_on', |
|
|
|
|
|
'fixmode_on' |
|
|
|
|
|
|
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
// 2. 모든 최상위 필수 필드가 존재하는지 확인 |
|
|
|
|
|
for (const field of topLevelRequiredFields) { |
|
|
|
|
|
if (!data.hasOwnProperty(field)) { |
|
|
|
|
|
console.error(`Validation Error: Missing required top-level field: '${field}'`); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 3. 각 필드의 값 타입 및 유효성 검증 |
|
|
|
|
|
|
|
|
|
|
|
// protocol 필드 유효성 검사 |
|
|
|
|
|
const validProtocols = ['MODBUS', 'OPC_UA', 'CAN', 'NONE']; |
|
|
|
|
|
if (typeof data.protocol !== 'string' || !validProtocols.includes(data.protocol)) { |
|
|
|
|
|
console.error(`Validation Error: Invalid or missing 'protocol' field. Expected one of ${validProtocols.join(', ')}, got '${data.protocol}'`); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ON/OFF 상태를 나타내는 필드 검사 |
|
|
|
|
|
const boolLikeFields = ['dr_on', 'heading_on', 'heading_imu_on', 'fixmode_on']; |
|
|
|
|
|
for (const field of boolLikeFields) { |
|
|
|
|
|
if (typeof data[field] !== 'string' || (data[field] !== 'on' && data[field] !== 'off')) { |
|
|
|
|
|
console.error(`Validation Error: Invalid value for boolean-like field '${field}'. Expected 'ON' or 'OFF', got '${data[field]}'`); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 기타 최상위 필드의 타입 검사 (예시, 필요에 따라 추가) |
|
|
|
|
|
if (typeof data.analog_input_level !== 'string' && typeof data.analog_input_level !== 'number') { |
|
|
|
|
|
console.error("Validation Error: 'analog_input_level' is not a string or number."); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 리스트 내부 필드 유효성 검사 |
|
|
|
|
|
|
|
|
|
|
|
// MODBUS 리스트 검증 |
|
|
|
|
|
if (data.protocol === 'MODBUS') { |
|
|
|
|
|
// protocol이 'MODBUS'일 경우, MODBUS 배열은 필수 |
|
|
|
|
|
if (!data.hasOwnProperty('MODBUS') || !Array.isArray(data.MODBUS)) { |
|
|
|
|
|
console.error("Validation Error: 'MODBUS' array is required and must be an array when protocol is 'MODBUS'."); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
// MODBUS 배열 내용 유효성 검사 |
|
|
|
|
|
const modbusRequiredFields = ['field', 'idt', 'addr', 'odt', 'dv']; |
|
|
|
|
|
for (const [index, item] of data.MODBUS.entries()) { |
|
|
|
|
|
if (typeof item !== 'object' || item === null) { |
|
|
|
|
|
console.error(`Validation Error: MODBUS item at index ${index} is not an object.`); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
for (const field of modbusRequiredFields) { |
|
|
|
|
|
if (!item.hasOwnProperty(field) || String(item[field]).trim() === '') { |
|
|
|
|
|
console.error(`Validation Error: MODBUS item at index ${index} missing or empty required field: '${field}'`); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
// protocol이 'MODBUS'가 아닌 경우, MODBUS 배열은 존재하지 않거나 빈 배열이어야 함 |
|
|
|
|
|
if (data.hasOwnProperty('MODBUS') && data.MODBUS.length > 0) { |
|
|
|
|
|
console.error("Validation Error: 'MODBUS' array must be empty or absent when protocol is not 'MODBUS'."); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
// 만약 존재한다면 배열 타입이어야 함 |
|
|
|
|
|
if (data.hasOwnProperty('MODBUS') && !Array.isArray(data.MODBUS)) { |
|
|
|
|
|
console.error("Validation Error: 'MODBUS' field must be an array if present, even when protocol is not 'MODBUS'."); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// OPC_UA 리스트 검증 |
|
|
|
|
|
if (data.protocol === 'OPC_UA') { |
|
|
|
|
|
// protocol이 'OPC_UA'일 경우, OPC_UA 배열은 필수 |
|
|
|
|
|
if (!data.hasOwnProperty('OPC_UA') || !Array.isArray(data.OPC_UA)) { |
|
|
|
|
|
console.error("Validation Error: 'OPC_UA' array is required and must be an array when protocol is 'OPC_UA'."); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
// OPC_UA 배열 내용 유효성 검사 |
|
|
|
|
|
const opcUaRequiredFields = ['field', 'ns', 'addr', 'odt', 'dv']; |
|
|
|
|
|
for (const [index, item] of data.OPC_UA.entries()) { |
|
|
|
|
|
if (typeof item !== 'object' || item === null) { |
|
|
|
|
|
console.error(`Validation Error: OPC_UA item at index ${index} is not an object.`); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
for (const field of opcUaRequiredFields) { |
|
|
|
|
|
if (!item.hasOwnProperty(field) || String(item[field]).trim() === '') { |
|
|
|
|
|
console.error(`Validation Error: OPC_UA item at index ${index} missing or empty required field: '${field}'`); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
// protocol이 'OPC_UA'가 아닌 경우, OPC_UA 배열은 존재하지 않거나 빈 배열이어야 함 |
|
|
|
|
|
if (data.hasOwnProperty('OPC_UA') && data.OPC_UA.length > 0) { |
|
|
|
|
|
console.error("Validation Error: 'OPC_UA' array must be empty or absent when protocol is not 'OPC_UA'."); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
// 만약 존재한다면 배열 타입이어야 함 |
|
|
|
|
|
if (data.hasOwnProperty('OPC_UA') && !Array.isArray(data.OPC_UA)) { |
|
|
|
|
|
console.error("Validation Error: 'OPC_UA' field must be an array if present, even when protocol is not 'OPC_UA'."); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// CAN 리스트 검증 (can_input이 'on'인 경우 필수 - protocol 값과는 독립적) |
|
|
|
|
|
if (data.can_input.toLowerCase() === 'on') { |
|
|
|
|
|
if (!data.hasOwnProperty('CAN') || !Array.isArray(data.CAN)) { |
|
|
|
|
|
console.error("Validation Error: 'CAN' is required when can_input is 'on', and must be an array."); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
const canRequiredFields = ['field', 'id', 'odt', 'dv']; |
|
|
|
|
|
for (const [index, item] of data.CAN.entries()) { |
|
|
|
|
|
if (typeof item !== 'object' || item === null) { |
|
|
|
|
|
console.error(`Validation Error: CAN item at index ${index} is not an object.`); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
for (const field of canRequiredFields) { |
|
|
|
|
|
if (!item.hasOwnProperty(field) || String(item[field]).trim() === '') { |
|
|
|
|
|
console.error(`Validation Error: CAN item at index ${index} missing or empty required field: '${field}'`); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
// can_input이 'off'일 경우 CAN이 존재하면 배열이어야 함 (선택 사항) |
|
|
|
|
|
if (data.hasOwnProperty('CAN') && data.CAN.length > 0) { |
|
|
|
|
|
console.error("Validation Error: 'CAN' array must be empty or absent when can_input is 'off'."); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
if (data.hasOwnProperty('CAN') && !Array.isArray(data.CAN)) { |
|
|
|
|
|
console.error("Validation Error: 'CAN' field should be an array if present, even when can_input is 'off'."); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 모든 검사를 통과하면 유효함 |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
const list = document.getElementById('modbus-field-list'); |
|
|
const list = document.getElementById('modbus-field-list'); |
|
|
// 처음에 행이 없으면 빈 행을 하나 추가 |
|
|
// 처음에 행이 없으면 빈 행을 하나 추가 |
|
|
if (list.children.length === 0) { |
|
|
if (list.children.length === 0) { |
|
|
@ -2151,89 +2599,6 @@ |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
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', () => { |
|
|
document.getElementById('saveProtocolBtn').addEventListener('click', () => { |
|
|
console.log('버튼 클릭 됨'); |
|
|
console.log('버튼 클릭 됨'); |
|
|
@ -2327,16 +2692,6 @@ |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
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', () => { |
|
|
document.getElementById('saveCanBtn').addEventListener('click', () => { |
|
|
fetch('http://localhost:8080/setting/register', { |
|
|
fetch('http://localhost:8080/setting/register', { |
|
|
method: 'POST', |
|
|
method: 'POST', |
|
|
@ -2352,82 +2707,7 @@ |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('uploadProtocolBtn').addEventListener('click', () => { |
|
|
|
|
|
fetch('http://localhost:8080/setting/register-upload', { |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
|
|
|
body: JSON.stringify(collectConfigData()) |
|
|
|
|
|
}) |
|
|
|
|
|
.then(res => res.json()) |
|
|
|
|
|
.then(data => { |
|
|
|
|
|
// result_code가 200이면 성공 |
|
|
|
|
|
if (data.result && data.result.result_code === 200) { |
|
|
|
|
|
alert('하드웨어 업로드 성공!'); |
|
|
|
|
|
} else { |
|
|
|
|
|
alert('하드웨어 업로드 실패: ' + (data.result?.result_message || data.body || '')); |
|
|
|
|
|
} |
|
|
|
|
|
}) |
|
|
|
|
|
.catch(err => { |
|
|
|
|
|
alert('통신 오류: ' + err.message); |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
document.getElementById('uploadModbusBtn').addEventListener('click', () => { |
|
|
|
|
|
fetch('http://localhost:8080/setting/register-upload', { |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
|
|
|
body: JSON.stringify(collectConfigData()) |
|
|
|
|
|
}) |
|
|
|
|
|
.then(res => res.json()) |
|
|
|
|
|
.then(data => { |
|
|
|
|
|
// result_code가 200이면 성공 |
|
|
|
|
|
if (data.result && data.result.result_code === 200) { |
|
|
|
|
|
alert('하드웨어 업로드 성공!'); |
|
|
|
|
|
} else { |
|
|
|
|
|
alert('하드웨어 업로드 실패: ' + (data.result?.result_message || data.body || '')); |
|
|
|
|
|
} |
|
|
|
|
|
}) |
|
|
|
|
|
.catch(err => { |
|
|
|
|
|
alert('통신 오류: ' + err.message); |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
document.getElementById('uploadOpcuaFileBtn').addEventListener('click', () => { |
|
|
|
|
|
fetch('http://localhost:8080/setting/register-upload', { |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
|
|
|
body: JSON.stringify(collectConfigData()) |
|
|
|
|
|
}) |
|
|
|
|
|
.then(res => res.json()) |
|
|
|
|
|
.then(data => { |
|
|
|
|
|
// result_code가 200이면 성공 |
|
|
|
|
|
if (data.result && data.result.result_code === 200) { |
|
|
|
|
|
alert('하드웨어 업로드 성공!'); |
|
|
|
|
|
} else { |
|
|
|
|
|
alert('하드웨어 업로드 실패: ' + (data.result?.result_message || data.body || '')); |
|
|
|
|
|
} |
|
|
|
|
|
}) |
|
|
|
|
|
.catch(err => { |
|
|
|
|
|
alert('통신 오류: ' + err.message); |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
document.getElementById('uploadCanBtn').addEventListener('click', () => { |
|
|
|
|
|
fetch('http://localhost:8080/setting/register-upload', { |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
|
|
|
body: JSON.stringify(collectConfigData()) |
|
|
|
|
|
}) |
|
|
|
|
|
.then(res => res.json()) |
|
|
|
|
|
.then(data => { |
|
|
|
|
|
// result_code가 200이면 성공 |
|
|
|
|
|
if (data.result && data.result.result_code === 200) { |
|
|
|
|
|
alert('하드웨어 업로드 성공!'); |
|
|
|
|
|
} else { |
|
|
|
|
|
alert('하드웨어 업로드 실패: ' + (data.result?.result_message || data.body || '')); |
|
|
|
|
|
} |
|
|
|
|
|
}) |
|
|
|
|
|
.catch(err => { |
|
|
|
|
|
alert('통신 오류: ' + err.message); |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
</script> |
|
|
</script> |
|
|
</body> |
|
|
</body> |
|
|
</html> |
|
|
</html> |