Files
HC900-Crawler/src/Hc900Crawler/wwwroot/js/write.js
windpacer daeb5316a2 refactor: 태그 대소문자 register-map 기준 대문자로 전역 통일
- FeedforwardSupervisor: PvTag() ToUpperInvariant + empty FeedTag 가드
- FeedforwardConfigStore: 모든 ToLowerInvariant() 제거
- FeedRampAdvisorService: ToLowerInvariant 제거 + StringComparison.OrdinalIgnoreCase
- SimOverrideStore: ToLowerInvariant 제거
- Hc900RealtimeService: HealthCheck SERVING 판정, mapping 없는 태그 대소문자 유지
- PidExtractorService: ToLowerInvariant → OrdinalIgnoreCase 비교
- Hc900Entities: 주석 업데이트 (대문자 표준)
- load_state_labels.py: 소문자 변환 금지, controller_id 파라미터 추가
- Hc900Controllers: 대소문자 무시 정렬
- write.js: .MODE → AutoManState/RemLocSPState/SP_SelectState/TuneSetState
- setup.js/html: 중복 함수 제거, C5 컨트롤러 placeholder
2026-06-04 09:43:37 +09:00

102 lines
5.0 KiB
JavaScript

/* ─────────────────────────────────────────────────────────────
15 HC900 Write
───────────────────────────────────────────────────────────── */
async function writeLoadControllers() {
const d = await api('GET', '/api/gateway/status');
const sel = document.getElementById('w-ctrl');
sel.innerHTML = '';
const controllers = d.controllers || {};
const entries = Object.entries(controllers);
if (entries.length === 0) {
const opt = document.createElement('option');
opt.value = 'HC1'; opt.textContent = 'HC1';
sel.appendChild(opt);
return;
}
entries.forEach(([id, connected]) => {
const opt = document.createElement('option');
opt.value = id; opt.textContent = `${id}${connected ? 'Connected' : 'Disconnected'}`;
sel.appendChild(opt);
});
}
async function wrWriteTag() {
const controllerId = document.getElementById('w-ctrl').value || 'HC1';
const tagName = document.getElementById('wr-nodeid').value.trim();
const value = parseFloat(document.getElementById('wr-value').value);
if (!tagName) return log('wr-log', [{ c: 'err', t: '❌ 태그명을 입력하세요.' }]);
if (isNaN(value)) return log('wr-log', [{ c: 'err', t: '❌ 유효한 숫자를 입력하세요.' }]);
try {
const d = await api('POST', '/api/gateway/write', { controllerId, tagName, value });
log('wr-log', [
{ c: d.success ? 'ok' : 'err', t: (d.success ? '✅ ' : '❌ ') + 'Write ' + esc(tagName) + ' = ' + value + (d.error ? ' → ' + esc(d.error) : '') },
]);
} catch (e) {
log('wr-log', [{ c: 'err', t: '❌ ' + e.message }]);
}
}
async function wrSetMode() {
const controllerId = document.getElementById('w-ctrl').value || 'HC1';
const tagName = document.getElementById('wr-mode-nodeid').value.trim();
const mode = document.getElementById('wr-mode').value;
if (!tagName) return log('wr-log', [{ c: 'err', t: '❌ 태그명을 입력하세요.' }]);
// 모드별 R/W 태그 매핑 (register-map의 .MD(읽기전용)이 아님)
// Auto/Manual → AutoManState, LSP/RSP → RemLocSPState, SP1/SP2 → SP_SelectState, Tune → TuneSetState
const modeMap = { '0': 'AutoManState', '1': 'AutoManState', '2': 'RemLocSPState', '3': 'SP_SelectState', '4': 'TuneSetState' };
const valueMap = { '0': 0, '1': 1, '2': 1, '3': 1, '4': 1 }; // Auto=1, RSP=1, RSP/LSP=1, Tune=1
const modeTag = modeMap[mode] ?? 'AutoManState';
const writeValue = mode === '0' ? 0 : 1; // 0=Manual/LSP, 1=Auto/RSP
try {
const d = await api('POST', '/api/gateway/write', { controllerId, tagName: tagName + '.' + modeTag, value: writeValue });
log('wr-log', [
{ c: d.success ? 'ok' : 'err', t: (d.success ? '✅ ' : '❌ ') + 'Mode ' + esc(tagName) + ' → ' + mode + ' (' + modeTag + ')' + (d.error ? ' → ' + esc(d.error) : '') },
]);
} catch (e) {
log('wr-log', [{ c: 'err', t: '❌ ' + e.message }]);
}
}
async function wrControlOp() {
const controllerId = document.getElementById('w-ctrl').value || 'HC1';
const tagName = document.getElementById('wr-ctrl-tag').value.trim();
const opValue = parseFloat(document.getElementById('wr-ctrl-op').value);
const restoreAuto = document.getElementById('wr-ctrl-restore').checked;
if (!tagName) return log('wr-log', [{ c: 'err', t: '❌ 태그명을 입력하세요.' }]);
if (isNaN(opValue)) return log('wr-log', [{ c: 'err', t: '❌ 유효한 OP 값을 입력하세요.' }]);
try {
const d = await api('POST', '/api/gateway/write', { controllerId, tagName, value: opValue });
log('wr-log', [
{ c: d.success ? 'ok' : 'err', t: (d.success ? '✅ ' : '❌ ') + '통합 제어 ' + esc(tagName) + ' OP=' + opValue + (d.error ? ' — ' + esc(d.error) : '') },
]);
} catch (e) {
log('wr-log', [{ c: 'err', t: '❌ ' + e.message }]);
}
}
async function wrReadTag() {
const tagName = document.getElementById('wr-read-nodeid').value.trim().toLowerCase();
if (!tagName) return;
try {
const pts = await api('GET', '/api/realtime/points');
const point = pts.find(p => (p.TagName || '').toLowerCase() === tagName);
const box = document.getElementById('wr-read-result');
if (point) {
box.innerHTML = `
<div class="kv"><span class="kk">Tag Name</span><span class="kv2">${esc(point.TagName)}</span></div>
<div class="kv"><span class="kk">Value</span><span class="kv2 ok">${esc(point.LiveValue)}</span></div>
<div class="kv"><span class="kk">Timestamp</span><span class="kv2">${point.Timestamp ? new Date(point.Timestamp).toLocaleString('ko-KR') : '-'}</span></div>
`;
} else {
box.innerHTML = `<div class="kv"><span class="kk">Error</span><span class="kv2 err">태그를 찾을 수 없음</span></div>`;
}
} catch (e) {
document.getElementById('wr-read-result').innerHTML = `<div class="kv"><span class="kk">Error</span><span class="kv2 err">${esc(e.message)}</span></div>`;
}
}