fix(chartread): enable ARGYLL_NOT_INTERACTIVE for stdin piping & fix instlist JSON parsing (resolves #134) #135
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "iccery",
|
"name": "iccery",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.3.4",
|
"version": "0.3.5",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"fetch-argyll": "node scripts/fetch-argyll.mjs",
|
"fetch-argyll": "node scripts/fetch-argyll.mjs",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "iccery"
|
name = "iccery"
|
||||||
version = "0.3.4"
|
version = "0.3.5"
|
||||||
description = "Modern Printer Profiling UI frontend for ArgyllCMS"
|
description = "Modern Printer Profiling UI frontend for ArgyllCMS"
|
||||||
authors = ["Gordon"]
|
authors = ["Gordon"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ impl ProcessManager {
|
|||||||
command.stdout(Stdio::piped());
|
command.stdout(Stdio::piped());
|
||||||
command.stderr(Stdio::piped());
|
command.stderr(Stdio::piped());
|
||||||
command.stdin(Stdio::piped());
|
command.stdin(Stdio::piped());
|
||||||
|
command.env("ARGYLL_NOT_INTERACTIVE", "1");
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "ICCery",
|
"productName": "ICCery",
|
||||||
"version": "0.3.4",
|
"version": "0.3.5",
|
||||||
"identifier": "com.gronod.iccery",
|
"identifier": "com.gronod.iccery",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../src"
|
"frontendDist": "../src"
|
||||||
|
|||||||
+57
-21
@@ -94,22 +94,24 @@ export function initChartread() {
|
|||||||
btnDetectInstruments.textContent = "Detecting...";
|
btnDetectInstruments.textContent = "Detecting...";
|
||||||
setPrompt("Querying connected spectrophotometers and colorimeters via instlist...");
|
setPrompt("Querying connected spectrophotometers and colorimeters via instlist...");
|
||||||
|
|
||||||
const detected = [];
|
let stdoutAccumulator = "";
|
||||||
|
const regexDetected = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const unlistenStdout = await listen("process:stdout", (event) => {
|
const unlistenStdout = await listen("process:stdout", (event) => {
|
||||||
if (event.payload.id !== "instlist" || !event.payload.line) return;
|
if (event.payload.id !== "instlist" || !event.payload.line) return;
|
||||||
const line = event.payload.line.trim();
|
const line = event.payload.line;
|
||||||
|
stdoutAccumulator += line + "\n";
|
||||||
|
|
||||||
// Matches Argyll instlist output e.g. "1: 'i1Pro' on 'USB'" or "1: 'ColorMunki'" or "1 = 'i1Display'"
|
// Legacy regex line matching fallback
|
||||||
const KNOWN_INST_TOKENS = /i1|ColorMunki|Spyder|spectro|Display|Huey|DTP|SpectroScan|Smile|Klein/i;
|
const KNOWN_INST_TOKENS = /i1|ColorMunki|Spyder|spectro|Display|Huey|DTP|SpectroScan|Smile|Klein/i;
|
||||||
const match = line.match(/^(\d+)[\s:=]+'?([^'\n]+)'?(?:\s+on\s+'?([^'\n]+)'?)?/i);
|
const match = line.trim().match(/^(\d+)[\s:=]+'?([^'\n]+)'?(?:\s+on\s+'?([^'\n]+)'?)?/i);
|
||||||
if (match) {
|
if (match) {
|
||||||
const index = match[1];
|
const index = match[1];
|
||||||
const name = match[2].trim();
|
const name = match[2].trim();
|
||||||
const port = match[3] ? match[3].trim() : "";
|
const port = match[3] ? match[3].trim() : "";
|
||||||
if (KNOWN_INST_TOKENS.test(name) || port.length > 0) {
|
if (KNOWN_INST_TOKENS.test(name) || port.length > 0) {
|
||||||
detected.push({ index, name, port });
|
regexDetected.push({ index, name, port });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -123,16 +125,35 @@ export function initChartread() {
|
|||||||
|
|
||||||
instrumentSelect.innerHTML = `<option value="">Auto (First available port)</option>`;
|
instrumentSelect.innerHTML = `<option value="">Auto (First available port)</option>`;
|
||||||
|
|
||||||
if (detected.length > 0) {
|
let devicesList = [];
|
||||||
detected.forEach((inst) => {
|
|
||||||
|
// Try parsing JSON output from instlist
|
||||||
|
try {
|
||||||
|
const trimmed = stdoutAccumulator.trim();
|
||||||
|
const parsed = JSON.parse(trimmed);
|
||||||
|
if (parsed && Array.isArray(parsed.devices)) {
|
||||||
|
devicesList = parsed.devices.map(d => ({
|
||||||
|
index: String(d.port || ""),
|
||||||
|
name: d.name || d.type || "Instrument",
|
||||||
|
type: d.type || "",
|
||||||
|
port: String(d.port || ""),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// Fall back to regex parsed items if not JSON
|
||||||
|
devicesList = regexDetected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devicesList.length > 0) {
|
||||||
|
devicesList.forEach((inst) => {
|
||||||
const opt = document.createElement("option");
|
const opt = document.createElement("option");
|
||||||
// Do not pass instlist ordinal as -c comm port; use empty value for auto-port (#111)
|
// Port value for -c switch. If port is 1 or auto, empty string leaves -c omitted for default port
|
||||||
opt.value = "";
|
opt.value = inst.port && inst.port !== "1" ? inst.port : "";
|
||||||
opt.textContent = `${inst.name}${inst.port ? ` on ${inst.port}` : ""}`;
|
opt.textContent = `${inst.type || inst.name}${inst.port ? ` (Port ${inst.port})` : ""}`;
|
||||||
instrumentSelect.appendChild(opt);
|
instrumentSelect.appendChild(opt);
|
||||||
});
|
});
|
||||||
instrumentSelect.value = "";
|
instrumentSelect.value = "";
|
||||||
setPrompt(`Found ${detected.length} instrument(s): ${detected.map(d => d.name).join(", ")}`);
|
setPrompt(`Found ${devicesList.length} instrument(s): ${devicesList.map(d => d.type || d.name).join(", ")}`);
|
||||||
} else {
|
} else {
|
||||||
setPrompt("No instruments found via instlist. Ensure USB cable is plugged in.");
|
setPrompt("No instruments found via instlist. Ensure USB cable is plugged in.");
|
||||||
}
|
}
|
||||||
@@ -164,6 +185,8 @@ export function initChartread() {
|
|||||||
btnStartRead.classList.remove("hidden");
|
btnStartRead.classList.remove("hidden");
|
||||||
break;
|
break;
|
||||||
case STATE.CALIBRATING:
|
case STATE.CALIBRATING:
|
||||||
|
btnCalibrate.disabled = false;
|
||||||
|
btnCalibrate.textContent = "✓ Calibrate";
|
||||||
btnCalibrate.classList.remove("hidden");
|
btnCalibrate.classList.remove("hidden");
|
||||||
btnCancel.classList.remove("hidden");
|
btnCancel.classList.remove("hidden");
|
||||||
break;
|
break;
|
||||||
@@ -243,21 +266,26 @@ export function initChartread() {
|
|||||||
// Parse prompts for state transitions
|
// Parse prompts for state transitions
|
||||||
const lineLower = line.toLowerCase();
|
const lineLower = line.toLowerCase();
|
||||||
|
|
||||||
if (lineLower.includes("calibrat") && lineLower.includes("place")) {
|
if (
|
||||||
|
(lineLower.includes("place") && (lineLower.includes("reference") || lineLower.includes("white") || lineLower.includes("calibrat"))) ||
|
||||||
|
lineLower.includes("hit any key to continue") ||
|
||||||
|
lineLower.includes("calibration")
|
||||||
|
) {
|
||||||
setState(STATE.CALIBRATING);
|
setState(STATE.CALIBRATING);
|
||||||
setPrompt(line);
|
setPrompt(line.trim());
|
||||||
} else if (lineLower.includes("hit") && lineLower.includes("read") && lineLower.includes("strip")) {
|
} else if (
|
||||||
|
(lineLower.includes("hit") && lineLower.includes("read") && lineLower.includes("strip")) ||
|
||||||
|
lineLower.includes("ready to read") ||
|
||||||
|
(lineLower.includes("read") && lineLower.includes("strip") && lineLower.includes("key"))
|
||||||
|
) {
|
||||||
setState(STATE.AWAITING_STRIP);
|
setState(STATE.AWAITING_STRIP);
|
||||||
setPrompt(line);
|
setPrompt(line.trim());
|
||||||
} else if (lineLower.includes("ready to read")) {
|
|
||||||
setState(STATE.AWAITING_STRIP);
|
|
||||||
setPrompt(line);
|
|
||||||
} else if (lineLower.includes("reading strip") || lineLower.includes("processing")) {
|
} else if (lineLower.includes("reading strip") || lineLower.includes("processing")) {
|
||||||
setState(STATE.READING);
|
setState(STATE.READING);
|
||||||
setPrompt(line);
|
setPrompt(line.trim());
|
||||||
} else if (lineLower.includes("error") || lineLower.includes("too fast") || lineLower.includes("too slow") || lineLower.includes("misread")) {
|
} else if (lineLower.includes("error") || lineLower.includes("too fast") || lineLower.includes("too slow") || lineLower.includes("misread")) {
|
||||||
setState(STATE.ERROR);
|
setState(STATE.ERROR);
|
||||||
setPrompt("⚠️ " + line);
|
setPrompt("⚠️ " + line.trim());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -423,8 +451,16 @@ export function initChartread() {
|
|||||||
if (btnCalibrate) {
|
if (btnCalibrate) {
|
||||||
btnCalibrate.addEventListener("click", async () => {
|
btnCalibrate.addEventListener("click", async () => {
|
||||||
try {
|
try {
|
||||||
|
btnCalibrate.disabled = true;
|
||||||
|
btnCalibrate.textContent = "⏳ Calibrating...";
|
||||||
|
setPrompt("Sending calibration command to instrument...");
|
||||||
await invoke("send_stdin", { id: currentProcessId, input: " \n" });
|
await invoke("send_stdin", { id: currentProcessId, input: " \n" });
|
||||||
} catch (e) { console.error("send_stdin error:", e); }
|
} catch (e) {
|
||||||
|
console.error("send_stdin error:", e);
|
||||||
|
btnCalibrate.disabled = false;
|
||||||
|
btnCalibrate.textContent = "✓ Calibrate";
|
||||||
|
setPrompt(`Failed to send calibration signal: ${e}`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user