MSRE206 Controller

Fullständig Kontroll: YLK Electronics MSRE206

--- Välj Port... ---
Initialiseras...

⚙️ Kontroll & Status

🔧 Konfigurationsinställningar

💾 ISO Data (Track 1, 2, 3)

🧬 Raw Data (Binary Hex)

📋 Logg & Respons

[SYSTEM] Initialiseras...

LPDEM & Protokollhantering

Välj kommunikationsmetod. Web Serial API hanterar både fysisk port (COM/TTY) och virtualisering via LPDEM/TCP direkt i webbläsaren.

Vald kommunikationsmetod: SERIAL
// ======================== VARIABLER ======================== const comTypeSelect = document.getElementById('comTypeSelect'); const portIdentifier = document.getElementById('portIdentifier'); const portIdentifierSelect = document.getElementById('portIdentifierSelect'); const tcpPortInput = document.getElementById('tcpPortInput'); const currentCommTypeDisplay = document.getElementById('currentCommType'); // UI Elements const connectionStatusDisplay = document.getElementById('connectionStatusDisplay'); const currentPortDisplay = document.getElementById('currentPortDisplay'); const connectionStatusText = document.getElementById('connectionStatusText'); const consoleLog = document.getElementById('consoleLog'); const lastResponseHexSpan = document.getElementById('lastResponseHex'); const lastResponseAsciiSpan = document.getElementById('lastResponseAscii'); const lastResponseMessageSpan = document.getElementById('lastResponseMessage'); const lastResponseBox = document.getElementById('lastResponseBox'); // State let status = { port: null, status: 'INITIALIZING', serialPort: null, reader: null, writer: null }; let log = []; let lastResponse = null; let isLoading = false; // ======================== CORE FUNCTIONS ======================== // Funktion för att lägga till loggmeddelanden function addLog(message, type = 'INFO') { const p = document.createElement('p'); let colorClass = 'text-gray-400'; switch(type) { case 'INFO': colorClass = 'text-gray-400'; break; case 'ACTION': colorClass = 'text-blue-300 font-semibold'; break; case 'RESULT': colorClass = 'text-green-300'; break; case 'ERROR': colorClass = 'text-red-400'; break; case 'FATAL': colorClass = 'text-yellow-300 font-bold'; break; case 'RESPONSE': colorClass = 'text-yellow-200'; break; } p.className = `${colorClass} break-words`; p.innerHTML = `[${type}] ${message}`; consoleLog.prepend(p); log.push({ type, message }); } // Funktion för att uppdatera statusindikatorn function updateStatus(newStatus, port) { status.port = port; status.status = newStatus; const map = { 'CONNECTED': { text: 'CONNECTED', color: 'bg-green-500', pulse: 'animate-pulse-green' }, 'DISCONNECTED': { text: 'DISCONNECTED', color: 'bg-red-500', pulse: '' }, 'CONNECTING': { text: 'CONNECTING', color: 'bg-yellow-500', pulse: 'animate-pulse-green' }, 'ERROR': { text: 'ERROR', color: 'bg-red-500', pulse: '' }, 'INITIALIZING': { text: 'INITIALIZING', color: 'bg-indigo-500', pulse: 'animate-pulse-green' } }; const map = map[newStatus] || map['INITIALIZING']; // Uppdatera huvudstatusfältet connectionStatusDisplay.className = `mt-6 inline-flex items-center p-4 rounded-full text-base font-bold transition duration-300 ${map.color} ${map.pulse}`; connectionStatusDisplay.querySelector('span:last-child').textContent = `Status: ${newStatus} (${port})`; } // Uppdaterar sista respons-boxen function updateResponseDisplay(hex, ascii, customMsg = null) { lastResponse = { hex: hex, ascii: ascii, message: customMsg }; lastResponseHexSpan.textContent = hex; lastResponseAsciiSpan.textContent = ascii; lastResponseMessageSpan.textContent = customMsg || 'Ingen specifik meddelande.'; lastResponseBox.classList.remove('hidden'); } // ========= KOMMUNIKATIONSHANTERING (CORE) ========= /** * Läser data kontinuerligt från den öppna porten och uppdaterar loggen. * Detta måste köras separat från själva skickningsoperationen. */ async function readSerialStream() { if (!status.reader) return; try { const decoder = new TextDecoder(); // Läser en chunk av data från porten const { value, done } = await status.reader.read(); if (done) { addLog(`[SYSTEM] Dataström avslutad. Porten kan ha stängts.`, 'INFO'); updateStatus('DISCONNECTED', status.port); return; } // Hitta där av en fullständig byte (finns ofta flera bytes i en chunk) const dataChunk = new Uint8Array(value); // Hitta förekomster av null byte (0x00) som avgränsare, eller bara logga hela chunken let chunkData = ''; for (const byte of dataChunk) { chunkData += String.fromCharCode(byte); } // Logga hela datablocket för enkelhetens skull const responseHex = Array.from(new Uint8Array(value)).map(b => b.toString(16).toUpperCase().padStart(2, '0')).join(''); const responseAscii = chunkData.trim(); addLog(`[RESPONSE] Mottaget svar! ${responseHex} (${responseAscii})`, 'RESPONSE'); updateResponseDisplay(responseHex, responseAscii, `Data mottagen från ${status.port}`); // Återkalla funktionen för att fortsätta lyssna readSerialStream(); } catch (error) { addLog(`[FATAL ERROR] Stream läsfel: ${error.name} - ${error.message}`, 'FATAL'); updateStatus('ERROR', status.port); } } /** * Skickar ett kommando och väntar på ett svar. * @param {string} commandHex - Kommandon i Hex-format (t.ex. '1B65') */ async function executeCommand(commandHex) { if (isLoading) { addLog("[WARNING] Väntar på föregående kommando att avslutas...", 'ERROR'); return; } const selectedPort = comTypeSelect.value === 'TCP' ? document.getElementById('portIdentifierInput').value : document.getElementById('portIdentifierSelect').value; const selectedType = comTypeSelect.value; if (!selectedPort) { addLog("[WARNING] Vänligen välj en COM/IP-port först!", 'ERROR'); return; } isLoading = true; addLog(`\n========== COMMAND START ==========\n[ACTION] Skickar kommando: ${commandHex} (${selectedType}) till ${selectedPort}`, 'ACTION'); try { // 1. Skicka data const encoder = new TextEncoder(); const dataToSend = encoder.encode(commandHex); if (selectedType === 'SERIAL' || selectedType === 'LPDEM') { await status.writer.write(dataToSend); } else if (selectedType === 'TCP') { // TCP-hantering är mer komplex (kräver socket), här använder vi en mock/simulering addLog(`[SIMULATION] Skickar ${commandHex} via TCP till ${selectedPort}`, 'INFO'); // Mocka en liten fördröjning och simulerar att skrivningen skedde await new Promise(resolve => setTimeout(resolve, 50)); } // 2. STARTA LYSNING (om den inte redan lyssnar) if (!status.reader) { // Starterna läser i bakgrunden medan skrivningen sker readSerialStream(); } } catch (error) { addLog(`[FATAL ERROR] Kommunikationsfel: ${error.name} - ${error.message}`, 'FATAL'); updateStatus('ERROR', status.port); } finally { isLoading = false; } } // ========= PROTOKOLL & STYRNING ========= /** * Hanterar växling mellan SERIAL, LPDEM och TCP. */ async function updatePortAndCommType() { const selectedType = comTypeSelect.value; const portId = selectedType === 'TCP' ? document.getElementById('portIdentifierInput').value : document.getElementById('portIdentifierSelect').value; // UI Uppdatering currentCommTypeDisplay.textContent = selectedType; currentPortDisplay.textContent = portId || '--- Välj Port... ---'; // Byta visning beroende på typ const isTCP = selectedType === 'TCP'; const portIdDisplay = document.getElementById('portIdentifier'); if (isTCP) { portIdDisplay.innerHTML = ``; // Attach listener till den nya inputen document.getElementById('portIdentifierInput').addEventListener('change', () => { currentPortDisplay.textContent = document.getElementById('portIdentifierInput').value || '--- Välj Port ---'; checkPortStatus(document.getElementById('portIdentifierInput').value); }); } else { portIdDisplay.innerHTML = ` `; // Attach listener till SELECT document.getElementById('portIdentifierSelect').addEventListener('change', () => { currentPortDisplay.textContent = document.getElementById('portIdentifierSelect').value || '--- Välj Port ---'; checkPortStatus(document.getElementById('portIdentifierSelect').value); }); } // Kör statuscheck för att bekräfta att den nya konfigurationen fungerar if (portId) { checkPortStatus(portId); } else { updateStatus('DISCONNECTED', '---'); } } // Dynamiskt hämtar/uppdaterar portlistan baserat på val async function refreshPortList(currentType) { try { // Anropa serialPort.getPorts() const ports = await navigator.serial.getPorts(); let portList = []; ports.forEach(async (port) => { const name = port.getInfo().path; portList.push({ path: name, readable: true, writable: true }); }); if (portList.length === 0) { // Fallback om inget hittas portList.push({ path: 'N/A (None found)', readable: false, writable: false }); } // --- UI POPULATION --- const defaultOption = document.createElement('option'); defaultOption.value = ""; defaultOption.textContent = "--- Välj Port ---"; portIdentifierSelect.innerHTML = ''; // Rensa gamla alternativ portIdentifierSelect.appendChild(defaultOption); portList.forEach(p => { const option = document.createElement('option'); option.value = p.path; option.textContent = p.path; portIdentifierSelect.appendChild(option); }); // Välj den första porten som standard och kör statuscheck const firstPort = portList[0].path; portIdentifierSelect.value = firstPort; currentPortDisplay.textContent = firstPort; // När portlistan är uppdaterad, kör statuschecken för den valda porten checkPortStatus(firstPort); } catch (err) { // Error kan vara permission error eller att API:et inte stöds let message = err.name === 'NotFoundError' ? 'Inga portar hittades på systemet.' : `API Error: ${err.message}`; addLog(`[SYSTEM] Misslyckades att hämta portlista: ${message}`, 'FATAL'); updateStatus('ERROR', 'N/A'); if (err.name === 'SecurityError') { addLog("[SYSTEM] Security Error: Kör denna sida via HTTPS för att garantera portåtkomst.", 'FATAL'); } } } // Initierar hela protokollval-systemet function initializeProtocolSelection() { comTypeSelect.addEventListener('change', () => { updatePortAndCommType(); }); // Kör första gången för att fylla portlistan och sätta standardporten refreshPortList('SERIAL'); } function initializeApp() { addLog("[SYSTEM] Applikationen startar. Försök hämta portlista...", 'INFO'); initializeProtocolSelection(); } // ======================== SPECIFIKA KOMMAND (FUNKTIONER) ======================== // --- ISO DATA HANDLER --- function readISOData() { executeCommand('1B72'); } function writeISOData() { const t1 = document.getElementById('isoTrack1').value.toUpperCase(); const t2 = document.getElementById('isoTrack2').value.toUpperCase(); const t3 = document.getElementById('isoTrack3').value.toUpperCase(); // Data Block: 1B 73 [T1] 3F 1C 1B [T2] 3F 1C 1B [T3] 3F 1C const dataBlock = `1B73${t1}3F1C1B${t2}3F1C1B${t3}3F1C`; const commandHex = `1B77${dataBlock}`; // w [Data Block] executeCommand(commandHex); } function eraseISOData() { // Command: c [Select Byte] -> Hex code: 1B 63 [Select Byte] // Select Byte 00000111 = Track 1, 2 & 3 (Hex: 07) const selectByte = '07'; const commandHex = `1B63${selectByte}`; executeCommand(commandHex); } // --- RAW DATA HANDLER --- function readRawData() { executeCommand('1B6D'); } function writeRawData() { const rawData = document.getElementById('rawData').value.replace(/\s/g, ''); if (rawData.length < 2) { addLog("[WARNING] Inga rådata angivna för skrift.", 'ERROR'); return; } // Command: n [Raw Data Block] -> Hex code: 1B 6E [Raw Data Block] const commandHex = `1B6E${rawData}`; executeCommand(commandHex); } // --- CONFIGURATION HANDLERS --- function setBPIConfig() { // Command: b [Density] (Track 2: 1B 62 [D2 or 4B]) const bpiValue = document.getElementById('configBPI').value; const commandHex = `1B62${bpiValue}`; executeCommand(commandHex); } function setLZConfig() { // Command: z [TK1/3 LZ] [TK2 LZ] -> Hex code: 1B 7A [00~ff] [00~ff] const lzT1T3 = document.getElementById('configLZ').value.substring(0, 2).toUpperCase(); const lzT2 = '16'; // Standard LZ för Track 2 const commandHex = `1B7A${lzT1T3}${lzT2}`; executeCommand(commandHex); } // --- ALLMANLIGA KOMMAND --- function setHiCoConfig() { executeCommand('1B78'); } // x function setLowCoConfig() { executeCommand('1B79'); } // y function getCoStatus() { executeCommand('1B64'); } // d function checkLeadingZero() { executeCommand('1B6C'); } // l function ramTest() { executeCommand('1B87'); } // 1B 87 function sensorTest() { executeCommand('1B86'); } // 1B 86 function getModel() { executeCommand('1B74'); } // t function getFirmwareVersion() { executeCommand('76'); } // 76 // Kör appen när DOM är helt laddad document.addEventListener('DOMContentLoaded', initializeApp);