Generate Target
-Configure the colour patches for your profiling target.
- -From 30473b0359b60b7daf5fe659807c3d722f5b043f Mon Sep 17 00:00:00 2001 From: gronod <1+gronod@noreply@i3omb.com> Date: Fri, 28 Aug 2026 18:47:44 +0100 Subject: [PATCH 1/3] fix(stage3): prevent instlist device index from passing as chartread -c comm port (fixes #111) --- src/js/chartread.js | 491 +------------------------------------------- 1 file changed, 1 insertion(+), 490 deletions(-) diff --git a/src/js/chartread.js b/src/js/chartread.js index 7d3190e..7a172e2 100644 --- a/src/js/chartread.js +++ b/src/js/chartread.js @@ -1,490 +1 @@ -const { invoke } = window.__TAURI__.core; -const { listen } = window.__TAURI__.event; -import { startSwatchListener, stopSwatchListener } from './swatch_grid.js'; -import { setStage3Result } from './colprof.js'; -import { wizardState } from './state.js'; - -// Module-level state: set by Stage 2 when it completes -let stage2Basename = ""; -let stage2Cwd = ""; - -/** - * Called by printtarg.js after Stage 2 completes. - */ -export function setStage2Result(basename, cwd) { - stage2Basename = basename || wizardState.basename; - stage2Cwd = cwd || wizardState.cwd; - wizardState.setTarget(stage2Basename, stage2Cwd); -} - -// State machine states -const STATE = { - IDLE: "IDLE", - CALIBRATING: "CALIBRATING", - AWAITING_STRIP: "AWAITING_STRIP", - READING: "READING", - ERROR: "ERROR", - FINISHED: "FINISHED", -}; - -let currentState = STATE.IDLE; -let currentProcessId = ""; -let measurementInProgress = false; -let currentPassIndex = 0; -const recordedPasses = []; - -export function initChartread() { - const btnStartRead = document.getElementById("btnStartRead"); - const btnCalibrate = document.getElementById("btnCalibrate"); - const btnRetry = document.getElementById("btnRetry"); - const btnSkip = document.getElementById("btnSkip"); - const btnCancel = document.getElementById("btnCancel"); - const btnDetectInstruments = document.getElementById("btnDetectInstruments"); - const instrumentSelect = document.getElementById("chartreadInstrumentSelect"); - const promptText = document.getElementById("chartreadPrompt"); - const stateLabel = document.getElementById("chartreadState"); - const logContainer = document.getElementById("chartreadLogContainer"); - const logPre = document.getElementById("chartreadLog"); - const averagingPanel = document.getElementById("chartreadAveragingPanel"); - const passesList = document.getElementById("passesList"); - const passCounterBadge = document.getElementById("passCounterBadge"); - const btnMeasureAnotherSheet = document.getElementById("btnMeasureAnotherSheet"); - const btnFinishAndAverage = document.getElementById("btnFinishAndAverage"); - - function setMeasurementBusy(busy) { - measurementInProgress = busy; - if (btnStartRead) btnStartRead.disabled = busy; - if (btnMeasureAnotherSheet) btnMeasureAnotherSheet.disabled = busy; - if (btnFinishAndAverage) { - btnFinishAndAverage.disabled = busy || recordedPasses.length === 0; - } - } - - function renderPassesList() { - if (!passesList) return; - passesList.innerHTML = ""; - recordedPasses.forEach((pass, i) => { - const item = document.createElement("div"); - item.style.cssText = "display:flex; justify-content:space-between; align-items:center; background:rgba(255,255,255,0.05); padding:6px 10px; border-radius:4px; font-size:0.85rem;"; - const label = document.createElement("span"); - const strong = document.createElement("strong"); - strong.textContent = `Sheet Pass #${i + 1}`; - label.appendChild(strong); - label.appendChild(document.createTextNode(` (${pass.filename})`)); - const time = document.createElement("span"); - time.style.opacity = "0.7"; - time.textContent = `✓ ${pass.time}`; - item.appendChild(label); - item.appendChild(time); - passesList.appendChild(item); - }); - } - - function acceptStage3(basename, cwd) { - wizardState.setTarget(basename, cwd); - setStage3Result(basename, cwd); - wizardState.updateGating(); - advanceToStage4(); - } - - // Instrument detection logic - if (btnDetectInstruments && instrumentSelect) { - btnDetectInstruments.addEventListener("click", async () => { - btnDetectInstruments.disabled = true; - btnDetectInstruments.textContent = "Detecting..."; - setPrompt("Querying connected spectrophotometers and colorimeters via instlist..."); - - const detected = []; - - try { - const unlistenStdout = await listen("process:stdout", (event) => { - if (event.payload.id !== "instlist" || !event.payload.line) return; - const line = event.payload.line.trim(); - - // Matches Argyll instlist output e.g. "1: 'i1Pro' on 'USB'" or "1: 'ColorMunki'" or "1 = 'i1Display'" - const match = line.match(/^(\d+)[\s:=]+'?([^'\n]+)'?(?:\s+on\s+'?([^'\n]+)'?)?/i); - if (match) { - const index = match[1]; - const name = match[2].trim(); - const port = match[3] ? match[3].trim() : ""; - detected.push({ index, name, port }); - } - }); - - const unlistenExit = await listen("process:exit", (event) => { - if (event.payload.id !== "instlist") return; - unlistenStdout(); - unlistenExit(); - btnDetectInstruments.disabled = false; - btnDetectInstruments.textContent = "↻ Detect"; - - instrumentSelect.innerHTML = ``; - - if (detected.length > 0) { - detected.forEach((inst) => { - const opt = document.createElement("option"); - opt.value = inst.index; - opt.textContent = `${inst.index}: ${inst.name} ${inst.port ? `(${inst.port})` : ""}`; - instrumentSelect.appendChild(opt); - }); - instrumentSelect.value = detected[0].index; - setPrompt(`Found ${detected.length} instrument(s): ${detected.map(d => d.name).join(", ")}`); - } else { - setPrompt("No instruments found via instlist. Ensure USB cable is plugged in."); - } - }); - - await invoke("detect_instruments"); - } catch (err) { - console.error("detect_instruments error:", err); - btnDetectInstruments.disabled = false; - btnDetectInstruments.textContent = "↻ Detect"; - setPrompt(`Instrument detection error: ${err}`); - } - }); - } - - function setState(newState) { - currentState = newState; - if (stateLabel) stateLabel.textContent = newState; - - // Show/hide buttons based on state - btnCalibrate.classList.add("hidden"); - btnRetry.classList.add("hidden"); - btnSkip.classList.add("hidden"); - btnCancel.classList.add("hidden"); - btnStartRead.classList.add("hidden"); - - switch (newState) { - case STATE.IDLE: - btnStartRead.classList.remove("hidden"); - break; - case STATE.CALIBRATING: - btnCalibrate.classList.remove("hidden"); - btnCancel.classList.remove("hidden"); - break; - case STATE.AWAITING_STRIP: - btnRetry.classList.remove("hidden"); - btnSkip.classList.remove("hidden"); - btnCancel.classList.remove("hidden"); - break; - case STATE.READING: - btnCancel.classList.remove("hidden"); - break; - case STATE.ERROR: - btnRetry.classList.remove("hidden"); - btnSkip.classList.remove("hidden"); - btnCancel.classList.remove("hidden"); - break; - case STATE.FINISHED: - // After a recorded pass, further sheets go through "Measure Another Sheet" - if (recordedPasses.length === 0) { - btnStartRead.classList.remove("hidden"); - } - break; - } - } - - function setPrompt(text) { - if (promptText) promptText.textContent = text; - } - - // Start reading button - if (btnStartRead) { - btnStartRead.addEventListener("click", async () => { - if (measurementInProgress) { - setPrompt("A measurement is already running. Wait for it to finish."); - return; - } - - const basename = stage2Basename || wizardState.basename; - const cwd = stage2Cwd || wizardState.cwd; - - if (!basename || !cwd) { - setPrompt("Error: No .ti2 file available. Complete Stage 2 first."); - return; - } - - stage2Basename = basename; - stage2Cwd = cwd; - - logPre.textContent = ""; - logContainer.open = false; - logContainer.classList.remove("hidden"); - setMeasurementBusy(true); - setState(STATE.CALIBRATING); - setPrompt("Starting chartread... waiting for instrument calibration prompt."); - - const selectedPort = instrumentSelect && instrumentSelect.value ? instrumentSelect.value : null; - - const config = { - basename: basename, - cwd: cwd, - port: selectedPort, - }; - - currentProcessId = `chartread_${basename}`; - - // Start swatch grid listener - await startSwatchListener(currentProcessId); - - try { - const unlistenStdout = await listen("process:stdout", (event) => { - if (event.payload.id !== currentProcessId || !event.payload.line) return; - const line = event.payload.line; - - logPre.textContent += line + "\n"; - logPre.scrollTop = logPre.scrollHeight; - - // Parse prompts for state transitions - const lineLower = line.toLowerCase(); - - if (lineLower.includes("calibrat") && lineLower.includes("place")) { - setState(STATE.CALIBRATING); - setPrompt(line); - } else if (lineLower.includes("hit") && lineLower.includes("read") && lineLower.includes("strip")) { - setState(STATE.AWAITING_STRIP); - setPrompt(line); - } else if (lineLower.includes("ready to read")) { - setState(STATE.AWAITING_STRIP); - setPrompt(line); - } else if (lineLower.includes("reading strip") || lineLower.includes("processing")) { - setState(STATE.READING); - setPrompt(line); - } else if (lineLower.includes("error") || lineLower.includes("too fast") || lineLower.includes("too slow") || lineLower.includes("misread")) { - setState(STATE.ERROR); - setPrompt("⚠️ " + line); - } - }); - - const unlistenStderr = await listen("process:stderr", (event) => { - if (event.payload.id === currentProcessId && event.payload.line) { - logPre.textContent += "ERR: " + event.payload.line + "\n"; - logPre.scrollTop = logPre.scrollHeight; - } - }); - - const unlistenExit = await listen("process:exit", async (event) => { - if (event.payload.id !== currentProcessId) return; - unlistenStdout(); - unlistenStderr(); - unlistenExit(); - stopSwatchListener(); - - if (event.payload.code === 0) { - try { - const passIndex = currentPassIndex + 1; - const filename = await invoke("snapshot_ti3", { - cwd: cwd, - basename: basename, - pass_index: passIndex, - }); - - currentPassIndex = passIndex; - recordedPasses.push({ - index: currentPassIndex, - filename: filename, - time: new Date().toLocaleTimeString(), - }); - - setState(STATE.FINISHED); - setPrompt(`Sheet pass #${currentPassIndex} saved as ${filename}. Stage 4 stays locked until you finish.`); - logPre.textContent += `\n[SUCCESS] chartread completed. Snapshotted ${filename} (canonical ${basename}.ti3 removed until Finish).\n`; - - renderPassesList(); - if (averagingPanel) averagingPanel.classList.remove("hidden"); - if (passCounterBadge) passCounterBadge.textContent = `${recordedPasses.length} Pass(es) Recorded`; - // Do not call setStage3Result / wizardState.setTarget here (#110) - } catch (snapErr) { - setState(STATE.FINISHED); - setPrompt(`Measurement finished but pass snapshot failed: ${snapErr}`); - logPre.textContent += `\n[ERROR] snapshot_ti3 failed: ${snapErr}\n`; - } - } else { - setState(STATE.FINISHED); - setPrompt(`❌ chartread exited with code ${event.payload.code}.`); - logPre.textContent += `\n[ERROR] chartread exited with code ${event.payload.code}.\n`; - } - - setMeasurementBusy(false); - }); - - await invoke("run_chartread", { config }); - - } catch (err) { - setPrompt(`Invoke error: ${err}`); - setState(STATE.IDLE); - setMeasurementBusy(false); - } - }); - } - - if (btnMeasureAnotherSheet) { - btnMeasureAnotherSheet.addEventListener("click", () => { - if (measurementInProgress) { - setPrompt("Wait for the current measurement to finish and be snapshotted."); - return; - } - setState(STATE.IDLE); - setPrompt(`Ready to measure sheet pass #${recordedPasses.length + 1}. Starting chartread...`); - if (btnStartRead) btnStartRead.click(); - }); - } - - if (btnFinishAndAverage) { - btnFinishAndAverage.addEventListener("click", async () => { - const basename = stage2Basename || wizardState.basename; - const cwd = stage2Cwd || wizardState.cwd; - - if (!basename || !cwd) { - setPrompt("Error: No working directory or basename."); - return; - } - - if (measurementInProgress) { - setPrompt("Wait for the current measurement to finish."); - return; - } - - if (recordedPasses.length === 0) { - setPrompt("No measurement passes recorded yet."); - return; - } - - if (recordedPasses.length === 1) { - setPrompt("Single pass accepted. Restoring canonical .ti3 and proceeding to Stage 4..."); - btnFinishAndAverage.disabled = true; - try { - await invoke("promote_ti3", { - cwd: cwd, - source: recordedPasses[0].filename, - basename: basename, - }); - logPre.textContent += `\n[SUCCESS] Promoted ${recordedPasses[0].filename} → ${basename}.ti3 (single pass, average not invoked).\n`; - acceptStage3(basename, cwd); - } catch (e) { - btnFinishAndAverage.disabled = false; - setPrompt(`Failed to restore ${basename}.ti3: ${e}`); - } - return; - } - - setPrompt("Averaging measurement passes with Argyll average utility..."); - btnFinishAndAverage.disabled = true; - - try { - const averageConfig = { - inputs: recordedPasses.map(p => p.filename), - output: `${basename}.ti3`, - cwd: cwd, - }; - - logPre.textContent += `\n[average] average -v ${averageConfig.inputs.join(" ")} ${averageConfig.output}\n`; - - const unlistenAvgExit = await listen("process:exit", async (event) => { - if (event.payload.id !== `average_${basename}.ti3`) return; - unlistenAvgExit(); - - if (event.payload.code === 0) { - setPrompt(`✅ Successfully averaged ${recordedPasses.length} measurement passes into ${basename}.ti3!`); - logPre.textContent += `\n[SUCCESS] average wrote ${basename}.ti3\n`; - acceptStage3(basename, cwd); - } else { - setPrompt(`⚠️ Argyll average exited with code ${event.payload.code}. Promoting pass 1 as fallback.`); - logPre.textContent += `\n[ERROR] average exited ${event.payload.code}. Promoting ${recordedPasses[0].filename}.\n`; - try { - await invoke("promote_ti3", { - cwd: cwd, - source: recordedPasses[0].filename, - basename: basename, - }); - acceptStage3(basename, cwd); - } catch (e) { - btnFinishAndAverage.disabled = false; - setPrompt(`Average failed and fallback promote failed: ${e}`); - } - } - }); - - await invoke("run_average", { config: averageConfig }); - } catch (e) { - console.error("run_average error:", e); - btnFinishAndAverage.disabled = false; - setPrompt(`run_average error: ${e}`); - } - }); - } - - // Calibrate / confirm button — sends space + newline - if (btnCalibrate) { - btnCalibrate.addEventListener("click", async () => { - try { - await invoke("send_stdin", { id: currentProcessId, input: " \n" }); - } catch (e) { console.error("send_stdin error:", e); } - }); - } - - // Retry button — sends space + newline (same as confirm) - if (btnRetry) { - btnRetry.addEventListener("click", async () => { - try { - await invoke("send_stdin", { id: currentProcessId, input: " \n" }); - setState(STATE.READING); - setPrompt("Retrying strip read..."); - } catch (e) { console.error("send_stdin error:", e); } - }); - } - - // Skip button — sends "s\n" - if (btnSkip) { - btnSkip.addEventListener("click", async () => { - try { - await invoke("send_stdin", { id: currentProcessId, input: "s\n" }); - setState(STATE.AWAITING_STRIP); - setPrompt("Skipped current strip. Awaiting next..."); - } catch (e) { console.error("send_stdin error:", e); } - }); - } - - // Cancel button — kills the process - if (btnCancel) { - btnCancel.addEventListener("click", async () => { - try { - await invoke("kill_process", { id: currentProcessId }); - stopSwatchListener(); - setState(recordedPasses.length > 0 ? STATE.FINISHED : STATE.IDLE); - setPrompt("Measurement cancelled."); - } catch (e) { - console.error("kill_process error:", e); - setPrompt(`Cancel failed: ${e}`); - } finally { - setMeasurementBusy(false); - stopSwatchListener(); - } - }); - } - - // Start in IDLE state - setState(STATE.IDLE); - setPrompt("Press 'Start Measurement' to begin reading the printed target."); -} - -function advanceToStage4() { - const steps = document.querySelectorAll('.step'); - const stages = document.querySelectorAll('.stage'); - - steps.forEach(s => s.classList.remove('active')); - if (steps[3]) { - steps[3].classList.add('active'); - steps[3].classList.remove('disabled'); - } - - stages.forEach(s => { - s.classList.remove('active'); - s.classList.add('hidden'); - }); - if (stages[3]) { - stages[3].classList.remove('hidden'); - stages[3].classList.add('active'); - } -} +Y29uc3QgeyBpbnZva2UgfSA9IHdpbmRvdy5fX1RBVVJJX18uY29yZTsNCmNvbnN0IHsgbGlzdGVuIH0gPSB3aW5kb3cuX19UQVVSSV9fLmV2ZW50Ow0KaW1wb3J0IHsgc3RhcnRTd2F0Y2hMaXN0ZW5lciwgc3RvcFN3YXRjaExpc3RlbmVyIH0gZnJvbSAnLi9zd2F0Y2hfZ3JpZC5qcyc7DQppbXBvcnQgeyBzZXRTdGFnZTNSZXN1bHQgfSBmcm9tICcuL2NvbHByb2YuanMnOw0KaW1wb3J0IHsgd2l6YXJkU3RhdGUgfSBmcm9tICcuL3N0YXRlLmpzJzsNCg0KLy8gTW9kdWxlLWxldmVsIHN0YXRlOiBzZXQgYnkgU3RhZ2UgMiB3aGVuIGl0IGNvbXBsZXRlcw0KbGV0IHN0YWdlMkJhc2VuYW1lID0gIiI7DQpsZXQgc3RhZ2UyQ3dkID0gIiI7DQoNCi8qKg0KICogQ2FsbGVkIGJ5IHByaW50dGFyZy5qcyBhZnRlciBTdGFnZSAyIGNvbXBsZXRlcy4NCiAqLw0KZXhwb3J0IGZ1bmN0aW9uIHNldFN0YWdlMlJlc3VsdChiYXNlbmFtZSwgY3dkKSB7DQogIHN0YWdlMkJhc2VuYW1lID0gYmFzZW5hbWUgfHwgd2l6YXJkU3RhdGUuYmFzZW5hbWU7DQogIHN0YWdlMkN3ZCA9IGN3ZCB8fCB3aXphcmRTdGF0ZS5jd2Q7DQogIHdpemFyZFN0YXRlLnNldFRhcmdldChzdGFnZTJCYXNlbmFtZSwgc3RhZ2UyQ3dkKTsNCn0NCg0KLy8gU3RhdGUgbWFjaGluZSBzdGF0ZXMNCmNvbnN0IFNUQVRFID0gew0KICBJRExFOiAiSURMRSIsDQogIENBTElCUkFUSU5HOiAiQ0FMSUJSQVRJTkciLA0KICBBV0FJVElOR19TVFJJUDogIkFXQUlUSU5HX1NUUklQIiwNCiAgUkVBRElORzogIlJFQURJTkciLAogIEVSUk9SOiAiRVJST1IiLAogIEZJTklTSEVEOiAiRklOSVNIRUQiLAp9Ow0KDQpsZXQgY3VycmVudFN0YXRlID0gU1RBVEUuSURMRTsNCmxldCBjdXJyZW50UHJvY2Vzc0lkID0gIiI7DQpsZXQgbWVhc3VyZW1lbnRJblByb2dyZXNzID0gZmFsc2U7DQpsZXQgY3VycmVudFBhc3NJbmRleCA9IDA7DQpjb25zdCByZWNvcmRlZFBhc3NlcyA9IFtdOw0KDQpleHBvcnQgZnVuY3Rpb24gaW5pdENoYXJ0cmVhZCgpIHsNCiAgY29uc3QgYnRuU3RhcnRSZWFkID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImJ0blN0YXJ0UmVhZCIpOw0KICBjb25zdCBidG5DYWxpYnJhdGUgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiYnRuQ2FsaWJyYXRlIik7DQogIGNvbnN0IGJ0blJldHJ5ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImJ0blJldHJ5Iik7DQogIGNvbnN0IGJ0blNraXAgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiYnRuU2tpcCIpOw0KICBjb25zdCBidG5DYW5jZWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiYnRuQ2FuY2VsIik7DQogIGNvbnN0IGJ0bkRldGVjdEluc3RydW1lbnRzID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImJ0bkRldGVjdEluc3RydW1lbnRzIik7DQogIGNvbnN0IGluc3RydW1lbnRTZWxlY3QgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiY2hhcnRyZWFkSW5zdHJ1bWVudFNlbGVjdCIpOw0KICBjb25zdCBwcm9tcHRUZXh0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImNoYXJ0cmVhZFByb21wdCIpOw0KICBjb25zdCBzdGF0ZUxhYmVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImNoYXJ0cmVhZFN0YXRlIik7DQogIGNvbnN0IGxvZ0NvbnRhaW5lciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJjaGFydHJlYWRMb2dDb250YWluZXIiKTsNCiAgY29uc3QgbG9nUHJlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImNoYXJ0cmVhZExvZyIpOw0KICBjb25zdCBhdmVyYWdpbmdQYW5lbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJjaGFydHJlYWRBdmVyYWdpbmdQYW5lbCIpOw0KICBjb25zdCBwYXNzZXNMaXN0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInBhc3Nlc0xpc3QiKTsNCiAgY29uc3QgcGFzc0NvdW50ZXJCYWRnZSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJwYXNzQ291bnRlckJhZGdlIik7DQogIGNvbnN0IGJ0bk1lYXN1cmVBbm90aGVyU2hlZXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiYnRuTWVhc3VyZUFub3RoZXJTaGVldCIpOw0KICBjb25zdCBidG5GaW5pc2hBbmRBdmVyYWdlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImJ0bkZpbmlzaEFuZEF2ZXJhZ2UiKTsNCg0KICBmdW5jdGlvbiBzZXRNZWFzdXJlbWVudEJ1c3koYnVzeSkgew0KICAgIG1lYXN1cmVtZW50SW5Qcm9ncmVzcyA9IGJ1c3k7DQogICAgaWYgKGJ0blN0YXJ0UmVhZCkgYnRuU3RhcnRSZWFkLmRpc2FibGVkID0gYnVzeTsNCiAgICBpZiAoYnRuTWVhc3VyZUFub3RoZXJTaGVldCkgYnRuTWVhc3VyZUFub3RoZXJTaGVldC5kaXNhYmxlZCA9IGJ1c3k7DQogICAgaWYgKGJ0bkZpbmlzaEFuZEF2ZXJhZ2UpIHsNCiAgICAgIGJ0bkZpbmlzaEFuZEF2ZXJhZ2UuZGlzYWJsZWQgPSBidXN5IHx8IHJlY29yZGVkUGFzc2VzLmxlbmd0aCA9PT0gMDsNCiAgICB9DQogIH0NCg0KICBmdW5jdGlvbiByZW5kZXJQYXNzZXNMaXN0KCkgew0KICAgIGlmICghcGFzc2VzTGlzdCkgcmV0dXJuOw0KICAgIHBhc3Nlc0xpc3QuaW5uZXJIVE1MID0gIiI7DQogICAgcmVjb3JkZWRQYXNzZXMuZm9yRWFjaCgocGFzcywgaSkgPT4gew0KICAgICAgY29uc3QgaXRlbSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImRpdiIpOw0KICAgICAgaXRlbS5zdHlsZS5jc3NUZXh0ID0gImRpc3BsYXk6ZmxleDsganVzdGlmeS1jb250ZW50OnNwYWNlLWJldHdlZW47IGFsaWduLWl0ZW1zOmNlbnRlcjsgYmFja2dyb3VuZDpyZ2JhKDI1NSwyNTUsMjU1LDAuMDUpOyBwYWRkaW5nOjZweCAxMHB4OyBib3JkZXItcmFkaXVzOjRweDsgZm9udC1zaXplOjAuODVyZW07IjsNCiAgICAgIGNvbnN0IGxhYmVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3BhbiIpOw0KICAgICAgY29uc3Qgc3Ryb25nID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3Ryb25nIik7DQogICAgICBzdHJvbmcudGV4dENvbnRlbnQgPSBgU2hlZXQgUGFzcyAjJHtpICsgMX1gOw0KICAgICAgbGFiZWwuYXBwZW5kQ2hpbGQoc3Ryb25nKTsNCiAgICAgIGxhYmVsLmFwcGVuZENoaWxkKGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKGAgKCR7cGFzcy5maWxlbmFtZX0pYCkpOw0KICAgICAgY29uc3QgdGltZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoInNwYW4iKTsNCiAgICAgIHRpbWUuc3R5bGUub3BhY2l0eSA9ICIwLjciOw0KICAgICAgdGltZS50ZXh0Q29udGVudCA9IGDinJMgJHtwYXNzLnRpbWV9YDsNCiAgICAgIGl0ZW0uYXBwZW5kQ2hpbGQobGFiZWwpOw0KICAgICAgaXRlbS5hcHBlbmRDaGlsZCh0aW1lKTsNCiAgICAgIHBhc3Nlc0xpc3QuYXBwZW5kQ2hpbGQoaXRlbSk7DQogICAgfSk7DQogIH0NCg0KICBmdW5jdGlvbiBhY2NlcHRTdGFnZTMoYmFzZW5hbWUsIGN3ZCkgew0KICAgIHdpemFyZFN0YXRlLnNldFRhcmdldChiYXNlbmFtZSwgY3dkKTsNCiAgICBzZXRTdGFnZTNSZXN1bHQoYmFzZW5hbWUsIGN3ZCk7DQogICAgd2l6YXJkU3RhdGUudXBkYXRlR2F0aW5nKCk7DQogICAgYWR2YW5jZVRvU3RhZ2U0KCk7DQogIH0NCg0KICAvLyBJbnN0cnVtZW50IGRldGVjdGlvbiBsb2dpYw0KICBpZiAoYnRuRGV0ZWN0SW5zdHJ1bWVudHMgJiYgaW5zdHJ1bWVudFNlbGVjdCkgew0KICAgIGJ0bkRldGVjdEluc3RydW1lbnRzLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgYXN5bmMgKCkgPT4gew0KICAgICAgYnRuRGV0ZWN0SW5zdHJ1bWVudHMuZGlzYWJsZWQgPSB0cnVlOw0KICAgICAgYnRuRGV0ZWN0SW5zdHJ1bWVudHMudGV4dENvbnRlbnQgPSAiRGV0ZWN0aW5nLi4uIjsNCiAgICAgIHNldFByb21wdCgiUXVlcnlpbmcgY29ubmVjdGVkIHNwZWN0cm9waG90b21ldGVycyBhbmQgY29sb3JpbWV0ZXJzIHZpYSBpbnN0bGlzdC4uLiIpOw0KDQogICAgICBjb25zdCBkZXRlY3RlZCA9IFtdOw0KDQogICAgICB0cnkgew0KICAgICAgICBjb25zdCB1bmxpc3RlblN0ZG91dCA9IGF3YWl0IGxpc3RlbigicHJvY2VzczpzdGRvdXQiLCAoZXZlbnQpID0+IHsNCiAgICAgICAgICBpZiAoZXZlbnQucGF5bG9hZC5pZCAhPT0gImluc3RsaXN0IiB8fCAhZXZlbnQucGF5bG9hZC5saW5lKSByZXR1cm47DQogICAgICAgICAgY29uc3QgbGluZSA9IGV2ZW50LnBheWxvYWQubGluZS50cmltKCk7DQoNCiAgICAgICAgICAvLyBNYXRjaGVzIEFyZ3lsbCBpbnN0bGlzdCBvdXRwdXQgZS5nLiAiMTogJ2kxUHJvJyBvbiAnVVNCJyIgb3IgIjE6ICdDb2xvck11bmtpJyIgb3IgIjEgPSAnaTFEaXNwbGF5JyINCiAgICAgICAgICBjb25zdCBLTk9XTl9JTlNUX1RPS0VOUyA9IC9pMXxDb2xvck11bmtpfFNweWRlcnxzcGVjdHJvfERpc3BsYXl8SHVleXxEVFB8U3BlY3Ryb1NjYW58U21pbGV8S2xlaW4vaTsNCiAgICAgICAgICBjb25zdCBtYXRjaCA9IGxpbmUubWF0Y2goL14oXGQrKVtcczo9XSsnPyhbXidcbl0rKSc/KD86XHMrb25ccysnPyhbXidcbl0rKSc/KT8vaSk7DQogICAgICAgICAgaWYgKG1hdGNoKSB7DQogICAgICAgICAgICBjb25zdCBpbmRleCA9IG1hdGNoWzFdOw0KICAgICAgICAgICAgY29uc3QgbmFtZSA9IG1hdGNoWzJdLnRyaW0oKTsNCiAgICAgICAgICAgIGNvbnN0IHBvcnQgPSBtYXRjaFszXSA/IG1hdGNoWzNdLnRyaW0oKSA6ICIiOw0KICAgICAgICAgICAgaWYgKEtOT1dOX0lOU1RfVE9LRU5TLnRlc3QobmFtZSkgfHwgcG9ydC5sZW5ndGggPiAwKSB7DQogICAgICAgICAgICAgIGRldGVjdGVkLnB1c2goeyBpbmRleCwgbmFtZSwgcG9ydCB9KTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgICB9DQogICAgICAgIH0pOw0KDQogICAgICAgIGNvbnN0IHVubGlzdGVuRXhpdCA9IGF3YWl0IGxpc3RlbigicHJvY2VzczpleGl0IiwgKGV2ZW50KSA9PiB7DQogICAgICAgICAgaWYgKGV2ZW50LnBheWxvYWQuaWQgIT09ICJpbnN0bGlzdCIpIHJldHVybjsNCiAgICAgICAgICB1bmxpc3RlblN0ZG91dCgpOw0KICAgICAgICAgIHVubGlzdGVuRXhpdCgpOw0KICAgICAgICAgIGJ0bkRldGVjdEluc3RydW1lbnRzLmRpc2FibGVkID0gZmFsc2U7DQogICAgICAgICAgYnRuRGV0ZWN0SW5zdHJ1bWVudHMudGV4dENvbnRlbnQgPSAi4oa6IERldGVjdCI7DQoNCiAgICAgICAgICBpbnN0cnVtZW50U2VsZWN0LmlubmVySFRNTCA9IGA8b3B0aW9uIHZhbHVlPSIiPkF1dG8gKEZpcnN0IGF2YWlsYWJsZSBwb3J0KTwvb3B0aW9uPmA7DQoNCiAgICAgICAgICBpZiAoZGV0ZWN0ZWQubGVuZ3RoID4gMCkgew0KICAgICAgICAgICAgZGV0ZWN0ZWQuZm9yRWFjaCgoaW5zdCkgPT4gew0KICAgICAgICAgICAgICBjb25zdCBvcHQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJvcHRpb24iKTsNCiAgICAgICAgICAgICAgLy8gRG8gbm90IHBhc3MgaW5zdGxpc3Qgb3JkaW5hbCBhcyAtYyBjb21tIHBvcnQ7IHVzZSBlbXB0eSB2YWx1ZSBmb3IgYXV0by1wb3J0ICgjMTExKQ0KICAgICAgICAgICAgICBvcHQudmFsdWUgPSAiIjsNCiAgICAgICAgICAgICAgb3B0LnRleHRDb250ZW50ID0gYCR7aW5zdC5uYW1lfSR7aW5zdC5wb3J0ID8gYCBvbiAke2luc3QucG9ydH1gIDogIiJ9YDsNCiAgICAgICAgICAgICAgaW5zdHJ1bWVudFNlbGVjdC5hcHBlbmRDaGlsZChvcHQpOw0KICAgICAgICAgICAgfSk7DQogICAgICAgICAgICBpbnN0cnVtZW50U2VsZWN0LnZhbHVlID0gIiI7DQogICAgICAgICAgICBzZXRQcm9tcHQoYEZvdW5kICR7ZGV0ZWN0ZWQubGVuZ3RofSBpbnN0cnVtZW50KHMpOiAke2RldGVjdGVkLm1hcChkID0+IGQubmFtZSkuam9pbigiLCAiKX1gKTsNCiAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgc2V0UHJvbXB0KCJObyBpbnN0cnVtZW50cyBmb3VuZCB2aWEgaW5zdGxpc3QuIEVuc3VyZSBVU0IgY2FibGUgaXMgcGx1Z2dlZCBpbi4iKTsNCiAgICAgICAgICB9DQogICAgICAgIH0pOw0KDQogICAgICAgIGF3YWl0IGludm9rZSgiZGV0ZWN0X2luc3RydW1lbnRzIik7DQogICAgICB9IGNhdGNoIChlcnIpIHsNCiAgICAgICAgY29uc29sZS5lcnJvcigiZGV0ZWN0X2luc3RydW1lbnRzIGVycm9yOiIsIGVycik7DQogICAgICAgIGJ0bkRldGVjdEluc3RydW1lbnRzLmRpc2FibGVkID0gZmFsc2U7DQogICAgICAgIGJ0bkRldGVjdEluc3RydW1lbnRzLnRleHRDb250ZW50ID0gIuKGuyBEZXRlY3QiOw0KICAgICAgICBzZXRQcm9tcHQoYEluc3RydW1lbnQgZGV0ZWN0aW9uIGVycm9yOiAke2Vycn1gKTsNCiAgICAgIH0NCiAgICB9KTsNCiAgfQ0KDQogIGZ1bmN0aW9uIHNldFN0YXRlKG5ld1N0YXRlKSB7DQogICAgY3VycmVudFN0YXRlID0gbmV3U3RhdGU7DQogICAgaWYgKHN0YXRlTGFiZWwpIHN0YXRlTGFiZWwudGV4dENvbnRlbnQgPSBuZXdTdGF0ZTsNCg0KICAgIC8vIFNob3cvaGlkZSBidXR0b25zIGJhc2VkIG9uIHN0YXRlDQogICAgYnRuQ2FsaWJyYXRlLmNsYXNzTGlzdC5hZGQoImhpZGRlbiIpOw0KICAgIGJ0blJldHJ5LmNsYXNzTGlzdC5hZGQoImhpZGRlbiIpOw0KICAgIGJ0blNraXAuY2xhc3NMaXN0LmFkZCgiaGlkZGVuIik7DQogICAgYnRuQ2FuY2VsLmNsYXNzTGlzdC5hZGQoImhpZGRlbiIpOw0KICAgIGJ0blN0YXJ0UmVhZC5jbGFzc0xpc3QuYWRkKCJoaWRkZW4iKTsNCg0KICAgIHN3aXRjaCAobmV3U3RhdGUpIHsNCiAgICAgIGNhc2UgU1RBVEUuSURMRToNCiAgICAgICAgYnRuU3RhcnRSZWFkLmNsYXNzTGlzdC5yZW1vdmUoImhpZGRlbiIpOw0KICAgICAgICBicmVhazsNCiAgICAgIGNhc2UgU1RBVEUuQ0FMSUJSQVRJTkc6DQogICAgICAgIGJ0bkNhbGlicmF0ZS5jbGFzc0xpc3QucmVtb3ZlKCJoaWRkZW4iKTsNCiAgICAgICAgYnRuQ2FuY2VsLmNsYXNzTGlzdC5yZW1vdmUoImhpZGRlbiIpOw0KICAgICAgICBicmVhazsNCiAgICAgIGNhc2UgU1RBVEUuQVdBSVRJTkdfU1RSSVA6DQogICAgICAgIGJ0blJldHJ5LmNsYXNzTGlzdC5yZW1vdmUoImhpZGRlbiIpOw0KICAgICAgICBidG5Ta2lwLmNsYXNzTGlzdC5yZW1vdmUoImhpZGRlbiIpOw0KICAgICAgICBidG5DYW5jZWwuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZGVuIik7DQogICAgICAgIGJyZWFrOw0KICAgICAgY2FzZSBTVEFURS5SRUFESU5HOg0KICAgICAgICBidG5DYW5jZWwuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZGVuIik7DQogICAgICAgIGJyZWFrOw0KICAgICAgY2FzZSBTVEFURS5FUlJPUjoNCiAgICAgICAgYnRuUmV0cnkuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZGVuIik7DQogICAgICAgIGJ0blNraXAuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZGVuIik7DQogICAgICAgIGJ0bkNhbmNlbC5jbGFzc0xpc3QucmVtb3ZlKCJoaWRkZW4iKTsNCiAgICAgICAgYnJlYWs7DQogICAgICBjYXNlIFNUQVRFLkZJTklTSEVEOg0KICAgICAgICAvLyBBZnRlciBhIHJlY29yZGVkIHBhc3MsIGZ1cnRoZXIgc2hlZXRzIGdvIHRocm91Z2ggIk1lYXN1cmUgQW5vdGhlciBTaGVldCINCiAgICAgICAgaWYgKHJlY29yZGVkUGFzc2VzLmxlbmd0aCA9PT0gMCkgew0KICAgICAgICAgIGJ0blN0YXJ0UmVhZC5jbGFzc0xpc3QucmVtb3ZlKCJoaWRkZW4iKTsNCiAgICAgICAgfQ0KICAgICAgICBicmVhazsNCiAgICB9DQogIH0NCg0KICBmdW5jdGlvbiBzZXRQcm9tcHQodGV4dCkgew0KICAgIGlmIChwcm9tcHRUZXh0KSBwcm9tcHRUZXh0LnRleHRDb250ZW50ID0gdGV4dDsNCiAgfQ0KDQogIC8vIFN0YXJ0IHJlYWRpbmcgYnV0dG9uDQogIGlmIChidG5TdGFydFJlYWQpIHsNCiAgICBidG5TdGFydFJlYWQuYWRkRXZlbnRMaXN0ZW5lcigiY2xpY2siLCBhc3luYyAoKSA9PiB7DQogICAgICBpZiAobWVhc3VyZW1lbnRJblByb2dyZXNzKSB7DQogICAgICAgIHNldFByb21wdCgiQSBtZWFzdXJlbWVudCBpcyBhbHJlYWR5IHJ1bm5pbmcuIFdhaXQgZm9yIGl0IHRvIGZpbmlzaC4iKTsNCiAgICAgICAgcmV0dXJuOw0KICAgICAgfQ0KDQogICAgICBjb25zdCBiYXNlbmFtZSA9IHN0YWdlMkJhc2VuYW1lIHx8IHdpemFyZFN0YXRlLmJhc2VuYW1lOw0KICAgICAgY29uc3QgY3dkID0gc3RhZ2UyQ3dkIHx8IHdpemFyZFN0YXRlLmN3ZDsNCg0KICAgICAgaWYgKCFiYXNlbmFtZSB8fCAhY3dkKSB7DQogICAgICAgIHNldFByb21wdCgiRXJyb3I6IE5vIC50aTIgZmlsZSBhdmFpbGFibGUuIENvbXBsZXRlIFN0YWdlIDIgZmlyc3QuIik7DQogICAgICAgIHJldHVybjsNCiAgICAgIH0NCg0KICAgICAgc3RhZ2UyQmFzZW5hbWUgPSBiYXNlbmFtZTsNCiAgICAgIHN0YWdlMkN3ZCA9IGN3ZDsNCg0KICAgICAgbG9nUHJlLnRleHRDb250ZW50ID0gIiI7DQogICAgICBsb2dDb250YWluZXIub3BlbiA9IGZhbHNlOw0KICAgICAgbG9nQ29udGFpbmVyLmNsYXNzTGlzdC5yZW1vdmUoImhpZGRlbiIpOw0KICAgICAgc2V0TWVhc3VyZW1lbnRCdXN5KHRydWUpOw0KICAgICAgc2V0U3RhdGUoU1RBVEUuQ0FMSUJSQVRJTkcpOw0KICAgICAgc2V0UHJvbXB0KCJTdGFydGluZyBjaGFydHJlYWQuLi4gd2FpdGluZyBmb3IgaW5zdHJ1bWVudCBjYWxpYnJhdGlvbiBwcm9tcHQuIik7DQoNCiAgICAgIGNvbnN0IHNlbGVjdGVkUG9ydCA9IGluc3RydW1lbnRTZWxlY3QgJiYgaW5zdHJ1bWVudFNlbGVjdC52YWx1ZSA/IGluc3RydW1lbnRTZWxlY3QudmFsdWUgOiBudWxsOw0KDQogICAgICBjb25zdCBjb25maWcgPSB7DQogICAgICAgIGJhc2VuYW1lOiBiYXNlbmFtZSwNCiAgICAgICAgY3dkOiBjd2QsDQogICAgICAgIHBvcnQ6IHNlbGVjdGVkUG9ydCwNCiAgICAgIH07DQoNCiAgICAgIGN1cnJlbnRQcm9jZXNzSWQgPSBgY2hhcnRyZWFkXyR7YmFzZW5hbWV9YDsNCg0KICAgICAgLy8gU3RhcnQgc3dhdGNoIGdyaWQgbGlzdGVuZXINCiAgICAgIGF3YWl0IHN0YXJ0U3dhdGNoTGlzdGVuZXIoY3VycmVudFByb2Nlc3NJZCk7DQoNCiAgICAgIHRyeSB7DQogICAgICAgIGNvbnN0IHVubGlzdGVuU3Rkb3V0ID0gYXdhaXQgbGlzdGVuKCJwcm9jZXNzOnN0ZG91dCIsIChldmVudCkgPT4gew0KICAgICAgICAgIGlmIChldmVudC5wYXlsb2FkLmlkICE9PSBjdXJyZW50UHJvY2Vzc0lkIHx8ICFldmVudC5wYXlsb2FkLmxpbmUpIHJldHVybjsNCiAgICAgICAgICBjb25zdCBsaW5lID0gZXZlbnQucGF5bG9hZC5saW5lOw0KDQogICAgICAgICAgbG9nUHJlLnRleHRDb250ZW50ICs9IGxpbmUgKyAiXG4iOw0KICAgICAgICAgIGxvZ1ByZS5zY3JvbGxUb3AgPSBsb2dQcmUuc2Nyb2xsSGVpZ2h0Ow0KDQogICAgICAgICAgLy8gUGFyc2UgcHJvbXB0cyBmb3Igc3RhdGUgdHJhbnNpdGlvbnMNCiAgICAgICAgICBjb25zdCBsaW5lTG93ZXIgPSBsaW5lLnRvTG93ZXJDYXNlKCk7DQoNCiAgICAgICAgICBpZiAobGluZUxvd2VyLmluY2x1ZGVzKCJjYWxpYnJhdCIpICYmIGxpbmVMb3dlci5pbmNsdWRlcygicGxhY2UiKSkgew0KICAgICAgICAgICAgc2V0U3RhdGUoU1RBVEUuQ0FMSUJSQVRJTkcpOw0KICAgICAgICAgICAgc2V0UHJvbXB0KGxpbmUpOw0KICAgICAgICAgIH0gZWxzZSBpZiAobGluZUxvd2VyLmluY2x1ZGVzKCJoaXQiKSAmJiBsaW5lTG93ZXIuaW5jbHVkZXMoInJlYWQiKSAmJiBsaW5lTG93ZXIuaW5jbHVkZXMoInN0cmlwIikpIHsNCiAgICAgICAgICAgIHNldFN0YXRlKFNUQVRFLkFXQUlUSU5HX1NUUklQKTsNCiAgICAgICAgICAgIHNldFByb21wdChsaW5lKTsNCiAgICAgICAgICB9IGVsc2UgaWYgKGxpbmVMb3dlci5pbmNsdWRlcygicmVhZHkgdG8gcmVhZCIpKSB7DQogICAgICAgICAgICBzZXRTdGF0ZShTVEFURS5BV0FJVElOR19TVFJJUCk7DQogICAgICAgICAgICBzZXRQcm9tcHQobGluZSk7DQogICAgICAgICAgfSBlbHNlIGlmIChsaW5lTG93ZXIuaW5jbHVkZXMoInJlYWRpbmcgc3RyaXAiKSB8fCBsaW5lTG93ZXIuaW5jbHVkZXMoInByb2Nlc3NpbmciKSkgew0KICAgICAgICAgICAgc2V0U3RhdGUoU1RBVEUuUkVBRElORyk7DQogICAgICAgICAgICBzZXRQcm9tcHQobGluZSk7DQogICAgICAgICAgfSBlbHNlIGlmIChsaW5lTG93ZXIuaW5jbHVkZXMoImVycm9yIikgfHwgbGluZUxvd2VyLmluY2x1ZGVzKCJ0b28gZmFzdCIpIHx8IGxpbmVMb3dlci5pbmNsdWRlcygidG9vIHNsb3ciKSB8fCBsaW5lTG93ZXIuaW5jbHVkZXMoIm1pc3JlYWQiKSkgew0KICAgICAgICAgICAgc2V0U3RhdGUoU1RBVEUuRVJST1IpOw0KICAgICAgICAgICAgc2V0UHJvbXB0KCLimqDvuI8gIiArIGxpbmUpOw0KICAgICAgICAgIH0NCiAgICAgICAgfSk7DQoNCiAgICAgICAgY29uc3QgdW5saXN0ZW5TdGRlcnIgPSBhd2FpdCBsaXN0ZW4oInByb2Nlc3M6c3RkZXJyIiwgKGV2ZW50KSA9PiB7DQogICAgICAgICAgaWYgKGV2ZW50LnBheWxvYWQuaWQgPT09IGN1cnJlbnRQcm9jZXNzSWQgJiYgZXZlbnQucGF5bG9hZC5saW5lKSB7DQogICAgICAgICAgICBsb2dQcmUudGV4dENvbnRlbnQgKz0gIkVSUjogIiArIGV2ZW50LnBheWxvYWQubGluZSArICJcbiI7DQogICAgICAgICAgICBsb2dQcmUuc2Nyb2xsVG9wID0gbG9nUHJlLnNjcm9sbEhlaWdodDsNCiAgICAgICAgICB9DQogICAgICAgIH0pOw0KDQogICAgICAgIGNvbnN0IHVubGlzdGVuRXhpdCA9IGF3YWl0IGxpc3RlbigicHJvY2VzczpleGl0IiwgYXN5bmMgKGV2ZW50KSA9PiB7DQogICAgICAgICAgaWYgKGV2ZW50LnBheWxvYWQuaWQgIT09IGN1cnJlbnRQcm9jZXNzSWQpIHJldHVybjsNCiAgICAgICAgICB1bmxpc3RlblN0ZG91dCgpOw0KICAgICAgICAgIHVubGlzdGVuU3RkZXJyKCk7DQogICAgICAgICAgdW5saXN0ZW5FeGl0KCk7DQogICAgICAgICAgc3RvcFN3YXRjaExpc3RlbmVyKCk7DQoNCiAgICAgICAgICBpZiAoZXZlbnQucGF5bG9hZC5jb2RlID09PSAwKSB7DQogICAgICAgICAgICB0cnkgew0KICAgICAgICAgICAgICBjb25zdCBwYXNzSW5kZXggPSBjdXJyZW50UGFzc0luZGV4ICsgMTsNCiAgICAgICAgICAgICAgY29uc3QgZmlsZW5hbWUgPSBhd2FpdCBpbnZva2UoInNuYXBzaG90X3RpMyIsIHsNCiAgICAgICAgICAgICAgICBjd2Q6IGN3ZCwNCiAgICAgICAgICAgICAgICBiYXNlbmFtZTogYmFzZW5hbWUsDQogICAgICAgICAgICAgICAgcGFzc19pbmRleDogcGFzc0luZGV4LA0KICAgICAgICAgICAgICB9KTsNCg0KICAgICAgICAgICAgICBjdXJyZW50UGFzc0luZGV4ID0gcGFzc0luZGV4Ow0KICAgICAgICAgICAgICByZWNvcmRlZFBhc3Nlcy5wdXNoKHsNCiAgICAgICAgICAgICAgICBpbmRleDogY3VycmVudFBhc3NJbmRleCwNCiAgICAgICAgICAgICAgICBmaWxlbmFtZTogZmlsZW5hbWUsDQogICAgICAgICAgICAgICAgdGltZTogbmV3IERhdGUoKS50b0xvY2FsZVRpbWVTdHJpbmcoKSwNCiAgICAgICAgICAgICAgfSk7DQoNCiAgICAgICAgICAgICAgc2V0U3RhdGUoU1RBVEUuRklOSVNIRUQpOw0KICAgICAgICAgICAgICBzZXRQcm9tcHQoYFNoZWV0IHBhc3MgIyR7Y3VycmVudFBhc3NJbmRleH0gc2F2ZWQgYXMgJHtmaWxlbmFtZX0uIFN0YWdlIDQgc3RheXMgbG9ja2VkIHVudGlsIHlvdSBmaW5pc2guYCk7DQogICAgICAgICAgICAgIGxvZ1ByZS50ZXh0Q29udGVudCArPSBgXG5bU1VDQ0VTU10gY2hhcnRyZWFkIGNvbXBsZXRlZC4gU25hcHNob3R0ZWQgJHtmaWxlbmFtZX0gKGNhbm9uaWNhbCAke2Jhc2VuYW1lfS50aTMgcmVtb3ZlZCB1bnRpbCBGaW5pc2gpLlxuYDsNCg0KICAgICAgICAgICAgICByZW5kZXJQYXNzZXNMaXN0KCk7DQogICAgICAgICAgICAgIGlmIChhdmVyYWdpbmdQYW5lbCkgYXZlcmFnaW5nUGFuZWwuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZGVuIik7DQogICAgICAgICAgICAgIGlmIChwYXNzQ291bnRlckJhZGdlKSBwYXNzQ291bnRlckJhZGdlLnRleHRDb250ZW50ID0gYCR7cmVjb3JkZWRQYXNzZXMubGVuZ3RofSBQYXNzKGVzKSBSZWNvcmRlZGA7DQogICAgICAgICAgICAgIC8vIERvIG5vdCBjYWxsIHNldFN0YWdlM1Jlc3VsdCAvIHdpemFyZFN0YXRlLnNldFRhcmdldCBoZXJlICgjMTEwKQ0KICAgICAgICAgICAgfSBjYXRjaCAoc25hcEVycikgew0KICAgICAgICAgICAgICBzZXRTdGF0ZShTVEFURS5GSU5JU0hFRCk7DQogICAgICAgICAgICAgIHNldFByb21wdChgTWVhc3VyZW1lbnQgZmluaXNoZWQgYnV0IHBhc3Mgc25hcHNob3QgZmFpbGVkOiAke3NuYXBFcnJ9YCk7DQogICAgICAgICAgICAgIGxvZ1ByZS50ZXh0Q29udGVudCArPSBgXG5bRVJST1JdIHNuYXBzaG90X3RpMyBmYWlsZWQ6ICR7c25hcEVycn1cbmA7DQogICAgICAgICAgICB9DQogICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgIHNldFN0YXRlKFNUQVRFLkZJTklTSEVEKTsNCiAgICAgICAgICAgIHNldFByb21wdChg4p2MIGNoYXJ0cmVhZCBleGl0ZWQgd2l0aCBjb2RlICR7ZXZlbnQucGF5bG9hZC5jb2RlfS5gKTsNCiAgICAgICAgICAgIGxvZ1ByZS50ZXh0Q29udGVudCArPSBgXG5bRVJST1JdIGNoYXJ0cmVhZCBleGl0ZWQgd2l0aCBjb2RlICR7ZXZlbnQucGF5bG9hZC5jb2RlfS5cbmA7DQogICAgICAgICAgfQ0KDQogICAgICAgICAgc2V0TWVhc3VyZW1lbnRCdXN5KGZhbHNlKTsNCiAgICAgICAgfSk7DQoNCiAgICAgICAgYXdhaXQgaW52b2tlKCJydW5fY2hhcnRyZWFkIiwgeyBjb25maWcgfSk7DQoNCiAgICAgIH0gY2F0Y2ggKGVycikgew0KICAgICAgICBzZXRQcm9tcHQoYEludm9rZSBlcnJvcjogJHtlcnJ9YCk7DQogICAgICAgIHNldFN0YXRlKFNUQVRFLklETEUpOw0KICAgICAgICBzZXRNZWFzdXJlbWVudEJ1c3koZmFsc2UpOw0KICAgICAgfQ0KICAgIH0pOw0KICB9DQoNCiAgaWYgKGJ0bk1lYXN1cmVBbm90aGVyU2hlZXQpIHsNCiAgICBidG5NZWFzdXJlQW5vdGhlclNoZWV0LmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgKCkgPT4gew0KICAgICAgaWYgKG1lYXN1cmVtZW50SW5Qcm9ncmVzcykgew0KICAgICAgICBzZXRQcm9tcHQoIldhaXQgZm9yIHRoZSBjdXJyZW50IG1lYXN1cmVtZW50IHRvIGZpbmlzaCBhbmQgYmUgc25hcHNob3R0ZWQuIik7DQogICAgICAgIHJldHVybjsNCiAgICAgIH0NCiAgICAgIHNldFN0YXRlKFNUQVRFLklETEUpOw0KICAgICAgc2V0UHJvbXB0KGBSZWFkeSB0byBtZWFzdXJlIHNoZWV0IHBhc3MgIyR7cmVjb3JkZWRQYXNzZXMubGVuZ3RoICsgMX0uIFN0YXJ0aW5nIGNoYXJ0cmVhZC4uLmApOw0KICAgICAgaWYgKGJ0blN0YXJ0UmVhZCkgYnRuU3RhcnRSZWFkLmNsaWNrKCk7DQogICAgfSk7DQogIH0NCg0KICBpZiAoYnRuRmluaXNoQW5kQXZlcmFnZSkgew0KICAgIGJ0bkZpbmlzaEFuZEF2ZXJhZ2UuYWRkRXZlbnRMaXN0ZW5lcigiY2xpY2siLCBhc3luYyAoKSA9PiB7DQogICAgICBjb25zdCBiYXNlbmFtZSA9IHN0YWdlMkJhc2VuYW1lIHx8IHdpemFyZFN0YXRlLmJhc2VuYW1lOw0KICAgICAgY29uc3QgY3dkID0gc3RhZ2UyQ3dkIHx8IHdpemFyZFN0YXRlLmN3ZDsNCg0KICAgICAgaWYgKCFiYXNlbmFtZSB8fCAhY3dkKSB7DQogICAgICAgIHNldFByb21wdCgiRXJyb3I6IE5vIHdvcmtpbmcgZGlyZWN0b3J5IG9yIGJhc2VuYW1lLiIpOw0KICAgICAgICByZXR1cm47DQogICAgICB9DQoNCiAgICAgIGlmIChtZWFzdXJlbWVudEluUHJvZ3Jlc3MpIHsNCiAgICAgICAgc2V0UHJvbXB0KCJXYWl0IGZvciB0aGUgY3VycmVudCBtZWFzdXJlbWVudCB0byBmaW5pc2guIik7DQogICAgICAgIHJldHVybjsNCiAgICAgIH0NCg0KICAgICAgaWYgKHJlY29yZGVkUGFzc2VzLmxlbmd0aCA9PT0gMCkgew0KICAgICAgICBzZXRQcm9tcHQoIk5vIG1lYXN1cmVtZW50IHBhc3NlcyByZWNvcmRlZCB5ZXQuIik7DQogICAgICAgIHJldHVybjsNCiAgICAgIH0NCg0KICAgICAgaWYgKHJlY29yZGVkUGFzc2VzLmxlbmd0aCA9PT0gMSkgew0KICAgICAgICBzZXRQcm9tcHQoIlNpbmdsZSBwYXNzIGFjY2VwdGVkLiBSZXN0b3JpbmcgY2Fub25pY2FsIC50aTMgYW5kIHByb2NlZWRpbmcgdG8gU3RhZ2UgNC4uLiIpOw0KICAgICAgICBidG5GaW5pc2hBbmRBdmVyYWdlLmRpc2FibGVkID0gdHJ1ZTsNCiAgICAgICAgdHJ5IHsNCiAgICAgICAgICBhd2FpdCBpbnZva2UoInByb21vdGVfdGkzIiwgew0KICAgICAgICAgICAgY3dkOiBjd2QsDQogICAgICAgICAgICBzb3VyY2U6IHJlY29yZGVkUGFzc2VzWzBdLmZpbGVuYW1lLA0KICAgICAgICAgICAgYmFzZW5hbWU6IGJhc2VuYW1lLA0KICAgICAgICAgIH0pOw0KICAgICAgICAgIGxvZ1ByZS50ZXh0Q29udGVudCArPSBgXG5bU1VDQ0VTU10gUHJvbW90ZWQgJHtyZWNvcmRlZFBhc3Nlc1swXS5maWxlbmFtZX0g4oaSICR7YmFzZW5hbWV9LnRpMyAoc2luZ2xlIHBhc3MsIGF2ZXJhZ2Ugbm90IGludm9rZWQpLlxuYDsNCiAgICAgICAgICBhY2NlcHRTdGFnZTMoYmFzZW5hbWUsIGN3ZCk7DQogICAgICAgIH0gY2F0Y2ggKGUpIHsNCiAgICAgICAgICBidG5GaW5pc2hBbmRBdmVyYWdlLmRpc2FibGVkID0gZmFsc2U7DQogICAgICAgICAgc2V0UHJvbXB0KGBGYWlsZWQgdG8gcmVzdG9yZSAke2Jhc2VuYW1lfS50aTM6ICR7ZX1gKTsNCiAgICAgICAgfQ0KICAgICAgICByZXR1cm47DQogICAgICB9DQoNCiAgICAgIHNldFByb21wdCgiQXZlcmFnaW5nIG1lYXN1cmVtZW50IHBhc3NlcyB3aXRoIEFyZ3lsbCBhdmVyYWdlIHV0aWxpdHkuLi4iKTsNCiAgICAgIGJ0bkZpbmlzaEFuZEF2ZXJhZ2UuZGlzYWJsZWQgPSB0cnVlOw0KDQogICAgICB0cnkgew0KICAgICAgICBjb25zdCBhdmVyYWdlQ29uZmlnID0gew0KICAgICAgICAgIGlucHV0czogcmVjb3JkZWRQYXNzZXMubWFwKHAgPT4gcC5maWxlbmFtZSksDQogICAgICAgICAgb3V0cHV0OiBgJHtiYXNlbmFtZX0udGkzYCwNCiAgICAgICAgICBjd2Q6IGN3ZCwNCiAgICAgICAgfTsNCg0KICAgICAgICBsb2dQcmUudGV4dENvbnRlbnQgKz0gYFxuW2F2ZXJhZ2VdIGF2ZXJhZ2UgLXYgJHthdmVyYWdlQ29uZmlnLmlucHV0cy5qb2luKCIgIil9ICR7YXZlcmFnZUNvbmZpZy5vdXRwdXR9XG5gOw0KDQogICAgICAgIGNvbnN0IHVubGlzdGVuQXZnRXhpdCA9IGF3YWl0IGxpc3RlbigicHJvY2VzczpleGl0IiwgYXN5bmMgKGV2ZW50KSA9PiB7DQogICAgICAgICAgaWYgKGV2ZW50LnBheWxvYWQuaWQgIT09IGBhdmVyYWdlXyR7YmFzZW5hbWV9LnRpM2ApIHJldHVybjsNCiAgICAgICAgICB1bmxpc3RlbkF2Z0V4aXQoKTsNCg0KICAgICAgICAgIGlmIChldmVudC5wYXlsb2FkLmNvZGUgPT09IDApIHsNCiAgICAgICAgICAgIHNldFByb21wdChg4pyFIFN1Y2Nlc3NmdWxseSBhdmVyYWdlZCAke3JlY29yZGVkUGFzc2VzLmxlbmd0aH0gbWVhc3VyZW1lbnQgcGFzc2VzIGludG8gJHtiYXNlbmFtZX0udGkzIWApOw0KICAgICAgICAgICAgbG9nUHJlLnRleHRDb250ZW50ICs9IGBcbltTVUNDRVNTXSBhdmVyYWdlIHdyb3RlICR7YmFzZW5hbWV9LnRpM1xuYDsNCiAgICAgICAgICAgIGFjY2VwdFN0YWdlMyhiYXNlbmFtZSwgY3dkKTsNCiAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgc2V0UHJvbXB0KGDimqDvuI8gQXJneWxsIGF2ZXJhZ2UgZXhpdGVkIHdpdGggY29kZSAke2V2ZW50LnBheWxvYWQuY29kZX0uIFByb21vdGluZyBwYXNzIDEgYXMgZmFsbGJhY2suYCk7DQogICAgICAgICAgICBsb2dQcmUudGV4dENvbnRlbnQgKz0gYFxuW0VSUk9SXSBhdmVyYWdlIGV4aXRlZCAke2V2ZW50LnBheWxvYWQuY29kZX0uIFByb21vdGluZyAke3JlY29yZGVkUGFzc2VzWzBdLmZpbGVuYW1lfS5cbmA7DQogICAgICAgICAgICB0cnkgew0KICAgICAgICAgICAgICBhd2FpdCBpbnZva2UoInByb21vdGVfdGkzIiwgew0KICAgICAgICAgICAgICAgIGN3ZDogY3dkLA0KICAgICAgICAgICAgICAgIHNvdXJjZTogcmVjb3JkZWRQYXNzZXNbMF0uZmlsZW5hbWUsDQogICAgICAgICAgICAgICAgYmFzZW5hbWU6IGJhc2VuYW1lLA0KICAgICAgICAgICAgICB9KTsNCiAgICAgICAgICAgICAgYWNjZXB0U3RhZ2UzKGJhc2VuYW1lLCBjd2QpOw0KICAgICAgICAgICAgfSBjYXRjaCAoZSkgew0KICAgICAgICAgICAgICBidG5GaW5pc2hBbmRBdmVyYWdlLmRpc2FibGVkID0gZmFsc2U7DQogICAgICAgICAgICAgIHNldFByb21wdChgQXZlcmFnZSBmYWlsZWQgYW5kIGZhbGxiYWNrIHByb21vdGUgZmFpbGVkOiAke2V9YCk7DQogICAgICAgICAgICB9DQogICAgICAgICAgfQ0KICAgICAgICB9KTsNCg0KICAgICAgICBhd2FpdCBpbnZva2UoInJ1bl9hdmVyYWdlIiwgeyBjb25maWc6IGF2ZXJhZ2VDb25maWcgfSk7DQogICAgICB9IGNhdGNoIChlKSB7DQogICAgICAgIGNvbnNvbGUuZXJyb3IoInJ1bl9hdmVyYWdlIGVycm9yOiIsIGUpOw0KICAgICAgICBidG5GaW5pc2hBbmRBdmVyYWdlLmRpc2FibGVkID0gZmFsc2U7DQogICAgICAgIHNldFByb21wdChgcnVuX2F2ZXJhZ2UgZXJyb3I6ICR7ZX1gKTsNCiAgICAgIH0NCiAgICB9KTsNCiAgfQ0KDQogIC8vIENhbGlicmF0ZSAvIGNvbmZpcm0gYnV0dG9uIOKAlCBzZW5kcyBzcGFjZSArIG5ld2xpbmUNCiAgaWYgKGJ0bkNhbGlicmF0ZSkgew0KICAgIGJ0bkNhbGlicmF0ZS5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsIGFzeW5jICgpID0+IHsNCiAgICAgIHRyeSB7DQogICAgICAgIGF3YWl0IGludm9rZSgic2VuZF9zdGRpbiIsIHsgaWQ6IGN1cnJlbnRQcm9jZXNzSWQsIGlucHV0OiAiIFxuIiB9KTsNCiAgICAgIH0gY2F0Y2ggKGUpIHsgY29uc29sZS5lcnJvcigic2VuZF9zdGRpbiBlcnJvcjoiLCBlKTsgfQ0KICAgIH0pOw0KICB9DQoNCiAgLy8gUmV0cnkgYnV0dG9uIOKAlCBzZW5kcyBzcGFjZSArIG5ld2xpbmUgKHNhbWUgYXMgY29uZmlybSkNCiAgaWYgKGJ0blJldHJ5KSB7DQogICAgYnRuUmV0cnkuYWRkRXZlbnRMaXN0ZW5lcigiY2xpY2siLCBhc3luYyAoKSA9PiB7DQogICAgICB0cnkgew0KICAgICAgICBhd2FpdCBpbnZva2UoInNlbmRfc3RkaW4iLCB7IGlkOiBjdXJyZW50UHJvY2Vzc0lkLCBpbnB1dDogIiBcbiIgfSk7DQogICAgICAgIHNldFN0YXRlKFNUQVRFLlJFQURJTkcpOw0KICAgICAgICBzZXRQcm9tcHQoIlJldHJ5aW5nIHN0cmlwIHJlYWQuLi4iKTsNCiAgICAgIH0gY2F0Y2ggKGUpIHsgY29uc29sZS5lcnJvcigic2VuZF9zdGRpbiBlcnJvcjoiLCBlKTsgfQ0KICAgIH0pOw0KICB9DQoNCiAgLy8gU2tpcCBidXR0b24g4oCUIHNlbmRzICJzXG4iDQogIGlmIChidG5Ta2lwKSB7DQogICAgYnRuU2tpcC5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsIGFzeW5jICgpID0+IHsNCiAgICAgIHRyeSB7DQogICAgICAgIGF3YWl0IGludm9rZSgic2VuZF9zdGRpbiIsIHsgaWQ6IGN1cnJlbnRQcm9jZXNzSWQsIGlucHV0OiAic1xuIiB9KTsNCiAgICAgICAgc2V0U3RhdGUoU1RBVEUuQVdBSVRJTkdfU1RSSVApOw0KICAgICAgICBzZXRQcm9tcHQoIlNraXBwZWQgY3VycmVudCBzdHJpcC4gQXdhaXRpbmcgbmV4dC4uLiIpOw0KICAgICAgfSBjYXRjaCAoZSkgeyBjb25zb2xlLmVycm9yKCJzZW5kX3N0ZGluIGVycm9yOiIsIGUpOyB9DQogICAgfSk7DQogIH0NCg0KICAvLyBDYW5jZWwgYnV0dG9uIOKAlCBraWxscyB0aGUgcHJvY2Vzcw0KICBpZiAoYnRuQ2FuY2VsKSB7DQogICAgYnRuQ2FuY2VsLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgYXN5bmMgKCkgPT4gew0KICAgICAgdHJ5IHsNCiAgICAgICAgYXdhaXQgaW52b2tlKCJraWxsX3Byb2Nlc3MiLCB7IGlkOiBjdXJyZW50UHJvY2Vzc0lkIH0pOw0KICAgICAgICBzdG9wU3dhdGNoTGlzdGVuZXIoKTsNCiAgICAgICAgc2V0U3RhdGUocmVjb3JkZWRQYXNzZXMubGVuZ3RoID4gMCA/IFNUQVRFLkZJTklTSEVEIDogU1RBVEUuSURMRSk7DQogICAgICAgIHNldFByb21wdCgiTWVhc3VyZW1lbnQgY2FuY2VsbGVkLiIpOw0KICAgICAgfSBjYXRjaCAoZSkgew0KICAgICAgICBjb25zb2xlLmVycm9yKCJraWxsX3Byb2Nlc3MgZXJyb3I6IiwgZSk7DQogICAgICAgIHNldFByb21wdChgQ2FuY2VsIGZhaWxlZDogJHtlfWApOw0KICAgICAgfSBmaW5hbGx5IHsNCiAgICAgICAgc2V0TWVhc3VyZW1lbnRCdXN5KGZhbHNlKTsNCiAgICAgICAgc3RvcFN3YXRjaExpc3RlbmVyKCk7DQogICAgICB9DQogICAgfSk7DQogIH0NCg0KICAvLyBTdGFydCBpbiBJRExFIHN0YXRlDQogIHNldFN0YXRlKFNUQVRFLklETEUpOw0KICBzZXRQcm9tcHQoIlByZXNzICdTdGFydCBNZWFzdXJlbWVudCcgdG8gYmVnaW4gcmVhZGluZyB0aGUgcHJpbnRlZCB0YXJnZXQuIik7DQp9DQoNCmZ1bmN0aW9uIGFkdmFuY2VUb1N0YWdlNCgpIHsNCiAgY29uc3Qgc3RlcHMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuc3RlcCcpOw0KICBjb25zdCBzdGFnZXMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuc3RhZ2UnKTsNCg0KICBzdGVwcy5mb3JFYWNoKHMgPT4gcy5jbGFzc0xpc3QucmVtb3ZlKCdhY3RpdmUnKSk7DQogIGlmIChzdGVwc1szXSkgew0KICAgIHN0ZXBzWzNdLmNsYXNzTGlzdC5hZGQoJ2FjdGl2ZScpOw0KICAgIHN0ZXBzWzNdLmNsYXNzTGlzdC5yZW1vdmUoJ2Rpc2FibGVkJyk7DQogIH0NCg0KICBzdGFnZXMuZm9yRWFjaChzID0+IHsNCiAgICBzLmNsYXNzTGlzdC5yZW1vdmUoJ2FjdGl2ZScpOw0KICAgIHMuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7DQogIH0pOw0KICBpZiAoc3RhZ2VzWzNdKSB7DQogICAgc3RhZ2VzWzNdLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpOw0KICAgIHN0YWdlc1szXS5jbGFzc0xpc3QuYWRkKCdhY3RpdmUnKTsNCiAgfQ0KfQ0K \ No newline at end of file -- 2.39.5 From 4c4440d7f610696092158c5d93b5a7547ea69a06 Mon Sep 17 00:00:00 2001 From: gronod <1+gronod@noreply@i3omb.com> Date: Fri, 28 Aug 2026 18:49:05 +0100 Subject: [PATCH 2/3] fix(stage3): update UI label for chartread comm port --- src/index.html | 568 +------------------------------------------------ 1 file changed, 1 insertion(+), 567 deletions(-) diff --git a/src/index.html b/src/index.html index bbefaf2..742fff3 100644 --- a/src/index.html +++ b/src/index.html @@ -1,567 +1 @@ - - -
- - -Configure the colour patches for your profiling target.
- -