Browse Source

코드 정리

v2
원형 1 year ago
parent
commit
d0ff28e492
  1. 649
      src/main/resources/static/index.html

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

@ -723,14 +723,24 @@
can: document.getElementById('page-can')
};
/*----DOM 인터페이스 초기화----*/
document.addEventListener('DOMContentLoaded', () => {
const opcUaList = document.getElementById('opcua-field-list');
const modbusList = document.getElementById('modbus-field-list');
const fileUploadWrappersDevice = document.querySelectorAll('.file-upload-wrapper-device');
const modbusAddBtn = document.getElementById('modbus_add_btn');
const modbusFieldList = document.getElementById('modbus-field-list');
const modbusRowTemplate = document.getElementById('modbus-row-template');
const opcUaAddBtn = document.getElementById('opcua_add_btn');
const opcUaFieldList = document.getElementById('opcua-field-list');
const opcUaRowTemplate = document.getElementById('opcua-row-template');
const canFieldList = document.getElementById('can-field-list');
// 페이지 로드 시 자동으로 JSON 파일들 로드
loadInitialData();
// Device 파일 업로드
// 모든 파일 업로드 래퍼 섹션을 가져옵니다.
const fileUploadWrappersDevice = document.querySelectorAll('.file-upload-wrapper-device');
fileUploadWrappersDevice.forEach(wrapper => {
// 현재 래퍼 내에서 input[type="file"]과 버튼을 찾습니다.
const uploadDeviceFileInput = wrapper.querySelector('.upload-input-device');
@ -868,13 +878,14 @@
// 파일 선택 시 (change 이벤트 발생) 바로 업로드 로직 시작
uploadFileInput.addEventListener('change', async (event) => {
const selectedFile = event.target.files[0];
const fileName = selectedFile.name;
const reader = new FileReader();
if (event.target.files.length === 0) {
return;
}
const selectedFile = event.target.files[0];
const fileName = selectedFile.name;
// 파일명 검증 (data-expected-filename 사용)
if (fileName !== expectedFileName){
alert(`Invalid file. '${expectedFileName}' Please select a file. Current file: ${fileName} `);
@ -882,23 +893,11 @@
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(`The contents of the selected file are not in a valid ${fileType} setting file format.`);
uploadFileInput.value = '';
return;
}
// 백엔드 저장 API 호출 (data-upload-url 사용)
const response = await fetch(uploadUrl, {
method: 'POST',
@ -908,20 +907,24 @@
body: JSON.stringify(jsonData)
});
const result = await response.json();
// 업로드 유효성 검사 로직
if (!isValidRegisterConfig(jsonData)){ // 예를 들어, isValidModbusConfig(jsonData) 등
alert(`The contents of the selected file are not in a valid ${fileType} setting file format.`);
uploadFileInput.value = '';
return;
}
if (!response.ok) {
const errorText = await response.text();
throw new Error(`File upload failed: ${response.status} ${response.statusText} - ${errorText}`);
}
const result = await response.json();
if (result.result && result.result.result_code === 200) {
alert(`File uploaded to Device successfully (${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 => {
@ -954,187 +957,26 @@
alert(`An error occurred while reading the file. (${fileType})`);
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;
}
}
/*---저장 버튼에 대한 로직---*/
document.querySelectorAll('.save-file').forEach(btn=>btn.addEventListener('click', () => {saveToConfig()}))
// 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;
}
}
/*---device 다운로드 버튼에 대한 로직---*/
document.querySelectorAll('.download-device-file').forEach(btn=>{btn.addEventListener('click', () => {
downloadDeviceJson(collectDeviceData(data));
})})
/*---protocol 다운로드 버튼에 대한 로직---*/
document.querySelectorAll('.download-protocol-file').forEach(btn=>{btn.addEventListener('click', () => {
downloadProtocolJson(collectProtocolData(data));
})})
// 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;
}
}
document.getElementById('reg_can_input')?.addEventListener('change', toggleProtocolMenus);
// 모든 검사를 통과하면 유효함
return true;
}
const list = document.getElementById('modbus-field-list');
// 처음에 행이 없으면 빈 행을 하나 추가
if (list.children.length === 0) {
const modbusRowTemplate = document.getElementById('modbus-row-template');
if (modbusRowTemplate) {
const newRow = modbusRowTemplate.content.cloneNode(true);
list.appendChild(newRow);
}
}
let selectedRow = null;
/*---modbus 테이블에 행 추가 로직---*/
document.getElementById('modbus-field-list').addEventListener('click', function (e) {
let tr = e.target.closest('tr');
if (!tr) return;
@ -1152,16 +994,18 @@
document.getElementById('modbus_expr_input').value = tr.querySelector('.expr-display').textContent;
document.getElementById('modbus_mask_input').value = tr.querySelector('.mask-display').textContent;
});
const opcUalist = document.getElementById('opcua-field-list');
// 처음에 행이 없으면 빈 행을 하나 추가
if (opcUalist.children.length === 0) {
const opcUaRowTemplate = document.getElementById('opcua-row-template');
if (opcUaRowTemplate) {
const newRow = opcUaRowTemplate.content.cloneNode(true);
opcUalist.appendChild(newRow);
if (modbusList.children.length === 0) {
const modbusRowTemplate = document.getElementById('modbus-row-template');
if (modbusRowTemplate) {
const newRow = modbusRowTemplate.content.cloneNode(true);
modbusList.appendChild(newRow);
}
}
let selectedRow = null;
/*-------------------------------------------*/
/*---opcua 테이블에 행 추가 로직---*/
document.getElementById('opcua-field-list').addEventListener('click', function (e) {
let tr = e.target.closest('tr');
if (!tr) return;
@ -1179,44 +1023,47 @@
document.getElementById('opcua_expr_input').value = tr.querySelector('.opcua-expr-display').textContent;
document.getElementById('opcua_mask_input').value = tr.querySelector('.opcua-mask-display').textContent;
});
// 처음에 행이 없으면 빈 행을 하나 추가
if (opcUaList.children.length === 0) {
const opcUaRowTemplate = document.getElementById('opcua-row-template');
if (opcUaRowTemplate) {
const newRow = opcUaRowTemplate.content.cloneNode(true);
opcUaList.appendChild(newRow);
}
}
const modbusAddBtn = document.getElementById('modbus_add_btn');
const modbusFieldList = document.getElementById('modbus-field-list');
const modbusRowTemplate = document.getElementById('modbus-row-template');
if (modbusAddBtn && modbusFieldList && modbusRowTemplate) {
modbusAddBtn.addEventListener('click', () => {
const field = document.getElementById('modbus_field_input').value;
const addr = document.getElementById('modbus_addr_input').value;
const idt = document.getElementById('modbus_idt_input').value;
const odt = document.getElementById('modbus_odt_input').value;
const defaultValue = document.getElementById('modbus_dv_input').value;
const shift = document.getElementById('modbus_shift_input').value;
const expr = document.getElementById('modbus_expr_input').value;
const mask = document.getElementById('modbus_mask_input').value;
const newRow = modbusRowTemplate.content.cloneNode(true);
newRow.querySelector('.field-display').textContent = field;
newRow.querySelector('.addr-display').textContent = addr;
newRow.querySelector('.idt-display').textContent = idt;
newRow.querySelector('.odt-display').textContent = odt;
newRow.querySelector('.dv-display').textContent = defaultValue;
newRow.querySelector('.shift-display').textContent = shift;
newRow.querySelector('.expr-display').textContent = expr;
newRow.querySelector('.mask-display').textContent = mask;
modbusFieldList.appendChild(newRow);
modbusAddBtn.addEventListener('click', () => {
const field = document.getElementById('modbus_field_input').value;
const addr = document.getElementById('modbus_addr_input').value;
const idt = document.getElementById('modbus_idt_input').value;
const odt = document.getElementById('modbus_odt_input').value;
const defaultValue = document.getElementById('modbus_dv_input').value;
const shift = document.getElementById('modbus_shift_input').value;
const expr = document.getElementById('modbus_expr_input').value;
const mask = document.getElementById('modbus_mask_input').value;
const newRow = modbusRowTemplate.content.cloneNode(true);
newRow.querySelector('.field-display').textContent = field;
newRow.querySelector('.addr-display').textContent = addr;
newRow.querySelector('.idt-display').textContent = idt;
newRow.querySelector('.odt-display').textContent = odt;
newRow.querySelector('.dv-display').textContent = defaultValue;
newRow.querySelector('.shift-display').textContent = shift;
newRow.querySelector('.expr-display').textContent = expr;
newRow.querySelector('.mask-display').textContent = mask;
modbusFieldList.appendChild(newRow);
document.getElementById('modbus_field_input').value = '';
document.getElementById('modbus_addr_input').value = '';
document.getElementById('modbus_idt_input').value = '';
document.getElementById('modbus_odt_input').value = '';
document.getElementById('modbus_dv_input').value = '';
document.getElementById('modbus_shift_input').value = '';
document.getElementById('modbus_expr_input').value = '';
document.getElementById('modbus_mask_input').value = '';
});
document.getElementById('modbus_field_input').value = '';
document.getElementById('modbus_addr_input').value = '';
document.getElementById('modbus_idt_input').value = '';
document.getElementById('modbus_odt_input').value = '';
document.getElementById('modbus_dv_input').value = '';
document.getElementById('modbus_shift_input').value = '';
document.getElementById('modbus_expr_input').value = '';
document.getElementById('modbus_mask_input').value = '';
});
}
document.getElementById('modbus_modify_btn').onclick = function () {
if (!selectedRow) return alert('Please select the row you want to modify');
@ -1239,43 +1086,38 @@
document.querySelectorAll('.modbus-input-area input').forEach(i => i.value = '');
};
const opcUaAddBtn = document.getElementById('opcua_add_btn');
const opcUaFieldList = document.getElementById('opcua-field-list');
const opcUaRowTemplate = document.getElementById('opcua-row-template');
if (opcUaAddBtn && opcUaFieldList && opcUaRowTemplate) {
opcUaAddBtn.addEventListener('click', () => {
const field = document.getElementById('opcua_field_input').value;
const addr = document.getElementById('opcua_addr_input').value;
const ns = document.getElementById('opcua_ns_input').value;
const odt = document.getElementById('opcua_odt_input').value;
const defaultValue = document.getElementById('opcua_dv_input').value;
const shift = document.getElementById('opcua_shift_input').value;
const expr = document.getElementById('opcua_expr_input').value;
const mask = document.getElementById('opcua_mask_input').value;
const newRow = opcUaRowTemplate.content.cloneNode(true);
newRow.querySelector('.opcua-field-display').textContent = field;
newRow.querySelector('.opcua-addr-display').textContent = addr;
newRow.querySelector('.opcua-ns-display').textContent = ns;
newRow.querySelector('.opcua-odt-display').textContent = odt;
newRow.querySelector('.opcua-dv-display').textContent = defaultValue;
newRow.querySelector('.opcua-shift-display').textContent = shift;
newRow.querySelector('.opcua-expr-display').textContent = expr;
newRow.querySelector('.opcua-mask-display').textContent = mask;
opcUaFieldList.appendChild(newRow);
opcUaAddBtn.addEventListener('click', () => {
const field = document.getElementById('opcua_field_input').value;
const addr = document.getElementById('opcua_addr_input').value;
const ns = document.getElementById('opcua_ns_input').value;
const odt = document.getElementById('opcua_odt_input').value;
const defaultValue = document.getElementById('opcua_dv_input').value;
const shift = document.getElementById('opcua_shift_input').value;
const expr = document.getElementById('opcua_expr_input').value;
const mask = document.getElementById('opcua_mask_input').value;
const newRow = opcUaRowTemplate.content.cloneNode(true);
newRow.querySelector('.opcua-field-display').textContent = field;
newRow.querySelector('.opcua-addr-display').textContent = addr;
newRow.querySelector('.opcua-ns-display').textContent = ns;
newRow.querySelector('.opcua-odt-display').textContent = odt;
newRow.querySelector('.opcua-dv-display').textContent = defaultValue;
newRow.querySelector('.opcua-shift-display').textContent = shift;
newRow.querySelector('.opcua-expr-display').textContent = expr;
newRow.querySelector('.opcua-mask-display').textContent = mask;
opcUaFieldList.appendChild(newRow);
document.getElementById('opcua_field_input').value = '';
document.getElementById('opcua_addr_input').value = '';
document.getElementById('opcua_ns_input').value = '';
document.getElementById('opcua_odt_input').value = '';
document.getElementById('opcua_dv_input').value = '';
document.getElementById('opcua_shift_input').value = '';
document.getElementById('opcua_expr_input').value = '';
document.getElementById('opcua_mask_input').value = '';
});
document.getElementById('opcua_field_input').value = '';
document.getElementById('opcua_addr_input').value = '';
document.getElementById('opcua_ns_input').value = '';
document.getElementById('opcua_odt_input').value = '';
document.getElementById('opcua_dv_input').value = '';
document.getElementById('opcua_shift_input').value = '';
document.getElementById('opcua_expr_input').value = '';
document.getElementById('opcua_mask_input').value = '';
});
}
document.getElementById('opcua_modify_btn').onclick = function () {
if (!selectedRow) return alert('Please select the row you want to modify');
@ -1298,7 +1140,15 @@
document.querySelectorAll('.opcua-input-area input').forEach(i => i.value = '');
};
// CAN 테이블
if (canFieldList.children.length === 0) {
const canRowTemplate = document.getElementById('can-row-template');
if (canRowTemplate) {
const newRow = canRowTemplate.content.cloneNode(true);
canFieldList.appendChild(newRow);
}
}
// CAN 테이블 추가 로직
document.getElementById('can_add_btn').addEventListener('click', () => {
const field = document.getElementById('can_field_input').value;
const id = document.getElementById('can_id_input').value;
@ -1325,6 +1175,8 @@
document.getElementById('can_expr_input').value = '';
document.getElementById('can_mask_input').value = '';
});
// can 테이블 수정 로직
document.getElementById('can_modify_btn').onclick = function () {
if (!selectedRow) return alert('Please select the row you want to modify');
selectedRow.querySelector('.can-field-display').textContent = document.getElementById('can_field_input').value;
@ -1335,6 +1187,8 @@
selectedRow.querySelector('.can-expr-display').textContent = document.getElementById('can_expr_input').value;
selectedRow.querySelector('.can-mask-display').textContent = document.getElementById('can_mask_input').value;
};
//can 테이블 삭제 로직
document.getElementById('can_delete_btn').onclick = function () {
if (!selectedRow) return alert('Select the row you want to delete');
const list = document.getElementById('can-field-list');
@ -1343,7 +1197,7 @@
document.querySelectorAll('.can-input-area input').forEach(i => i.value = '');
};
// 프로토콜에 따른 사이드바 변경 로직
regProtocolSelect = document.getElementById('reg_protocol');
if (regProtocolSelect) {
regProtocolSelect.addEventListener('change', () => {
@ -1353,6 +1207,7 @@
toggleProtocolMenus();
}
/*----서버로 데이터 전송하는 함수----*/
function saveToConfig() {
const deviceData = collectDeviceData();
const protocolData = collectProtocolData();
@ -1361,14 +1216,14 @@
alert("Register settings are currently invalid. Cannot be saved.");
return;
}
// 두 개의 fetch를 병렬 실행
//device
const deviceReq = fetch('http://localhost:8080/setting/device', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(deviceData)
});
//protocol
const protocolReq = fetch('http://localhost:8080/setting/protocol', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@ -1390,56 +1245,19 @@
alert('save failure');
});
}
document.querySelectorAll('.save-file').forEach(btn=>btn.addEventListener('click', () => {saveToConfig()}))
document.querySelectorAll('.download-device-file').forEach(btn=>{btn.addEventListener('click', () => {
downloadDeviceJson(collectDeviceData(data));
})})
document.querySelectorAll('.download-protocol-file').forEach(btn=>{btn.addEventListener('click', () => {
downloadProtocolJson(collectProtocolData(data));
})})
document.getElementById('reg_can_input')?.addEventListener('change', toggleProtocolMenus);
const canFieldList = document.getElementById('can-field-list');
if (canFieldList.children.length === 0) {
const canRowTemplate = document.getElementById('can-row-template');
if (canRowTemplate) {
const newRow = canRowTemplate.content.cloneNode(true);
canFieldList.appendChild(newRow);
}
}
// CAN row 클릭 시 선택 표시 및 입력폼에 값 반영
canFieldList.addEventListener('click', function (e) {
let tr = e.target.closest('tr');
if (!tr) return;
Array.from(this.children).forEach(row => row.classList.remove('selected-row'));
tr.classList.add('selected-row');
selectedRow = tr;
document.getElementById('can_field_input').value = tr.querySelector('.can-field-display').textContent;
document.getElementById('can_id_input').value = tr.querySelector('.can-id-display').textContent;
document.getElementById('can_odt_input').value = tr.querySelector('.can-odt-display').textContent;
document.getElementById('can_dv_input').value = tr.querySelector('.can-dv-display').textContent;
document.getElementById('can_shift_input').value = tr.querySelector('.can-shift-display').textContent;
document.getElementById('can_expr_input').value = tr.querySelector('.can-expr-display').textContent;
document.getElementById('can_mask_input').value = tr.querySelector('.can-mask-display').textContent;
});
});
//네이게이션 바 클릭시 해당 페이지로 이동 로직
document.querySelectorAll('.nav-items button').forEach(btn => {
btn.addEventListener('click', () => {
showPage(btn.dataset.page);
});
});
//ssid 행 추가 버튼
document.getElementById('add-ssid').addEventListener('click', addSsidRow);
//ssid 행 삭제 버튼
document.getElementById('delete-ssid').addEventListener('click', () => {
if (ssidList.children.length > 1) {
ssidList.removeChild(ssidList.lastElementChild);
@ -1451,7 +1269,6 @@
}
})
/*-----device input데이터 받는 함수------*/
function collectDeviceData() {
const res = { ...data };
@ -1536,6 +1353,7 @@
collectedRegister.MODBUS.push(obj);
}
});
// OPC_UA 테이블 수집 (빈 값은 필드에서 제외)
const opcuaRows = document.querySelectorAll('#opcua-field-list tr');
collectedRegister.OPC_UA = [];
@ -1561,6 +1379,7 @@
collectedRegister.OPC_UA.push(obj);
}
});
// CAN 테이블 수집 (빈 값은 필드에서 제외)
const canRows = document.querySelectorAll('#can-field-list tr');
collectedRegister.CAN = [];
@ -1670,6 +1489,7 @@
}
/*-----------------------------------------------------*/
/*------데이터상 ssid 추가 함수--------*/
function createSsidRow(obj = {wifi_ssid: '', wifi_password: '', wifi_security: 'none'}) {
const node = ssidTmpl.content.firstElementChild.cloneNode(true);
node.querySelector('.ssid-name').value = obj.wifi_ssid ?? '';
@ -1677,7 +1497,9 @@
node.querySelector('select').value = (obj.wifi_security ?? 'none').toLowerCase();
return node;
}
/*-----------------------------------------------------*/
/*-------ssid 데이터 리스트 추가 함수-----------*/
function addSsidRow() {
ssidList.appendChild(createSsidRow());
}
@ -1694,7 +1516,9 @@
}
}
});
/*-----------------------------------------------------*/
/*-------ssid 리스트 병합 함수-------------*/
function gatherSsidRows() {
const arr = [];
ssidList.querySelectorAll('.ssid-row').forEach(row => {
@ -1705,8 +1529,180 @@
});
return arr.length ? arr : [{wifi_ssid: '', wifi_password: '', wifi_security: 'none'}];
}
/*-----------------------------------------------------*/
/*------프로토콜 데이터 유효성 검사 로직---------*/
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;
}
/*-----------------------------------------------------*/
/*-------network dhcp 이면 input field 잠금------------*/
document.querySelectorAll('input[name="ipMode"]').forEach(r => r.addEventListener('change', toggleNetInputs));
/* ───────────── 4. DHCP ↔ Static 전환 입력 잠금 ───────────── */
const netFields = ['wifi_ip', 'wifi_netmask', 'wifi_gateway', 'wifi_dns1', 'wifi_dns2']
.map(id => document.getElementById(id));
@ -1717,10 +1713,9 @@
el.classList.toggle('disabled', !isStatic);
});
}
/*-----------------------------------------------------*/
document.querySelectorAll('input[name="ipMode"]').forEach(r => r.addEventListener('change', toggleNetInputs));
/*-------imu-remap 정수와 부호 분리 함수-------------*/
function axisValueToPair(val) {
return val.startsWith('-')
? {axis: val.slice(1), sign: 'minus'}
@ -1730,8 +1725,9 @@
function pairToAxisValue(axis, sign) {
return sign === 'minus' ? `-${axis}` : axis;
}
/*-----------------------------------------------------*/
/* ───────────── 6. 데이터 → 화면 로딩 ───────────── */
/*--------초기 랜더링 화면 함수------------*/
function loadFromData(obj) {
/* Wi-Fi 모드 radio */
document.querySelector(`input[name="ipMode"][value="${obj.wifi_static === 'on' ? 'static' : 'dhcp'}"]`).checked = true;
@ -1756,9 +1752,11 @@
toggleNetInputs(); // 초기 잠금 상태
}
loadFromData(data);
/*-----------------------------------------------------*/
/*--------토굴 프로토콜 and can input 함수------------*/
let regProtocolSelect;
function toggleProtocolMenus() {
@ -1784,7 +1782,9 @@
const protocolDetails = document.querySelectorAll('details.nav')[1];
if (protocolDetails) protocolDetails.open = true;
}
/*-----------------------------------------------------*/
/*--------서버 DTO 기준 json 데이터 불러오는 함수------------------*/
function loadFromRegisterJson(register) { // register.json 데이터 로드
if (!register) {
console.error("Register data is null or undefined.");
@ -1903,7 +1903,6 @@
const newRow = canRowTemplate.content.cloneNode(true);
canFieldList.appendChild(newRow);
}
}
</script>
</body>

Loading…
Cancel
Save