diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 089e9bb..cde492b 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -386,14 +386,24 @@ pub async fn read_tiff_preview_png(path: String) -> Result { pub struct ChartreadConfig { pub basename: String, pub cwd: String, + pub port: Option, } pub fn build_chartread_args(config: &ChartreadConfig) -> Vec { - vec![ + let mut args = vec![ "-v".to_string(), "-u".to_string(), - config.basename.clone(), - ] + ]; + + if let Some(ref port) = config.port { + if !port.trim().is_empty() { + args.push("-c".to_string()); + args.push(port.trim().to_string()); + } + } + + args.push(config.basename.clone()); + args } #[tauri::command] @@ -699,11 +709,23 @@ mod tests { let config = ChartreadConfig { basename: "my_profile".to_string(), cwd: "/home/user".to_string(), + port: None, }; let args = build_chartread_args(&config); assert_eq!(args, vec!["-v", "-u", "my_profile"]); } + #[test] + fn test_build_chartread_args_with_port() { + let config = ChartreadConfig { + basename: "my_profile".to_string(), + cwd: "/home/user".to_string(), + port: Some("1".to_string()), + }; + let args = build_chartread_args(&config); + assert_eq!(args, vec!["-v", "-u", "-c", "1", "my_profile"]); + } + #[test] fn test_build_colprof_args() { let config = ColprofConfig { diff --git a/src/index.html b/src/index.html index 4af00bd..104de42 100644 --- a/src/index.html +++ b/src/index.html @@ -249,6 +249,19 @@

Measure Target

Read the printed target patches with your spectrophotometer.

+ +
+
+ +
+ + +
+
+
+
State: IDLE
@@ -485,4 +498,4 @@
- + \ No newline at end of file diff --git a/src/js/chartread.js b/src/js/chartread.js index 9b78b92..3020dd4 100644 --- a/src/js/chartread.js +++ b/src/js/chartread.js @@ -36,11 +36,70 @@ export function initChartread() { 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"); + // 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; @@ -103,9 +162,12 @@ export function initChartread() { 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}`;