From b2435a88b31a11e5c101209ea7d02f074b4ad07a Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 21 Aug 2026 18:33:20 +0100 Subject: [PATCH 1/2] feat(spectro): add instlist utility for JSON instrument enumeration - Implement spectro/instlist standalone discovery utility using new_icompaths() - Emit structured JSON array to stdout with port, name, and instrument type - Support -h, -?, --help, -v, and -D command-line switches - Add instlist to spectro/Jamfile build targets - Add doc/instlist.html and register in doc/MinorTools.html --- doc/MinorTools.html | 1 + doc/instlist.html | 39 +++++++++++++ spectro/Jamfile | 5 +- spectro/instlist.c | 134 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 doc/instlist.html create mode 100644 spectro/instlist.c diff --git a/doc/MinorTools.html b/doc/MinorTools.html index 5e9a5c4..93c6b01 100755 --- a/doc/MinorTools.html +++ b/doc/MinorTools.html @@ -52,6 +52,7 @@ Test 1D Brent solver
xicc/xcolorantslu.exe
xicc/xfbview.exe
spectro/dispwin.exe +
spectro/instlist.exe
target/ifarp.exe
target/ppoint.exe
target/qrand.exe diff --git a/doc/instlist.html b/doc/instlist.html new file mode 100644 index 0000000..59de379 --- /dev/null +++ b/doc/instlist.html @@ -0,0 +1,39 @@ + + + + instlist + + + + +

spectro/instlist

+
+ Enumerate connected instrument communication ports (USB and Serial) and output the list in JSON format.
+

Usage

+  instlist [-options]
+  -h, -?, --help  Show help information
+  -v              Verbose diagnostics to stderr
+  -D [level]      Print debug diagnostics to stderr
+

+

Output

+ The output is emitted to stdout as a JSON object containing an array of discovered device objects:
+
+{
+  "event": "instruments",
+  "devices": [
+    {
+      "port": 1,
+      "name": "usb:/bus0/dev1 (X-Rite i1Pro)",
+      "type": "i1Pro"
+    }
+  ]
+}
+    
+

Comments

