Compare commits

...
Author SHA1 Message Date
gronod ba409108bd Merge pull request 'Merge development into main (fix macOS arm64 cross-compilation)' (#50) from development into main
Windows x86_64 Release Build / build (push) Successful in 8m20s
Linux x86_64 Release Build / build (push) Successful in 9m8s
macOS Release Build / build (arm64, macOS_arm64_bin, arm64-apple-darwin) (push) Successful in 5m28s
macOS Release Build / build (x86_64, macOS_x86_64_bin, x86_64-apple-darwin) (push) Successful in 5m30s
macOS Release Build / universal (push) Successful in 1m30s
Merge pull request #50 from development into main
2026-09-05 19:53:02 +01:00
gronod 0005cc61bf Merge pull request 'fix: enable arm64 macOS cross-compilation on Intel runners' (#49) from fix/macos-arm64-cross-compile into development
Merge pull request #49 from fix/macos-arm64-cross-compile into development
2026-09-05 19:52:53 +01:00
gronod 017744fbdc fix(build): enable arm64 cross-compilation on Intel macOS runners
- Add -arch arm64 / -arch x86_64 compiler and linker flags to Jambase for macOS
- Pre-generate mkg3states and imdi_make outputs using host compiler before cross-compilation
- Conditionalize code-generator execution in tiff/Jamfile and imdi/Jamfile when outputs exist
- Add architecture verification in workflow smoke tests and universal packaging steps
2026-09-05 19:51:19 +01:00
gronod b55702337c Merge pull request 'Merge development into main (v3.5.0-ICCery1.8)' (#48) from development into main
Windows x86_64 Release Build / build (push) Failing after 2m12s
macOS Release Build / build (arm64, macOS_arm64_bin, arm64-apple-darwin) (push) Successful in 5m11s
Linux x86_64 Release Build / build (push) Successful in 8m35s
macOS Release Build / build (x86_64, macOS_x86_64_bin, x86_64-apple-darwin) (push) Successful in 5m42s
macOS Release Build / universal (push) Failing after 19s
2026-09-05 18:16:54 +01:00
gronod ca85951565 Merge pull request 'feat: i1Pro 2 visual LED feedback in chartread (#37)' (#47) from feat/i1pro2-led-feedback into development 2026-09-05 18:16:42 +01:00
gronod 70111c4272 feat(i1pro2): implement visual LED feedback in chartread (fixes #37)
- Add inst_led_state enum, INST_CAP_LED_STATUS capability, and set_led_state to inst.h / inst.c
- Implement i1pro2_indLEDset, worker thread i1pro2_led_thread, and i1pro_imp_set_led_state in i1pro_imp.c / i1pro_imp.h
- Wire up i1pro_set_led_state and determine_capabilities in i1pro.c for i1Pro 2 (Rev E)
- Integrate -Y l option and lifecycle hooks in chartread.c for calibration, readiness, success, failure, and clean exit
2026-09-05 18:13:31 +01:00
gronod 0672ab7fea Merge pull request 'ci(gitea): build intel and arm64 in matrix and merge into universal binary' (#46) from development into main
macOS Release Build / build (arm64, macOS_arm64_bin, arm64-apple-darwin) (push) Successful in 5m23s
Linux x86_64 Release Build / build (push) Successful in 9m0s
Windows x86_64 Release Build / build (push) Successful in 8m25s
macOS Release Build / build (x86_64, macOS_x86_64_bin, x86_64-apple-darwin) (push) Successful in 5m35s
macOS Release Build / universal (push) Failing after 19s
Reviewed-on: #46
2026-09-05 17:51:58 +01:00
gronod f837314f5c ci(gitea): build intel and arm64 in matrix and merge into universal binary 2026-09-05 17:48:53 +01:00
gronod e80e41beb9 Merge pull request 'Development' (#45) from development into main
Linux x86_64 Release Build / build (push) Successful in 11m36s
Windows x86_64 Release Build / build (push) Canceled after 2m47s
macOS x86_64 Release Build / build (push) Canceled after 0s
Reviewed-on: #45
2026-09-05 16:23:16 +01:00
gronod 851c7eece3 chore: update macos runner to use macos label in build workflow 2026-09-05 16:22:41 +01:00
gronod b67c7ca318 Merge pull request 'Amended gitea runner tag to macos-latest' (#44) from fix/dispcal_SIGSEV into development
Reviewed-on: #44
2026-09-05 16:19:49 +01:00
gronod 7744518b87 Amended gitea runner tag to macos-latest 2026-09-05 16:18:31 +01:00
gronod c5c06f5dc6 Merge pull request 'Merge development into main (CCOPTFLAG fix)' (#43) from development into main
Windows x86_64 Release Build / build (push) Failing after 1m27s
macOS x86_64 Release Build / build (push) Successful in 4m23s
Linux x86_64 Release Build / build (push) Successful in 10m53s
Merge pull request #43 from development into main
2026-09-05 16:05:24 +01:00
gronod 18c6a4c89d Merge pull request 'Add CCOPTFLAG warning tolerance to macOS Gitea workflow' (#42) from fix/dispcal_SIGSEV into development
Merge pull request #42 from fix/dispcal_SIGSEV into development
2026-09-05 16:05:15 +01:00
Gordon Bolton c4cc3de0ec ci(gitea): pass compiler warning tolerance flags in CCOPTFLAG for modern Apple Clang 2026-09-05 16:05:07 +01:00
gronod 931d92cddf Merge pull request 'Merge development into main (macOS CI fix)' (#41) from development into main
Windows x86_64 Release Build / build (push) Failing after 1m22s
macOS x86_64 Release Build / build (push) Failing after 2m47s
Linux x86_64 Release Build / build (push) Successful in 9m52s
Merge pull request #41 from development into main
2026-09-05 15:59:11 +01:00
gronod 16eb647b1a Merge pull request 'Fix macOS ftjam setup in Gitea Actions workflow' (#40) from fix/dispcal_SIGSEV into development
Merge pull request #40 from fix/dispcal_SIGSEV into development
2026-09-05 15:59:02 +01:00
Gordon Bolton 20bce9dff4 ci(gitea): use curl, make jam0, and non-root install for macOS ftjam setup 2026-09-05 15:58:54 +01:00
gronod edeb6dcf67 Merge pull request 'Merge development into main (v3.5.0-ICCery1.7)' (#39) from development into main
macOS x86_64 Release Build / build (push) Failing after 12s
Windows x86_64 Release Build / build (push) Successful in 8m22s
Linux x86_64 Release Build / build (push) Successful in 11m48s
Merge pull request #39 from development into main
2026-09-05 15:43:27 +01:00
gronod cb5e740688 Merge pull request 'Fix: dispcal SIGSEGV and add Gitea macOS runner workflow' (#38) from fix/dispcal_SIGSEV into development
Merge pull request #38 from fix/dispcal_SIGSEV into development
2026-09-05 15:43:14 +01:00
Gordon Bolton 5392458515 ci(gitea): add macos-14 x86_64 runner workflow using upload-artifact@v3 2026-09-05 15:42:50 +01:00
Gordon Bolton 39cad74c43 Fix SIGSEGV in i1Pro 2 teardown and cleanup
Windows x86_64 Release Build / build (push) Successful in 8m50s
Linux x86_64 Release Build / build (push) Successful in 21m53s
- Relocate LED turn-off to i1pro_close_port() before closing communication port
- Guard LED turn-off and log updates against NULL pointers and closed ports in del_i1proimp()
- Ensure clean termination, unblock, and joining of i1pro switch and trigger threads in both i1pro_close_port() and del_i1proimp()
- Protect against NULL terminate callback on Unix/macOS
2026-09-04 14:39:59 +01:00
gronod d271bb603d Merge pull request 'Development' (#36) from development into main
Windows x86_64 Release Build / build (push) Successful in 8m5s
Linux x86_64 Release Build / build (push) Successful in 9m22s
Reviewed-on: #36
2026-09-01 12:10:31 +01:00
gronod d1bf83928d Merge pull request 'Fix update macos runners' (#35) from fix-update-macos-runners into development
Reviewed-on: #35
2026-09-01 12:10:00 +01:00
gronod e194514264 Update .github/workflows/build-macos.yml 2026-09-01 12:09:14 +01:00
gronod 2261ebd331 Merge pull request 'Development' (#34) from development into main
Windows x86_64 Release Build / build (push) Failing after 1m10s
Linux x86_64 Release Build / build (push) Successful in 9m58s
Reviewed-on: #34
2026-09-01 11:22:12 +01:00
gronod de3065f4cc Merge pull request 'Update .github/workflows/build-macos.yml' (#33) from update-macos-act-runner into development
Reviewed-on: #33
2026-09-01 11:21:15 +01:00
gronod cf93305fdd ci(macos): add ad-hoc codesigning and signature verification to macOS workflow
Windows x86_64 Release Build / build (push) Successful in 8m21s
Linux x86_64 Release Build / build (push) Successful in 49m44s
2026-09-01 10:17:34 +01:00
gronod 1eb72e865b fix(package): apply ad-hoc code signatures to macOS Mach-O binaries in makepackagebin.sh 2026-09-01 10:17:06 +01:00
gronod e0f86eec29 Merge development into main (v3.5.0-ICCery.1.4)
Linux x86_64 Release Build / build (push) Successful in 9m37s
Windows x86_64 Release Build / build (push) Successful in 9m38s
Merge pull request 'Merge development into main (v3.5.0-ICCery.1.4)' (#31) from development into main
2026-09-01 00:16:56 +01:00
gronod 21d59d891c Merge development into main (v3.5.0-ICCery.1.3)
Windows x86_64 Release Build / build (push) Failing after 1m12s
Linux x86_64 Release Build / build (push) Successful in 8m34s
Merge pull request 'Merge development into main (v3.5.0-ICCery.1.3)' (#29) from development into main
2026-09-01 00:03:47 +01:00
gronod a07a5d638d Merge pull request 'Merge development into main (v3.5.0-ICCery.1.2)' (#26) from development into main
Windows x86_64 Release Build / build (push) Successful in 9m11s
Linux x86_64 Release Build / build (push) Successful in 9m26s
Merge pull request #26 from development into main
2026-08-29 14:39:39 +01:00
gronod b02df94744 Merge pull request 'Merge development into main (v3.5.0-ICCery.1.1)' (#23) from development into main
Windows x86_64 Release Build / build (push) Successful in 9m34s
Linux x86_64 Release Build / build (push) Successful in 9m41s
Merge pull request #23 from development into main
2026-08-28 20:23:45 +01:00
gronod fe0230b3d5 Merge branch 'development' into main
Windows x86_64 Release Build / build (push) Successful in 7m43s
Linux x86_64 Release Build / build (push) Successful in 10m3s
2026-08-26 01:11:26 +01:00
gronod 885fa3fee8 Merge pull request 'Development' (#18) from development into main
Windows x86_64 Release Build / build (push) Has been cancelled
Linux x86_64 Release Build / build (push) Has been cancelled
Reviewed-on: #18
2026-08-26 01:02:30 +01:00
11 changed files with 681 additions and 50 deletions
+234
View File
@@ -0,0 +1,234 @@
name: macOS Release Build
on:
push:
tags:
- 'v*'
workflow_dispatch:
jobs:
build:
strategy:
matrix:
include:
- arch: x86_64
machtype: x86_64-apple-darwin
artifact: macOS_x86_64_bin
- arch: arm64
machtype: arm64-apple-darwin
artifact: macOS_arm64_bin
runs-on: macos
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install ftjam
run: |
curl -fsSL -o ftjam.tar.gz https://sourceforge.net/projects/freetype/files/ftjam/2.5.2/ftjam-2.5.2.tar.gz/download || wget --no-check-certificate -q -O ftjam.tar.gz https://sourceforge.net/projects/freetype/files/ftjam/2.5.2/ftjam-2.5.2.tar.gz/download
tar -xzf ftjam.tar.gz
cd ftjam-2.5.2
make jam0 CC="cc -Wno-implicit-function-declaration -Wno-incompatible-pointer-types -Wno-int-conversion -Wno-implicit-int"
mkdir -p "$HOME/.local/bin"
cp jam0 "$HOME/.local/bin/jam"
echo "$HOME/.local/bin" >> $GITHUB_PATH
cp jam0 /usr/local/bin/jam 2>/dev/null || true
- name: Build ArgyllCMS
run: |
export PATH="$HOME/.local/bin:/usr/local/bin:$PATH"
export OSTYPE=darwin
export MACHTYPE=${{ matrix.machtype }}
export HOSTTYPE=${{ matrix.arch }}
export CCOPTFLAG="-O0 -Wno-error=implicit-function-declaration -Wno-error=incompatible-pointer-types -Wno-error=int-conversion -Wno-error=implicit-int"
# Pre-generate code-generator outputs using host compiler if needed
if [ ! -f tiff/libtiff/tif_fax3sm.c ]; then
echo "Pre-generating tiff/libtiff/tif_fax3sm.c using host compiler..."
(cd tiff && chmod +x configure && ./configure --disable-old-jpeg --disable-pixarlog --disable-zlib --disable-jbig --disable-lzma) >/dev/null 2>&1 || true
cc -Itiff/libtiff tiff/libtiff/mkg3states.c tiff/port/strcasecmp.c -o tiff/mkg3states
tiff/mkg3states -c const tiff/libtiff/tif_fax3sm.c
rm -f tiff/mkg3states
fi
if [ ! -f imdi/imdi_k.h ] || [ ! -f imdi/imdi_k.c ]; then
echo "Pre-generating imdi kernel files using host compiler..."
cc -Ih -Inumlib imdi/imdi_make.c imdi/imdi_gen.c imdi/cgen.c -o imdi/imdi_make
(cd imdi && ./imdi_make -f -d .)
rm -f imdi/imdi_make
fi
jam -q -d2 -fJambase -j1 -sBUILTIN_TIFF=true -sBUILTIN_JPEG=true -sBUILTIN_PNG=true -sBUILTIN_Z=true -sBUILTIN_SSL=true install
- name: Ad-hoc Sign & Verify Binaries
run: |
for f in bin/*; do
if [ -f "$f" ] && file "$f" | grep -q "Mach-O"; then
echo "Ad-hoc signing $f"
codesign -f -s - "$f"
codesign -dvv "$f"
fi
done
- name: Smoke Tests
env:
ARGYLL_NOT_INTERACTIVE: "1"
ARGYLL_EXCLUDE_SERIAL_SCAN: "1"
run: |
HOST_ARCH="$(uname -m)"
if [ "${{ matrix.arch }}" = "$HOST_ARCH" ]; then
echo "Running native execution smoke tests for ${{ matrix.arch }}..."
./bin/dispcal -? || true
./bin/chartread -? || true
./bin/colprof -? || true
./bin/targen -? || true
else
echo "Skipping execution smoke tests when cross-compiling (${{ matrix.arch }} on $HOST_ARCH)."
fi
echo "Verifying Mach-O binary architectures match ${{ matrix.arch }}:"
for f in bin/*; do
if [ -f "$f" ] && file "$f" | grep -q "Mach-O"; then
echo "$f: $(file -b "$f")"
if ! file "$f" | grep -q "${{ matrix.arch }}"; then
echo "ERROR: $f architecture does not match expected ${{ matrix.arch }}!"
exit 1
fi
fi
done
- name: Package Release
run: |
export PATH="$HOME/.local/bin:/usr/local/bin:$PATH"
export OSTYPE=darwin
export MACHTYPE=${{ matrix.machtype }}
export HOSTTYPE=${{ matrix.arch }}
export CCOPTFLAG="-O0 -Wno-error=implicit-function-declaration -Wno-error=incompatible-pointer-types -Wno-error=int-conversion -Wno-error=implicit-int"
export NO_BUILD=1
./makepackagebin.sh
TAG="${{ github.ref_name }}"
SHORT_SHA="$(git rev-parse --short HEAD)"
echo "SHORT_SHA=${SHORT_SHA}" >> $GITHUB_ENV
mv Argyll_V*_${{ matrix.artifact }}.tgz "Argyll_${TAG}_${SHORT_SHA}_${{ matrix.artifact }}.tgz"
- name: Upload Raw Binaries
uses: actions/upload-artifact@v3
with:
name: raw-bin-${{ matrix.arch }}
path: bin/
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: Argyll_${{ github.ref_name }}_${{ env.SHORT_SHA }}_${{ matrix.artifact }}
path: Argyll_*_${{ matrix.artifact }}.tgz
- name: Upload Release Asset
if: startsWith(github.ref, 'refs/tags/')
uses: akkuman/gitea-release-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: Release ${{ github.ref_name }}
body: "Release ${{ github.ref_name }} built from commit ${{ github.sha }}"
files: Argyll_*_${{ matrix.artifact }}.tgz
universal:
needs: build
runs-on: macos
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download x86_64 binaries
uses: actions/download-artifact@v3
with:
name: raw-bin-x86_64
path: raw-bin-x86_64
- name: Download arm64 binaries
uses: actions/download-artifact@v3
with:
name: raw-bin-arm64
path: raw-bin-arm64
- name: Create Universal Binaries
run: |
mkdir -p bin
for file in raw-bin-arm64/*; do
bin_name=$(basename "$file")
if [ -f "raw-bin-x86_64/$bin_name" ]; then
if file "raw-bin-arm64/$bin_name" | grep -q "Mach-O" && file "raw-bin-x86_64/$bin_name" | grep -q "Mach-O"; then
echo "Creating universal binary with lipo: $bin_name"
lipo -create "raw-bin-x86_64/$bin_name" "raw-bin-arm64/$bin_name" -output "bin/$bin_name"
chmod +x "bin/$bin_name"
codesign -f -s - "bin/$bin_name"
else
echo "Non Mach-O file, copying directly: $bin_name"
cp "$file" "bin/$bin_name"
fi
else
echo "Warning: $bin_name not found in raw-bin-x86_64, copying arm64 version"
cp "$file" "bin/$bin_name"
if file "bin/$bin_name" | grep -q "Mach-O"; then
codesign -f -s - "bin/$bin_name"
fi
fi
done
for file in raw-bin-x86_64/*; do
bin_name=$(basename "$file")
if [ ! -f "bin/$bin_name" ]; then
echo "Warning: $bin_name only found in raw-bin-x86_64, copying x86_64 version"
cp "$file" "bin/$bin_name"
if file "bin/$bin_name" | grep -q "Mach-O"; then
codesign -f -s - "bin/$bin_name"
fi
fi
done
- name: Verify Architecture & Code Signatures
env:
ARGYLL_NOT_INTERACTIVE: "1"
ARGYLL_EXCLUDE_SERIAL_SCAN: "1"
run: |
echo "=== Binary Architecture & Signature Verification ==="
for file in bin/*; do
if [ -f "$file" ] && file "$file" | grep -q "Mach-O"; then
echo "$file: $(file -b "$file")"
if ! file "$file" | grep -q "universal binary"; then
echo "ERROR: $file is not a universal binary!"
exit 1
fi
codesign -dvv "$file"
fi
done
echo "=== Smoke Tests ==="
./bin/dispcal -? || true
./bin/chartread -? || true
./bin/colprof -? || true
./bin/targen -? || true
- name: Package Universal Release
run: |
export OSTYPE=darwin
export MACHTYPE=universal-apple-darwin
export HOSTTYPE=universal
export NO_BUILD=1
./makepackagebin.sh
TAG="${{ github.ref_name }}"
SHORT_SHA="$(git rev-parse --short HEAD)"
echo "SHORT_SHA=${SHORT_SHA}" >> $GITHUB_ENV
mv Argyll_V*_macOS_universal_bin.tgz "Argyll_${TAG}_${SHORT_SHA}_macOS_universal_bin.tgz"
- name: Upload Universal Artifact
uses: actions/upload-artifact@v3
with:
name: Argyll_${{ github.ref_name }}_${{ env.SHORT_SHA }}_macOS_universal_bin
path: Argyll_*_macOS_universal_bin.tgz
- name: Upload Universal Release Asset
if: startsWith(github.ref, 'refs/tags/')
uses: akkuman/gitea-release-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: Release ${{ github.ref_name }}
body: "Release ${{ github.ref_name }} built from commit ${{ github.sha }}"
files: Argyll_*_macOS_universal_bin.tgz
+2 -2
View File
@@ -11,7 +11,7 @@ jobs:
strategy: strategy:
matrix: matrix:
include: include:
- os: macos-latest - os: macos-26-intel
arch: x86_64 arch: x86_64
machtype: x86_64-apple-darwin machtype: x86_64-apple-darwin
artifact: macOS_x86_64_bin artifact: macOS_x86_64_bin
@@ -96,7 +96,7 @@ jobs:
universal: universal:
needs: build needs: build
runs-on: macos-15 runs-on: macos-latest
permissions: permissions:
contents: write contents: write
+19 -1
View File
@@ -1045,7 +1045,25 @@ else if $(UNIX)
LINKFLAGS += -framework Carbon ; # default for .c LINKFLAGS += -framework Carbon ; # default for .c
LINKFLAGS += -framework Cocoa ; # default for .m LINKFLAGS += -framework Cocoa ; # default for .m
if $(COMPILER) = "OSX10_6_X86_64BIT" { if $(TARGET_ARCH) = "arm64"
|| $(HOSTTYPE) = "arm64"
|| $(MACHTYPE) = "arm64-apple-darwin" {
ECHO "Target architecture: macOS ARM 64-bit (arm64)" ;
TARGET64 = true ;
CCFLAGS += -arch arm64 ;
C++FLAGS += -arch arm64 ;
LINKFLAGS += -arch arm64 ;
} else if $(TARGET_ARCH) = "x86_64"
|| $(HOSTTYPE) = "x86_64"
|| $(MACHTYPE) = "x86_64-apple-darwin" {
ECHO "Target architecture: macOS Intel 64-bit (x86_64)" ;
TARGET64 = true ;
CCFLAGS += -arch x86_64 ;
C++FLAGS += -arch x86_64 ;
LINKFLAGS += -arch x86_64 ;
} else if $(COMPILER) = "OSX10_6_X86_64BIT" {
ECHO "We're cross compiling to OSX 10.6 AMD/Intel 64 bit" ; ECHO "We're cross compiling to OSX 10.6 AMD/Intel 64 bit" ;
# setup script needs to export MACOSX_DEPLOYMENT_TARGET="10.6" # setup script needs to export MACOSX_DEPLOYMENT_TARGET="10.6"
# and export CROSS_CFLAGS=--target=x86_64-apple-macosx10.6.0 # and export CROSS_CFLAGS=--target=x86_64-apple-macosx10.6.0
+9 -7
View File
@@ -33,16 +33,18 @@ if $(NT) && $(MINGW64) {
# imdi low level cgen test code # imdi low level cgen test code
Main ctest : ctest.c cgen.c ; Main ctest : ctest.c cgen.c ;
# make imdi code program
Main imdi_make : imdi_make.c imdi_gen.c cgen.c ;
HDRS = ../h ../numlib ; HDRS = ../h ../numlib ;
LINKLIBS = ../numlib/libnum ; LINKLIBS = ../numlib/libnum ;
# GenFile source.c : program args ; make custom file # make imdi code program
# Generate all the kernel files if ! [ GLOB imdi : imdi_k.h ] || ! [ GLOB imdi : imdi_k.c ] {
GenFileND imdi_k.h : imdi_make $(IMDI_MAKE_OPT) -d [ NormPaths $(DOT) ] ; Main imdi_make : imdi_make.c imdi_gen.c cgen.c ;
Clean clean : imdi_k.c ;
# 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 # imdi library
Library libimdi : imdi.c imdi_tab.c ; Library libimdi : imdi.c imdi_tab.c ;
+48 -2
View File
@@ -204,6 +204,17 @@ typedef struct {
extern int json_ui_out; extern int json_ui_out;
int json_ui_out = 0; int json_ui_out = 0;
static int g_use_leds = 0; /* Enable i1Pro 2 visual LED feedback */
/* Update instrument LED state safely */
static void update_led_state(inst *it, inst_led_state state) {
if (!g_use_leds || it == NULL)
return;
if (it->set_led_state != NULL) {
it->set_led_state(it, state);
}
}
static void compute_patch_metrics(chcol *scb, double *eLab, double *mLab) { static void compute_patch_metrics(chcol *scb, double *eLab, double *mLab) {
/* Expected Lab */ /* Expected Lab */
if (scb->eXYZ[0] != 0.0 || scb->eXYZ[1] != 0.0 || scb->eXYZ[2] != 0.0) { if (scb->eXYZ[0] != 0.0 || scb->eXYZ[1] != 0.0 || scb->eXYZ[2] != 0.0) {
@@ -1486,8 +1497,10 @@ a1log *log /* verb, debug & error log */
/* Do any needed calibration before the user places the instrument on a desired spot */ /* Do any needed calibration before the user places the instrument on a desired spot */
if (it->needs_calibration(it) & inst_calt_n_dfrble_mask) { if (it->needs_calibration(it) & inst_calt_n_dfrble_mask) {
update_led_state(it, inst_led_cal_wait);
if ((rv = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0)) if ((rv = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0))
!= inst_ok) { != inst_ok) {
update_led_state(it, inst_led_off);
printf("\nCalibration failed with error :'%s' (%s)\n", printf("\nCalibration failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv)); it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it); it->del(it);
@@ -1495,6 +1508,7 @@ a1log *log /* verb, debug & error log */
free(pfname); free(pfname);
return -1; return -1;
} }
update_led_state(it, inst_led_off);
} }
/* Enable (switch or user) via uicallback trigger if possible */ /* Enable (switch or user) via uicallback trigger if possible */
@@ -1637,6 +1651,7 @@ a1log *log /* verb, debug & error log */
printf("Press any other key to start:%s",fl_end); printf("Press any other key to start:%s",fl_end);
} }
do_fflush(); do_fflush();
update_led_state(it, inst_led_row_ready);
if ((rv = it->read_strip(it, "STRIP", stipa+nextrap, nn, guide, plen, glen, tlen, vals)) != inst_ok if ((rv = it->read_strip(it, "STRIP", stipa+nextrap, nn, guide, plen, glen, tlen, vals)) != inst_ok
&& (rv & inst_mask) != inst_user_trig) { && (rv & inst_mask) != inst_user_trig) {
@@ -1698,6 +1713,7 @@ a1log *log /* verb, debug & error log */
printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end);
do_fflush(); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') { if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n"); printf("\n");
if (nn != NULL) free(nn); if (nn != NULL) free(nn);
free(vals); free(vals);
@@ -1717,7 +1733,9 @@ a1log *log /* verb, debug & error log */
if (cap2 & inst2_no_feedback) if (cap2 & inst2_no_feedback)
bad_beep(); bad_beep();
printf("\nStrip read failed because instruments needs calibration\n"); printf("\nStrip read failed because instruments needs calibration\n");
update_led_state(it, inst_led_cal_wait);
ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0); ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0);
update_led_state(it, inst_led_off);
if (ev != inst_ok) { /* Abort or fatal error */ if (ev != inst_ok) { /* Abort or fatal error */
if (nn != NULL) free(nn); if (nn != NULL) free(nn);
free(vals); free(vals);
@@ -1735,12 +1753,14 @@ a1log *log /* verb, debug & error log */
/* Deal with a misread */ /* Deal with a misread */
} else if ((rv & inst_mask) == inst_misread) { } else if ((rv & inst_mask) == inst_misread) {
update_led_state(it, inst_led_row_fail);
if (cap2 & inst2_no_feedback) if (cap2 & inst2_no_feedback)
bad_beep(); bad_beep();
empty_con_chars(); empty_con_chars();
printf("\nStrip read failed due to misread (%s)\n",it->interp_error(it, rv)); printf("\nStrip read failed due to misread (%s)\n",it->interp_error(it, rv));
printf("Hit Esc to give up, any other key to retry%s:",fl_end); do_fflush(); printf("Hit Esc to give up, any other key to retry%s:",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') { if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n"); printf("\n");
if (nn != NULL) free(nn); if (nn != NULL) free(nn);
free(vals); free(vals);
@@ -1754,12 +1774,14 @@ a1log *log /* verb, debug & error log */
/* Deal with a communications error */ /* Deal with a communications error */
} else if ((rv & inst_mask) == inst_coms_fail) { } else if ((rv & inst_mask) == inst_coms_fail) {
update_led_state(it, inst_led_row_fail);
if (cap2 & inst2_no_feedback) if (cap2 & inst2_no_feedback)
bad_beep(); bad_beep();
empty_con_chars(); empty_con_chars();
printf("\nStrip read failed due to communication problem.\n"); printf("\nStrip read failed due to communication problem.\n");
printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush(); printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') { if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n"); printf("\n");
if (nn != NULL) free(nn); if (nn != NULL) free(nn);
free(vals); free(vals);
@@ -1798,12 +1820,14 @@ a1log *log /* verb, debug & error log */
/* Some other error. Treat it as fatal */ /* Some other error. Treat it as fatal */
} else { } else {
update_led_state(it, inst_led_row_fail);
if (cap2 & inst2_no_feedback) if (cap2 & inst2_no_feedback)
bad_beep(); bad_beep();
printf("\nStrip read failed due unexpected error :'%s' (%s)\n", printf("\nStrip read failed due unexpected error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv)); it->inst_interp_error(it, rv), it->interp_error(it, rv));
printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush(); printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') { if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n"); printf("\n");
if (nn != NULL) free(nn); if (nn != NULL) free(nn);
free(vals); free(vals);
@@ -1953,6 +1977,7 @@ a1log *log /* verb, debug & error log */
return -1; return -1;
} }
if (ch != 0x0d && ch != 0x0a) { /* !(CR or LF) */ if (ch != 0x0d && ch != 0x0a) { /* !(CR or LF) */
update_led_state(it, inst_led_row_fail);
printf("\n"); printf("\n");
continue; /* Try again */ continue; /* Try again */
} }
@@ -1976,6 +2001,7 @@ a1log *log /* verb, debug & error log */
printf("\nThere is at least one patch with an very unexpected response! (DeltaE %f)\n",werror); printf("\nThere is at least one patch with an very unexpected response! (DeltaE %f)\n",werror);
printf("Hit Return to use it anyway, any other key to retry, Esc or 'q' to give up:%s",fl_end); do_fflush(); printf("Hit Return to use it anyway, any other key to retry, Esc or 'q' to give up:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') { if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n"); printf("\n");
if (nn != NULL) free(nn); if (nn != NULL) free(nn);
free(vals); free(vals);
@@ -1985,6 +2011,7 @@ a1log *log /* verb, debug & error log */
return -1; return -1;
} }
if (ch != 0x0d && ch != 0x0a) { /* !Cr */ if (ch != 0x0d && ch != 0x0a) { /* !Cr */
update_led_state(it, inst_led_row_fail);
printf("\n"); printf("\n");
continue; continue;
} }
@@ -1995,6 +2022,7 @@ a1log *log /* verb, debug & error log */
/* Must be OK - save the readings */ /* Must be OK - save the readings */
if (cap2 & inst2_no_feedback) if (cap2 & inst2_no_feedback)
good_beep(); good_beep();
update_led_state(it, inst_led_row_success);
printf(" Strip read OK"); printf(" Strip read OK");
if (boff != 0) if (boff != 0)
printf(" (DTP51 offset fix of %d applied)",boff); printf(" (DTP51 offset fix of %d applied)",boff);
@@ -2048,6 +2076,7 @@ a1log *log /* verb, debug & error log */
} /* Go around to read another row */ } /* Go around to read another row */
update_led_state(it, inst_led_off);
free(vals); free(vals);
/* -------------------------------------------------- */ /* -------------------------------------------------- */
@@ -2065,8 +2094,10 @@ a1log *log /* verb, debug & error log */
/* Do any needed calibration before the user places the instrument on a desired spot */ /* Do any needed calibration before the user places the instrument on a desired spot */
if (it->needs_calibration(it) & inst_calt_n_dfrble_mask) { if (it->needs_calibration(it) & inst_calt_n_dfrble_mask) {
update_led_state(it, inst_led_cal_wait);
if ((rv = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0)) if ((rv = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0))
!= inst_ok) { != inst_ok) {
update_led_state(it, inst_led_off);
printf("\nCalibration failed with error :'%s' (%s)\n", printf("\nCalibration failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv)); it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it); it->del(it);
@@ -2074,6 +2105,7 @@ a1log *log /* verb, debug & error log */
free(pfname); free(pfname);
return -1; return -1;
} }
update_led_state(it, inst_led_off);
} }
/* Enable (switch or user) via uicallback trigger if possible */ /* Enable (switch or user) via uicallback trigger if possible */
@@ -2302,7 +2334,9 @@ a1log *log /* verb, debug & error log */
if (cap2 & inst2_no_feedback) if (cap2 & inst2_no_feedback)
bad_beep(); bad_beep();
printf("\nSpot read failed because instruments needs calibration\n"); printf("\nSpot read failed because instruments needs calibration\n");
update_led_state(it, inst_led_cal_wait);
ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0); ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0);
update_led_state(it, inst_led_off);
if (ev != inst_ok) { /* Abort or fatal error */ if (ev != inst_ok) { /* Abort or fatal error */
it->del(it); it->del(it);
if (pfname != NULL) if (pfname != NULL)
@@ -2312,12 +2346,14 @@ a1log *log /* verb, debug & error log */
continue; continue;
/* Deal with a misread */ /* Deal with a misread */
} else if ((rv & inst_mask) == inst_misread) { } else if ((rv & inst_mask) == inst_misread) {
update_led_state(it, inst_led_row_fail);
if (cap2 & inst2_no_feedback) if (cap2 & inst2_no_feedback)
bad_beep(); bad_beep();
empty_con_chars(); empty_con_chars();
printf("\nStrip read failed due to misread (%s)\n",it->interp_error(it, rv)); printf("\nStrip read failed due to misread (%s)\n",it->interp_error(it, rv));
printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush(); printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') { if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n"); printf("\n");
it->del(it); it->del(it);
if (pfname != NULL) if (pfname != NULL)
@@ -2328,12 +2364,14 @@ a1log *log /* verb, debug & error log */
continue; continue;
/* Deal with a communications error */ /* Deal with a communications error */
} else if ((rv & inst_mask) == inst_coms_fail) { } else if ((rv & inst_mask) == inst_coms_fail) {
update_led_state(it, inst_led_row_fail);
if (cap2 & inst2_no_feedback) if (cap2 & inst2_no_feedback)
bad_beep(); bad_beep();
empty_con_chars(); empty_con_chars();
printf("\nStrip read failed due to communication problem.\n"); printf("\nStrip read failed due to communication problem.\n");
printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush(); printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') { if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n"); printf("\n");
it->del(it); it->del(it);
if (pfname != NULL) if (pfname != NULL)
@@ -2369,12 +2407,14 @@ a1log *log /* verb, debug & error log */
} else { } else {
/* Some other error. Treat it as fatal */ /* Some other error. Treat it as fatal */
update_led_state(it, inst_led_row_fail);
if (cap2 & inst2_no_feedback) if (cap2 & inst2_no_feedback)
bad_beep(); bad_beep();
printf("\nPatch read failed due unexpected error :'%s' (%s)\n", printf("\nPatch read failed due unexpected error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv)); it->inst_interp_error(it, rv), it->interp_error(it, rv));
printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush(); printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') { if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n"); printf("\n");
it->del(it); it->del(it);
if (pfname != NULL) if (pfname != NULL)
@@ -2390,6 +2430,7 @@ a1log *log /* verb, debug & error log */
empty_con_chars(); empty_con_chars();
printf("\nAbort ? - Are you sure ? [y/n]:%s",fl_end); do_fflush(); printf("\nAbort ? - Are you sure ? [y/n]:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 'y' || ch == 'Y') { if ((ch = next_con_char()) == 'y' || ch == 'Y') {
update_led_state(it, inst_led_off);
printf("\n"); printf("\n");
it->del(it); it->del(it);
if (pfname != NULL) if (pfname != NULL)
@@ -2401,7 +2442,9 @@ a1log *log /* verb, debug & error log */
} else if (ch == 'k') { } else if (ch == 'k') {
inst_code ev; inst_code ev;
update_led_state(it, inst_led_cal_wait);
ev = inst_handle_calibrate(it, inst_calt_available, inst_calc_none, NULL, NULL, 0); ev = inst_handle_calibrate(it, inst_calt_available, inst_calc_none, NULL, NULL, 0);
update_led_state(it, inst_led_off);
if (ev != inst_ok) { /* Abort or fatal error */ if (ev != inst_ok) { /* Abort or fatal error */
it->del(it); it->del(it);
if (pfname != NULL) if (pfname != NULL)
@@ -2556,6 +2599,7 @@ a1log *log /* verb, debug & error log */
/* -------------------------------------------------- */ /* -------------------------------------------------- */
/* clean up */ /* clean up */
update_led_state(it, inst_led_off);
if (it != NULL) if (it != NULL)
it->del(it); it->del(it);
if (pfname != NULL) if (pfname != NULL)
@@ -2621,7 +2665,7 @@ usage() {
} }
fprintf(stderr," -T ratio Modify strip patch consistency tolerance by ratio\n"); fprintf(stderr," -T ratio Modify strip patch consistency tolerance by ratio\n");
fprintf(stderr," -S Suppress wrong strip & unexpected value warnings\n"); fprintf(stderr," -S Suppress wrong strip & unexpected value warnings\n");
// fprintf(stderr," -Y U Test i1pro2 UV measurement mode\n"); fprintf(stderr," -Y l Enable i1Pro 2 visual LED feedback\n");
fprintf(stderr," -W n|h|x Override serial port flow control: n = none, h = HW, x = Xon/Xoff\n"); fprintf(stderr," -W n|h|x Override serial port flow control: n = none, h = HW, x = Xon/Xoff\n");
#ifndef SALONEINSTLIB #ifndef SALONEINSTLIB
fprintf(stderr," -P Plot spectral if patch by patch\n"); fprintf(stderr," -P Plot spectral if patch by patch\n");
@@ -2902,7 +2946,9 @@ int main(int argc, char *argv[]) {
if (na == NULL) if (na == NULL)
usage(); /* "Parameter expected after -Y" */ usage(); /* "Parameter expected after -Y" */
{ if (na[0] == 'l' || na[0] == 'L') {
g_use_leds = 1;
} else {
usage(); /* "-Y parameter '%c' not recognised",na[0] */ usage(); /* "-Y parameter '%c' not recognised",na[0] */
} }
} }
+20 -1
View File
@@ -170,9 +170,14 @@ i1pro_determine_capabilities(i1pro *p) {
| inst2_user_switch_trig | inst2_user_switch_trig
| inst2_bidi_scan | inst2_bidi_scan
| inst2_has_scan_toll | inst2_has_scan_toll
| inst2_no_feedback
; ;
if (p->dtype == instI1Pro2) {
p->cap2 |= inst2_has_leds;
} else {
p->cap2 |= inst2_no_feedback;
}
if (p->m != NULL) { if (p->m != NULL) {
i1proimp *m = (i1proimp *)p->m; i1proimp *m = (i1proimp *)p->m;
i1pro_state *s = &m->ms[m->mmode]; i1pro_state *s = &m->ms[m->mmode];
@@ -943,11 +948,24 @@ i1pro_get_set_opt(inst *pp, inst_opt_type m, ...) {
} }
} }
/* Set device indicator LED status */
static inst_code
i1pro_set_led_state(inst *pp, inst_led_state state) {
i1pro *p = (i1pro *)pp;
if (p->dtype != instI1Pro2)
return inst_unsupported;
return i1pro_interp_code(p, i1pro_imp_set_led_state(p, state));
}
/* Destroy ourselves */ /* Destroy ourselves */
static void static void
i1pro_del(inst *pp) { i1pro_del(inst *pp) {
i1pro *p = (i1pro *)pp; i1pro *p = (i1pro *)pp;
/* Shut down instrument hardware and close the port, then free state */
i1pro_close_port(p);
del_i1proimp(p); del_i1proimp(p);
if (p->icom != NULL) if (p->icom != NULL)
p->icom->del(p->icom); p->icom->del(p->icom);
@@ -982,6 +1000,7 @@ extern i1pro *new_i1pro(icoms *icom, instType dtype) {
p->meas_delay = i1pro_meas_delay; p->meas_delay = i1pro_meas_delay;
p->white_change = i1pro_white_change; p->white_change = i1pro_white_change;
p->interp_error = i1pro_interp_error; p->interp_error = i1pro_interp_error;
p->set_led_state = i1pro_set_led_state;
p->del = i1pro_del; p->del = i1pro_del;
p->icom = icom; p->icom = icom;
+308 -32
View File
@@ -389,8 +389,121 @@ i1pro_code add_i1proimp(i1pro *p) {
return I1PRO_OK; return I1PRO_OK;
} }
/* Shutdown instrument, and then destroy */ /* Stop background threads (switch monitoring & delayed trigger) */
/* implementation structure */ static void
i1pro_stop_threads(i1pro *p) {
i1proimp *m;
int i;
if (p == NULL || p->m == NULL)
return;
m = (i1proimp *)p->m;
/* Stop the switch monitoring thread first so it isn't using the */
/* communication channel while we shut down the instrument. */
if (m->th != NULL) {
m->th_term = 1; /* Tell thread to exit on error */
/* Ask the instrument to stop sending switch events and cancel */
/* any outstanding switch read. Only do this if the port is still */
/* open and the required method pointers are valid. */
if (p->icom != NULL && p->icom->is_open
&& p->icom->usb_control != NULL
&& p->icom->usb_cancel_io != NULL) {
i1pro_terminate_switch(p);
}
/* Give the thread a chance to exit cleanly (up to 5 seconds). */
for (i = 0; m->th_termed == 0 && i < 100; i++)
msec_sleep(50);
if (m->th_termed == 0) {
a1logd(p->log,3,"i1pro switch thread did not terminate, forcing\n");
if (m->th->terminate != NULL)
m->th->terminate(m->th);
}
if (m->th->wait != NULL)
m->th->wait(m->th);
if (m->th->del != NULL)
m->th->del(m->th);
m->th = NULL;
usb_uninit_cancel(&m->sw_cancel); /* Don't need cancel token now */
usb_uninit_cancel(&m->rd_sync); /* Don't need sync token now */
a1logd(p->log,5,"i1pro switch thread terminated\n");
}
/* Stop the delayed trigger thread if it is still running */
if (m->trig_thread != NULL) {
if (m->trig_thread->wait != NULL)
m->trig_thread->wait(m->trig_thread);
if (m->trig_thread->del != NULL)
m->trig_thread->del(m->trig_thread);
m->trig_thread = NULL;
a1logd(p->log,5,"i1pro trigger thread terminated\n");
}
/* Stop the LED status feedback thread if running */
if (m->led_th != NULL) {
m->led_th_run = 0;
if (m->led_th->wait != NULL)
m->led_th->wait(m->led_th);
if (m->led_th->del != NULL)
m->led_th->del(m->led_th);
m->led_th = NULL;
a1logd(p->log,5,"i1pro led thread terminated\n");
}
if (m->led_th_init) {
amutex_del(m->led_lock);
m->led_th_init = 0;
}
}
/* Stop any pending I/O, shut down the instrument hardware, and close the */
/* communication port. This is separate from freeing the implementation data. */
i1pro_code
i1pro_close_port(i1pro *p) {
i1pro_code ev = I1PRO_OK;
i1proimp *m;
a1logd(p->log,3,"i1pro_close_port: called\n");
if (p->m == NULL)
return I1PRO_OK;
m = (i1proimp *)p->m;
/* Stop the switch monitoring thread and trigger thread first so they */
/* aren't using the communication channel while we shut down the instrument. */
i1pro_stop_threads(p);
/* Update usage log/calibration for Rev A..D before closing the port */
if (p->dtype != instI1Pro2 && p->icom != NULL && p->icom->is_open
&& p->icom->usb_control != NULL && p->icom->usb_write != NULL) {
i1pro_code uev;
if ((uev = i1pro_update_log(p)) != I1PRO_OK) {
a1logd(p->log,2,"i1pro_update_log: Updating the cal and log parameters to"
" EEProm failed failed\n");
}
}
/* Turn off i1Pro2/Rev E indicator LEDs before closing the port. */
/* Only attempt the write if the port is still actively open and the */
/* required USB method pointers are valid. Rev A..D do not have LEDs. */
if (p->dtype == instI1Pro2 && p->icom != NULL && p->icom->is_open
&& p->icom->usb_control != NULL && p->icom->usb_write != NULL) {
i1pro2_indLEDoff(p); /* Ignore error - we're shutting down */
}
/* Close the underlying communication port */
if (p->icom != NULL && p->icom->is_open && p->icom->close_port != NULL) {
p->icom->close_port(p->icom);
}
return ev;
}
/* Destroy implementation structure */
void del_i1proimp(i1pro *p) { void del_i1proimp(i1pro *p) {
a1logd(p->log,5,"i1pro_del called\n"); a1logd(p->log,5,"i1pro_del called\n");
@@ -404,38 +517,28 @@ void del_i1proimp(i1pro *p) {
int i, j; int i, j;
i1proimp *m = (i1proimp *)p->m; i1proimp *m = (i1proimp *)p->m;
i1pro_state *s; i1pro_state *s;
i1pro_code ev;
if (p->dtype != instI1Pro2 && (ev = i1pro_update_log(p)) != I1PRO_OK) { /* Defensive guard: ensure background threads are terminated and joined */
a1logd(p->log,2,"i1pro_update_log: Updating the cal and log parameters to" /* before freeing state, in case i1pro_close_port() was not called. */
" EEProm failed failed\n"); i1pro_stop_threads(p);
/* Defensive guard: update usage log/cal for Rev A..D if port is still open */
if (p->dtype != instI1Pro2 && p->icom != NULL && p->icom->is_open
&& p->icom->usb_control != NULL && p->icom->usb_write != NULL) {
i1pro_code uev;
if ((uev = i1pro_update_log(p)) != I1PRO_OK) {
a1logd(p->log,2,"i1pro_update_log: Updating the cal and log parameters to"
" EEProm failed failed\n");
}
} }
/* i1pro_terminate_switch() seems to fail on a rev A & Rev C ?? */ /* Defensive guard: if the com port is still open and this is an */
if (m->th != NULL) { /* Terminate switch monitor thread */ /* i1Pro2, make sure the indicator LEDs are turned off before any */
m->th_term = 1; /* Tell thread to exit on error */ /* memory is freed. In normal teardown i1pro_close_port() has */
i1pro_terminate_switch(p); /* already done this, but the guard protects any direct call paths. */
if (p->dtype == instI1Pro2 && p->icom != NULL && p->icom->is_open
for (i = 0; m->th_termed == 0 && i < 5; i++) && p->icom->usb_control != NULL && p->icom->usb_write != NULL) {
msec_sleep(50); /* Wait for thread to terminate */ i1pro2_indLEDoff(p);
if (i >= 5) {
a1logd(p->log,5,"i1pro switch thread termination failed\n");
m->th->terminate(m->th); /* Try and force thread to terminate */
}
/* Strange Mac M2/rosetta bug ?? */
if (m->th->del == NULL) {
a1logd(p->log,1,"i1pro_del: ,m->th-del is NULL!!!");
} else {
m->th->del(m->th);
}
usb_uninit_cancel(&m->sw_cancel); /* Don't need cancel token now */
usb_uninit_cancel(&m->rd_sync); /* Don't need sync token now */
a1logd(p->log,5,"i1pro switch thread terminated\n");
}
if (m->trig_thread != NULL) {
m->trig_thread->del(m->trig_thread);
a1logd(p->log,5,"i1pro trigger thread terminated\n");
} }
/* Free any per mode data */ /* Free any per mode data */
@@ -12319,11 +12422,17 @@ i1pro2_stop_ruler(void *pp, int parm) {
static int static int
i1pro2_indLEDseq(void *pp, unsigned char *buf, int size) { i1pro2_indLEDseq(void *pp, unsigned char *buf, int size) {
i1pro *p = (i1pro *)pp; i1pro *p = (i1pro *)pp;
i1proimp *m = (i1proimp *)p->m; i1proimp *m;
int rwbytes; /* Data bytes written */ int rwbytes; /* Data bytes written */
unsigned char pbuf[4]; /* Number of bytes being send */ unsigned char pbuf[4]; /* Number of bytes being send */
int se, rv = I1PRO_OK; int se, rv = I1PRO_OK;
if (p == NULL || p->m == NULL || p->icom == NULL || !p->icom->is_open
|| p->icom->usb_control == NULL || p->icom->usb_write == NULL) {
return I1PRO_INT_NO_COMS;
}
m = (i1proimp *)p->m;
int2buf(pbuf, size); int2buf(pbuf, size);
a1logd(p->log,2,"i1pro2_indLEDseq: length %d bytes\n", size); a1logd(p->log,2,"i1pro2_indLEDseq: length %d bytes\n", size);
@@ -12377,6 +12486,173 @@ i1pro2_indLEDoff(void *pp) {
return rv; return rv;
} }
/* Set indicator LEDs to a solid color mask (or off if mask is 0) */
static int
i1pro2_indLEDset(void *pp, unsigned char mask) {
i1pro *p = (i1pro *)pp;
int rv = I1PRO_OK;
unsigned char seq[] = {
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x0a,
0xff, 0xff, 0xff, 0xff,
0x00, 0x36, 0x40,
0x00, 0x00, 0x01
};
if (mask == 0)
return i1pro2_indLEDoff(pp);
seq[12] = mask;
a1logd(p->log,3,"i1pro2_indLEDset: called with mask 0x%02x\n", mask);
rv = i1pro2_indLEDseq(p, seq, sizeof(seq));
a1logd(p->log,3,"i1pro2_indLEDset: returning ICOM err 0x%x\n", rv);
return rv;
}
/* Helper to sleep up to ms while checking if state changed or thread stopping */
static void
led_sleep_ms(i1proimp *m, inst_led_state state, int ms) {
int elapsed = 0;
while (m->led_th_run && elapsed < ms) {
inst_led_state cur;
amutex_lock(m->led_lock);
cur = m->current_led_state;
amutex_unlock(m->led_lock);
if (cur != state)
break;
msec_sleep(20);
elapsed += 20;
}
}
/* Worker thread for asynchronous LED pulsing / patterns */
static int
i1pro2_led_thread(void *context) {
i1pro *p = (i1pro *)context;
i1proimp *m;
int phase = 0;
int i;
if (p == NULL || p->m == NULL)
return 0;
m = (i1proimp *)p->m;
a1logd(p->log, 3, "i1pro2_led_thread started\n");
while (m->led_th_run) {
inst_led_state state;
amutex_lock(m->led_lock);
state = m->current_led_state;
amutex_unlock(m->led_lock);
switch (state) {
case inst_led_cal_wait:
/* White flashing: 500ms ON / 500ms OFF (1.0 Hz) */
if (phase % 2 == 0)
i1pro2_indLEDset(p, 0x3F); /* White = both Red+Green+Blue */
else
i1pro2_indLEDoff(p);
led_sleep_ms(m, inst_led_cal_wait, 500);
phase++;
break;
case inst_led_row_ready:
/* Blue pulsing: 300ms ON / 700ms OFF (1.0 Hz) */
i1pro2_indLEDset(p, 0x24); /* Blue = both Left & Right Blue */
led_sleep_ms(m, inst_led_row_ready, 300);
i1pro2_indLEDoff(p);
led_sleep_ms(m, inst_led_row_ready, 700);
phase = 0;
break;
case inst_led_row_fail:
/* Red rapid strobe: 3 short bursts (100ms ON / 100ms OFF, 600ms total) */
for (i = 0; i < 3 && m->led_th_run; i++) {
i1pro2_indLEDset(p, 0x09); /* Red = both Left & Right Red */
msec_sleep(100);
i1pro2_indLEDoff(p);
msec_sleep(100);
}
/* Revert to off until next command */
amutex_lock(m->led_lock);
if (m->current_led_state == inst_led_row_fail)
m->current_led_state = inst_led_off;
amutex_unlock(m->led_lock);
phase = 0;
break;
case inst_led_row_success:
/* Green pulse: Solid confirmation illumination for 400ms */
i1pro2_indLEDset(p, 0x12); /* Green = both Left & Right Green */
msec_sleep(400);
i1pro2_indLEDoff(p);
/* Revert to off until next command */
amutex_lock(m->led_lock);
if (m->current_led_state == inst_led_row_success)
m->current_led_state = inst_led_off;
amutex_unlock(m->led_lock);
phase = 0;
break;
case inst_led_off:
default:
i1pro2_indLEDoff(p);
led_sleep_ms(m, inst_led_off, 100);
phase = 0;
break;
}
}
/* Extinguish LEDs upon thread exit if port is still open */
if (p->icom != NULL && p->icom->is_open)
i1pro2_indLEDoff(p);
a1logd(p->log, 3, "i1pro2_led_thread exiting\n");
return 0;
}
/* Set indicator LED status */
i1pro_code
i1pro_imp_set_led_state(i1pro *p, inst_led_state state) {
i1proimp *m;
if (p == NULL || p->m == NULL)
return I1PRO_INT_NO_COMS;
m = (i1proimp *)p->m;
if (p->dtype != instI1Pro2)
return I1PRO_UNSUPPORTED;
if (!m->led_th_init) {
amutex_init(m->led_lock);
m->led_th_init = 1;
}
amutex_lock(m->led_lock);
m->current_led_state = state;
amutex_unlock(m->led_lock);
/* If thread is not yet running and state is not off, start worker thread */
if (m->led_th == NULL && state != inst_led_off) {
m->led_th_run = 1;
if ((m->led_th = new_athread(i1pro2_led_thread, (void *)p)) == NULL) {
a1logd(p->log, 1, "i1pro_imp_set_led_state: new_athread failed\n");
return I1PRO_INT_THREADFAILED;
}
} else if (state == inst_led_off && m->led_th == NULL) {
/* Directly extinguish LEDs if port is open */
if (p->icom != NULL && p->icom->is_open)
i1pro2_indLEDoff(p);
}
return I1PRO_OK;
}
#ifdef NEVER #ifdef NEVER
// ~~99 play with LED settings // ~~99 play with LED settings
+12
View File
@@ -340,6 +340,13 @@ struct _i1proimp {
volatile double whitestamp; /* meas_delay() white timestamp */ volatile double whitestamp; /* meas_delay() white timestamp */
volatile double trigstamp; /* meas_delay() trigger timestamp */ volatile double trigstamp; /* meas_delay() trigger timestamp */
/* Visual LED feedback thread management */
athread *led_th;
int led_th_run;
int led_th_init;
amutex led_lock;
inst_led_state current_led_state;
}; typedef struct _i1proimp i1proimp; }; typedef struct _i1proimp i1proimp;
/* Add an implementation structure */ /* Add an implementation structure */
@@ -348,6 +355,9 @@ i1pro_code add_i1proimp(i1pro *p);
/* Destroy implementation structure */ /* Destroy implementation structure */
void del_i1proimp(i1pro *p); void del_i1proimp(i1pro *p);
/* Close instrument (stop threads, update log, turn off LEDs, close coms) */
i1pro_code i1pro_close_port(i1pro *p);
/* ============================================================ */ /* ============================================================ */
/* Error codes returned from i1pro_imp */ /* Error codes returned from i1pro_imp */
@@ -532,6 +542,8 @@ i1pro_code i1pro_set_stdres(i1pro *p);
/* Modify the scan consistency tollerance */ /* Modify the scan consistency tollerance */
i1pro_code i1pro_set_scan_toll(i1pro *p, double toll_ratio); i1pro_code i1pro_set_scan_toll(i1pro *p, double toll_ratio);
/* Set device indicator LED status */
i1pro_code i1pro_imp_set_led_state(i1pro *p, inst_led_state state);
/* Update the single remission calibration and instrument usage log */ /* Update the single remission calibration and instrument usage log */
i1pro_code i1pro_update_log(i1pro *p); i1pro_code i1pro_update_log(i1pro *p);
+7
View File
@@ -589,6 +589,11 @@ static inst_config config_enum(inst *p, int ec) {
return inst_conf_unknown; return inst_conf_unknown;
} }
/* Set device indicator LED status (default implementation) */
static inst_code set_led_state(inst *p, inst_led_state state) {
return inst_unsupported;
}
/* ---------------------------------------------- */ /* ---------------------------------------------- */
/* Delete things set/done by new_inst() */ /* Delete things set/done by new_inst() */
@@ -824,6 +829,8 @@ void *cntx /* Context for callback */
p->last_scomerr = last_scomerr; p->last_scomerr = last_scomerr;
if (p->config_enum == NULL) if (p->config_enum == NULL)
p->config_enum = config_enum; p->config_enum = config_enum;
if (p->set_led_state == NULL)
p->set_led_state = set_led_state;
/* Set the provided user interaction callback */ /* Set the provided user interaction callback */
p->set_uicallback(p, uicallback, cntx); p->set_uicallback(p, uicallback, cntx);
+15
View File
@@ -371,6 +371,9 @@ typedef enum {
} inst2_capability; } inst2_capability;
/* Capability alias for instrument indicator LED status feedback */
#define INST_CAP_LED_STATUS inst2_has_leds
/* Instrument capabilities 3 (room for expansion) */ /* Instrument capabilities 3 (room for expansion) */
/* (Available capabilities may be mode dependent) */ /* (Available capabilities may be mode dependent) */
typedef enum { typedef enum {
@@ -558,6 +561,15 @@ typedef enum {
} inst_opt_type; } inst_opt_type;
/* Generic Instrument LED Status States */
typedef enum {
inst_led_off = 0,
inst_led_cal_wait = 1, /* Flashing White */
inst_led_row_ready = 2, /* Flashing Blue */
inst_led_row_fail = 3, /* Flashing Red */
inst_led_row_success = 4 /* Solid/Flash Green */
} inst_led_state;
/* Optional manufacturers instrument filter fitted to instrument (for inst_opt_set_filter) */ /* Optional manufacturers instrument filter fitted to instrument (for inst_opt_set_filter) */
/* These could be physical (i.e. Spectrolino, i1Pro3), or they could be virtual (i.e. i1Pro3) */ /* These could be physical (i.e. Spectrolino, i1Pro3), or they could be virtual (i.e. i1Pro3) */
typedef enum { typedef enum {
@@ -1170,6 +1182,9 @@ typedef struct _inst_meascondsel {
/* (This is used for deciding fallback/retry strategies) */ \ /* (This is used for deciding fallback/retry strategies) */ \
int (*last_scomerr)(struct _inst *p); \ int (*last_scomerr)(struct _inst *p); \
\ \
/* Set device indicator LED status (if supported) */ \
inst_code (*set_led_state)(struct _inst *p, inst_led_state state); \
\
/* Destroy ourselves */ \ /* Destroy ourselves */ \
void (*del)(struct _inst *p); \ void (*del)(struct _inst *p); \
+7 -5
View File
@@ -31,12 +31,14 @@ ObjectKeep port/getopt.c ;
ObjectKeep port/strcasecmp.c ; ObjectKeep port/strcasecmp.c ;
# generation utilities (don't use libport ! - there will be dependency problems for Xcompile!) # generation utilities (don't use libport ! - there will be dependency problems for Xcompile!)
if $(UNIX) { if ! [ GLOB tiff/libtiff : tif_fax3sm.c ] {
Main mkg3states : libtiff/mkg3states.c [ CatPaths port : strcasecmp.c ] ; if $(UNIX) {
} else { Main mkg3states : libtiff/mkg3states.c [ CatPaths port : strcasecmp.c ] ;
Main mkg3states : libtiff/mkg3states.c [ CatPaths port : getopt.c strcasecmp.c ] ; } else {
Main mkg3states : libtiff/mkg3states.c [ CatPaths port : getopt.c strcasecmp.c ] ;
}
GenFileND libtiff/tif_fax3sm.c : mkg3states -c const [ NormPaths libtiff/tif_fax3sm.c ] ;
} }
GenFileND libtiff/tif_fax3sm.c : mkg3states -c const [ NormPaths libtiff/tif_fax3sm.c ] ;
# support library for tools # support library for tools
if $(UNIX) { if $(UNIX) {