+ This tool is designed to allow graphical user interfaces and external automation scripts to discover available spectrophotometers and colorimeters across Windows, macOS, and Linux without needing to parse interactive usage output.
+
+ The port number directly corresponds to the -c parameter accepted by tools such as chartread, spotread, and dispread.
+
+ + diff --git a/spectro/Jamfile b/spectro/Jamfile index d4788ff..e0df1fc 100755 --- a/spectro/Jamfile +++ b/spectro/Jamfile @@ -51,7 +51,7 @@ if $(UNIX) { #Products Libraries = libinsttypes libdisptechs libinst libdisp libconv libinstapp ; Executables = dispwin synthcal dispread dispcal fakeread synthread - chartread spotread illumread ccxxmake average oeminst ; + chartread spotread instlist illumread ccxxmake average oeminst ; Headers = inst.h ; Samples = SOtele.sp linear.cal strange.cal ccxx.ti1 ; @@ -198,6 +198,9 @@ Main illumread : illumread.c : : : ../target : : ; # Printed target spot reader utility Main spotread : spotread.c : : : : : ; +# Connected instrument enumeration utility +Main instlist : instlist.c : : : : : ; + echo "HOME = " $(HOME) ; echo "PWD = " $(PWD) ; diff --git a/spectro/instlist.c b/spectro/instlist.c new file mode 100644 index 0000000..2b59b03 --- /dev/null +++ b/spectro/instlist.c @@ -0,0 +1,134 @@ +/* + * Argyll Color Management System + * USB & Serial Instrument Enumeration Utility. + * Outputs connected instruments in structured JSON format. + * + * Author: Graeme W. Gill / Gordon + * + * Copyright 2026 Graeme W. Gill / Gordon + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License.txt file for licencing details. + */ + +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#include "numlib.h" +#include "cgats.h" +#include "conv.h" +#include "xicc.h" +#include "insttypes.h" +#include "icoms.h" +#include "inst.h" + +static void usage(char *diag, ...) { + fprintf(stderr,"Enumerate Instrument Ports, Version %s\n",ARGYLL_VERSION_STR); + if (diag != NULL) { + va_list args; + fprintf(stderr," Diagnostic: "); + va_start(args, diag); + vfprintf(stderr, diag, args); + va_end(args); + fprintf(stderr,"\n"); + } + fprintf(stderr,"Author: Graeme W. Gill / Gordon, licensed under the AGPL Version 3\n"); + fprintf(stderr,"usage: instlist [-options]\n"); + fprintf(stderr," -h, -?, --help Show this help information\n"); + fprintf(stderr," -v Verbose diagnostics to stderr\n"); + fprintf(stderr," -D [level] Print debug diagnostics to stderr\n"); + exit(1); +} + +int main(int argc, char *argv[]) { + int fa, nfa; + int verb = 0; + int debug = 0; + icompaths *icmps = NULL; + icompath **paths = NULL; + int i; + int count = 0; + + /* Set executable path for logger */ + set_exe_path(argv[0]); + + /* Process command-line options */ + for (fa = 1; fa < argc; fa++) { + nfa = fa + 1; + if (argv[fa][0] == '-') { + char *na = NULL; + if (nfa < argc && argv[nfa][0] != '-') + na = argv[nfa]; + + if (strcmp(argv[fa], "-h") == 0 || strcmp(argv[fa], "-?") == 0 || + strcmp(argv[fa], "--help") == 0 || strcmp(argv[fa], "-help") == 0) { + usage(NULL); + } else if (argv[fa][1] == 'v') { + verb = 1; + g_log->verb = verb; + } else if (argv[fa][1] == 'D') { + debug = 1; + if (na != NULL && na[0] >= '0' && na[0] <= '9') { + debug = atoi(na); + fa = nfa; + } + g_log->debug = debug; + } else { + usage("Unknown option '%s'", argv[fa]); + } + } else { + usage("Unexpected argument '%s'", argv[fa]); + } + } + + if ((icmps = new_icompaths(g_log)) == NULL) { + printf("{\n \"event\": \"instruments\",\n \"devices\": []\n}\n"); + fflush(stdout); + return 1; + } + + paths = icmps->paths; + + printf("{\n \"event\": \"instruments\",\n \"devices\": [\n"); + + if (paths != NULL) { + for (i = 0; paths[i] != NULL; i++) { + char *tname = inst_name(paths[i]->dtype); + char *name = paths[i]->name ? paths[i]->name : "Unknown"; + + if (count > 0) { + printf(",\n"); + } + + printf(" {\n"); + printf(" \"port\": %d,\n", i + 1); + printf(" \"name\": \""); + /* Escape quotes and backslashes in name */ + { + char *p; + for (p = name; *p != '\0'; p++) { + if (*p == '"' || *p == '\\') { + putchar('\\'); + } + putchar(*p); + } + } + printf("\",\n"); + printf(" \"type\": \"%s\"\n", (tname != NULL && tname[0] != '\0') ? tname : "Unknown"); + printf(" }"); + count++; + } + } + + printf("\n ]\n}\n"); + fflush(stdout); + + if (icmps != NULL) + icmps->del(icmps); + + return 0; +} -- 2.39.5 From 8cb92ca3a60cf7423bb6018185a0dfe5ac17c4d2 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 21 Aug 2026 19:00:41 +0100 Subject: [PATCH 2/2] Fix build cleanup for instlist and imdi_k.c --- imdi/Jamfile | 1 + spectro/Jamfile | 1 + 2 files changed, 2 insertions(+) diff --git a/imdi/Jamfile b/imdi/Jamfile index 79b27fb..2c0ec65 100755 --- a/imdi/Jamfile +++ b/imdi/Jamfile @@ -42,6 +42,7 @@ LINKLIBS = ../numlib/libnum ; # GenFile source.c : program args ; make custom file # Generate all the kernel files GenFileND imdi_k.h : imdi_make $(IMDI_MAKE_OPT) -d [ NormPaths $(DOT) ] ; +Clean clean : imdi_k.c ; # imdi library Library libimdi : imdi.c imdi_tab.c ; diff --git a/spectro/Jamfile b/spectro/Jamfile index e0df1fc..38323d0 100755 --- a/spectro/Jamfile +++ b/spectro/Jamfile @@ -200,6 +200,7 @@ Main spotread : spotread.c : : : : : ; # Connected instrument enumeration utility Main instlist : instlist.c : : : : : ; +Clean clean : instlist$(SUFEXE) instlist.o ; echo "HOME = " $(HOME) ; echo "PWD = " $(PWD) ; -- 2.39.5