diff --git a/.appveyor.yml b/.appveyor.yml index f81752634..fda7644a0 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -206,7 +206,7 @@ for: environment: ARTISAN_OS: macos - MACOSX_DEPLOYMENT_TARGET: 11.0 + MACOSX_DEPLOYMENT_TARGET: 12.0 PYTHONSITEPKGS: /Users/appveyor/venv${PYTHON_V}/lib/python${PYTHON_V}/site-packages QT_PATH: ${PYTHONSITEPKGS}/PyQt6/Qt6 QT_SRC_PATH: /Users/appveyor/Qt/6.6/macos diff --git a/src/Info.plist b/src/Info.plist index 5ee046a88..2cfd5897e 100644 --- a/src/Info.plist +++ b/src/Info.plist @@ -137,12 +137,11 @@ LSArchitecturePriority arm64 - x86_64 LSHasLocalizedDisplayName LSMinimumSystemVersion - 11.0 + 14.0 LSMultipleInstancesProhibited false NSBluetoothAlwaysUsageDescription diff --git a/src/artisanlib/filters.py b/src/artisanlib/filters.py index 305861a2e..b8acabbcf 100644 --- a/src/artisanlib/filters.py +++ b/src/artisanlib/filters.py @@ -165,10 +165,11 @@ def _process(self, x:float) -> float: yraw = ys + yerr # define the filters - import scipy.signal # type: ignore + from scipy.signal import iirfilter, lfilter, sosfilt # type:ignore[import-untyped] + # # define lowpass filter with 2.5 Hz cutoff frequency of order 4 - b, a = scipy.signal.iirfilter(4, Wn=2.5, fs=fs, btype='low', ftype='butter') - y_scipy_lfilter = scipy.signal.lfilter(b, a, yraw) + b, a = iirfilter(4, Wn=2.5, fs=fs, btype='low', ftype='butter') + y_scipy_lfilter = lfilter(b, a, yraw) live_lfilter = LiveLFilter(b, a) # simulate live filter - passing values one by one @@ -176,9 +177,9 @@ def _process(self, x:float) -> float: # define lowpass filter with 2.5 Hz cutoff frequency of order 2 - sos = scipy.signal.iirfilter(2, Wn=2.5, fs=fs, btype='low', + sos = iirfilter(2, Wn=2.5, fs=fs, btype='low', ftype='butter', output='sos') - y_scipy_sosfilt = scipy.signal.sosfilt(sos, yraw) + y_scipy_sosfilt = sosfilt(sos, yraw) live_sosfilter = LiveSosFilter(sos) # simulate live filter - passing values one by one diff --git a/src/artisanlib/main.py b/src/artisanlib/main.py index e85f677d1..1ec67954c 100644 --- a/src/artisanlib/main.py +++ b/src/artisanlib/main.py @@ -25853,7 +25853,7 @@ def initialize_locale(my_app:Artisan) -> str: qt_translation_modules:List[str] = [ 'qtbase', 'qtconnectivity', - 'qtwebengine' +# 'qtwebengine' ] # NOTE: on updates, need to update util.py:locale2full_local() as well diff --git a/src/artisanlib/pid.py b/src/artisanlib/pid.py index 3c8fb94b1..77b60e3c5 100644 --- a/src/artisanlib/pid.py +++ b/src/artisanlib/pid.py @@ -19,7 +19,7 @@ import time import numpy -import scipy.signal # type:ignore[import-untyped] +from scipy.signal import iirfilter # type:ignore[import-untyped] import logging from typing import Final, List, Optional, Callable @@ -334,7 +334,7 @@ def getDuty(self) -> Optional[float]: @staticmethod def derivativeFilter() -> LiveSosFilter: return LiveSosFilter( - scipy.signal.iirfilter(1, # order + iirfilter(1, # order Wn=0.2, # 0 < Wn < fs/2 (fs=1 -> fs/2=0.5) fs=1, # sampling rate, Hz btype='low', diff --git a/src/build-macos3.sh b/src/build-macos3.sh index e2338ec30..05bfd5023 100755 --- a/src/build-macos3.sh +++ b/src/build-macos3.sh @@ -32,6 +32,9 @@ echo "************* build derived files **************" if [ $? -ne 0 ]; then echo "Failed in build-derived.sh"; exit $?; else (echo "** Finished build-derived.sh"); fi +# remove useless .c file from Python site-packages from local build setups +rm -f ${PYTHONPATH}/site-packages/fontTools/misc/bezierTools.c # 1.9MB + # distribution rm -rf build dist sleep .3 # sometimes it takes a little for dist to get really empty diff --git a/src/includes/Machines/TRINITAS/T2.aset b/src/includes/Machines/TRINITAS/T2.aset index 70202b630..eb42853c0 100644 --- a/src/includes/Machines/TRINITAS/T2.aset +++ b/src/includes/Machines/TRINITAS/T2.aset @@ -16,13 +16,13 @@ extrastopbits=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0\x1) extratimeout=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x6?\xf0\0\0\0\0\0\0) [ExtraDev] -extraCurveVisibility1=true, true, true, true, true, true, true, true, true, true +extraCurveVisibility1=false, true, true, true, true, true, true, true, true, true extraCurveVisibility2=false, true, true, true, true, true, true, true, true, true extraDelta1=false, false, false, false, false, false, false, false, false, false extraDelta2=false, false, false, false, false, false, false, false, false, false extraFill1=0, 0, 0, 0, 0, 0, 0, 0, 0, 0 extraFill2=0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -extraLCDvisibility1=true, false, false, false, false, false, false, false, false, false +extraLCDvisibility1=false, false, false, false, false, false, false, false, false, false extraLCDvisibility2=false, false, false, false, false, false, false, false, false, false extradevicecolor1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\xe\0#\0\x32\0\x30\0\x38\0\x36\0\x62\0\x62) extradevicecolor2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\n\0\x62\0l\0\x61\0\x63\0k) @@ -69,7 +69,7 @@ input3div=1 input3float=false input3mode= input3register=106 -input3slave=3 +input3slave=0 input4FloatsAsInt=false input4bcd=false input4code=3 diff --git a/src/includes/Machines/TRINITAS/T2_air.aset b/src/includes/Machines/TRINITAS/T2_air.aset new file mode 100644 index 000000000..82db43ce5 --- /dev/null +++ b/src/includes/Machines/TRINITAS/T2_air.aset @@ -0,0 +1,124 @@ +[General] +Delay=2000 +roastertype_setup=TRINITAS T2 air +dropDuplicates=true +dropDuplicatesLimit=0.1 + +[Device] +id=29 + +[ExtraComm] +extrabaudrate=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\x96\0) +extrabytesize=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0\b) +extracomport=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\b\0\x43\0O\0M\0\x31) +extraparity=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\x2\0\x45) +extrastopbits=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0\x1) +extratimeout=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x6?\xf0\0\0\0\0\0\0) + +[ExtraDev] +extraCurveVisibility1=true, true, true, true, true, true, true, true, true, true +extraCurveVisibility2=false, true, true, true, true, true, true, true, true, true +extraDelta1=false, false, false, false, false, false, false, false, false, false +extraDelta2=false, false, false, false, false, false, false, false, false, false +extraFill1=0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +extraFill2=0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +extraLCDvisibility1=true, false, false, false, false, false, false, false, false, false +extraLCDvisibility2=false, false, false, false, false, false, false, false, false, false +extradevicecolor1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\xe\0#\0\x32\0\x30\0\x38\0\x36\0\x62\0\x62) +extradevicecolor2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\n\0\x62\0l\0\x61\0\x63\0k) +extradevices=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0!) +extramathexpression1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\0) +extramathexpression2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\0) +extraname1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\xe\0\x41\0i\0r\0\x66\0l\0o\0w) +extraname2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\xe\0\x45\0x\0t\0r\0\x61\0 \0\x32) + +[Modbus] +PID_OFF_action= +PID_ON_action= +PID_SV_register=0 +PID_d_register=0 +PID_i_register=0 +PID_p_register=0 +PID_slave_ID=0 +PIDmultiplier=0 +SVmultiplier=0 +baudrate=9600 +bytesize=8 +comport=COM4 +host=127.0.0.1 +input1FloatsAsInt=false +input1bcd=false +input1code=4 +input1div=0 +input1float=false +input1mode=C +input1register=1000 +input1slave=2 +input2FloatsAsInt=false +input2bcd=false +input2code=4 +input2div=0 +input2float=false +input2mode=C +input2register=1000 +input2slave=1 +input3FloatsAsInt=false +input3bcd=false +input3code=3 +input3div=1 +input3float=false +input3mode= +input3register=106 +input3slave=3 +input4FloatsAsInt=false +input4bcd=false +input4code=3 +input4div=0 +input4float=false +input4mode=C +input4register=0 +input4slave=0 +input5FloatsAsInt=false +input5bcd=false +input5code=3 +input5div=0 +input5float=false +input5mode=C +input5register=0 +input5slave=0 +input6FloatsAsInt=false +input6bcd=false +input6code=3 +input6div=0 +input6float=false +input6mode=C +input6register=0 +input6slave=0 +input7FloatsAsInt=false +input7bcd=false +input7code=3 +input7div=0 +input7float=false +input7mode=C +input7register=0 +input7slave=0 +input8FloatsAsInt=false +input8bcd=false +input8code=3 +input8div=0 +input8float=false +input8mode=C +input8register=0 +input8slave=0 +littleEndianFloats=false +parity=N +port=502 +stopbits=2 +timeout=0.4 +type=0 +wordorderLittle=true +optimizer=true +fetch_max_blocks=false + +[MachineSetup] +capacity=2 diff --git a/src/includes/Machines/TRINITAS/T2_legacy.aset b/src/includes/Machines/TRINITAS/T2_legacy.aset new file mode 100644 index 000000000..fa0e65098 --- /dev/null +++ b/src/includes/Machines/TRINITAS/T2_legacy.aset @@ -0,0 +1,124 @@ +[General] +Delay=2000 +roastertype_setup=TRINITAS T2 legacy +dropDuplicates=true +dropDuplicatesLimit=0.1 + +[Device] +id=29 + +[ExtraComm] +extrabaudrate=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\x96\0) +extrabytesize=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0\b) +extracomport=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\b\0\x43\0O\0M\0\x31) +extraparity=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\x2\0\x45) +extrastopbits=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0\x1) +extratimeout=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x6?\xf0\0\0\0\0\0\0) + +[ExtraDev] +extraCurveVisibility1=true, true, true, true, true, true, true, true, true, true +extraCurveVisibility2=false, true, true, true, true, true, true, true, true, true +extraDelta1=false, false, false, false, false, false, false, false, false, false +extraDelta2=false, false, false, false, false, false, false, false, false, false +extraFill1=0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +extraFill2=0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +extraLCDvisibility1=true, false, false, false, false, false, false, false, false, false +extraLCDvisibility2=false, false, false, false, false, false, false, false, false, false +extradevicecolor1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\xe\0#\0\x32\0\x30\0\x38\0\x36\0\x62\0\x62) +extradevicecolor2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\n\0\x62\0l\0\x61\0\x63\0k) +extradevices=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0!) +extramathexpression1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\0) +extramathexpression2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\0) +extraname1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\xe\0\x41\0i\0r\0\x66\0l\0o\0w) +extraname2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\xe\0\x45\0x\0t\0r\0\x61\0 \0\x32) + +[Modbus] +PID_OFF_action= +PID_ON_action= +PID_SV_register=0 +PID_d_register=0 +PID_i_register=0 +PID_p_register=0 +PID_slave_ID=0 +PIDmultiplier=0 +SVmultiplier=0 +baudrate=9600 +bytesize=8 +comport=COM4 +host=127.0.0.1 +input1FloatsAsInt=false +input1bcd=false +input1code=4 +input1div=0 +input1float=false +input1mode=C +input1register=1000 +input1slave=2 +input2FloatsAsInt=false +input2bcd=false +input2code=4 +input2div=0 +input2float=false +input2mode=C +input2register=1000 +input2slave=1 +input3FloatsAsInt=false +input3bcd=false +input3code=3 +input3div=1 +input3float=false +input3mode= +input3register=106 +input3slave=0 +input4FloatsAsInt=false +input4bcd=false +input4code=3 +input4div=0 +input4float=false +input4mode=C +input4register=0 +input4slave=0 +input5FloatsAsInt=false +input5bcd=false +input5code=3 +input5div=0 +input5float=false +input5mode=C +input5register=0 +input5slave=0 +input6FloatsAsInt=false +input6bcd=false +input6code=3 +input6div=0 +input6float=false +input6mode=C +input6register=0 +input6slave=0 +input7FloatsAsInt=false +input7bcd=false +input7code=3 +input7div=0 +input7float=false +input7mode=C +input7register=0 +input7slave=0 +input8FloatsAsInt=false +input8bcd=false +input8code=3 +input8div=0 +input8float=false +input8mode=C +input8register=0 +input8slave=0 +littleEndianFloats=false +parity=N +port=502 +stopbits=2 +timeout=0.4 +type=0 +wordorderLittle=true +optimizer=true +fetch_max_blocks=false + +[MachineSetup] +capacity=2 diff --git a/src/includes/Machines/TRINITAS/T7.aset b/src/includes/Machines/TRINITAS/T7.aset index d68df19b2..db847da77 100644 --- a/src/includes/Machines/TRINITAS/T7.aset +++ b/src/includes/Machines/TRINITAS/T7.aset @@ -23,7 +23,7 @@ extraDelta2=false, false, false, false, false, false, false, false, false, false extraFill1=0, 0, 0, 0, 0, 0, 0, 0, 0, 0 extraFill2=0, 0, 0, 0, 0, 0, 0, 0, 0, 0 extraLCDvisibility1=true, false, false, false, false, false, false, false, false, false -extraLCDvisibility2=true, false, false, false, false, false, false, false, false, false +extraLCDvisibility2=false, false, false, false, false, false, false, false, false, false extradevicecolor1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\xe\0#\0\x32\0\x30\0\x38\0\x36\0\x62\0\x62) extradevicecolor2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\xe\0#\0\x61\0\x62\0\x30\0\x34\0\x32\0\x36) extradevices=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0!) @@ -77,7 +77,7 @@ input4div=1 input4float=false input4mode= input4register=3 -input4slave=3 +input4slave=0 input5FloatsAsInt=false input5bcd=false input5code=3 diff --git a/src/includes/Machines/TRINITAS/T7_gas.aset b/src/includes/Machines/TRINITAS/T7_gas.aset new file mode 100644 index 000000000..73f8013a1 --- /dev/null +++ b/src/includes/Machines/TRINITAS/T7_gas.aset @@ -0,0 +1,124 @@ +[General] +Delay=2000 +roastertype_setup=TRINITAS T7 gas +dropDuplicates=true +dropDuplicatesLimit=0.1 + +[Device] +id=29 + +[ExtraComm] +extrabaudrate=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\x96\0) +extrabytesize=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0\b) +extracomport=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\b\0\x43\0O\0M\0\x31) +extraparity=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\x2\0\x45) +extrastopbits=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0\x1) +extratimeout=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x6?\xf0\0\0\0\0\0\0) + +[ExtraDev] +extraCurveVisibility1=true, true, true, true, true, true, true, true, true, true +extraCurveVisibility2=true, true, true, true, true, true, true, true, true, true +extraDelta1=false, false, false, false, false, false, false, false, false, false +extraDelta2=false, false, false, false, false, false, false, false, false, false +extraFill1=0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +extraFill2=0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +extraLCDvisibility1=true, false, false, false, false, false, false, false, false, false +extraLCDvisibility2=true, false, false, false, false, false, false, false, false, false +extradevicecolor1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\xe\0#\0\x32\0\x30\0\x38\0\x36\0\x62\0\x62) +extradevicecolor2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\xe\0#\0\x61\0\x62\0\x30\0\x34\0\x32\0\x36) +extradevices=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0!) +extramathexpression1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\0) +extramathexpression2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\0) +extraname1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\x6\0{\0\x30\0}) +extraname2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\x6\0{\0\x33\0}) + +[Modbus] +PID_OFF_action= +PID_ON_action= +PID_SV_register=0 +PID_d_register=0 +PID_i_register=0 +PID_p_register=0 +PID_slave_ID=0 +PIDmultiplier=0 +SVmultiplier=0 +baudrate=9600 +bytesize=8 +comport=COM4 +host=127.0.0.1 +input1FloatsAsInt=false +input1bcd=false +input1code=3 +input1div=0 +input1float=false +input1mode=C +input1register=1 +input1slave=3 +input2FloatsAsInt=false +input2bcd=false +input2code=3 +input2div=0 +input2float=false +input2mode=C +input2register=0 +input2slave=3 +input3FloatsAsInt=false +input3bcd=false +input3code=3 +input3div=1 +input3float=false +input3mode= +input3register=2 +input3slave=3 +input4FloatsAsInt=false +input4bcd=false +input4code=3 +input4div=1 +input4float=false +input4mode= +input4register=3 +input4slave=3 +input5FloatsAsInt=false +input5bcd=false +input5code=3 +input5div=0 +input5float=false +input5mode=C +input5register=0 +input5slave=0 +input6FloatsAsInt=false +input6bcd=false +input6code=3 +input6div=0 +input6float=false +input6mode=C +input6register=0 +input6slave=0 +input7FloatsAsInt=false +input7bcd=false +input7code=3 +input7div=0 +input7float=false +input7mode=C +input7register=0 +input7slave=0 +input8FloatsAsInt=false +input8bcd=false +input8code=3 +input8div=0 +input8float=false +input8mode=C +input8register=0 +input8slave=0 +littleEndianFloats=false +parity=N +port=502 +stopbits=1 +timeout=0.4 +type=0 +wordorderLittle=true +optimizer=true +fetch_max_blocks=false + +[MachineSetup] +capacity=7 diff --git a/src/includes/Machines/TRINITAS/T7_legacy.aset b/src/includes/Machines/TRINITAS/T7_legacy.aset new file mode 100644 index 000000000..fd0c83e97 --- /dev/null +++ b/src/includes/Machines/TRINITAS/T7_legacy.aset @@ -0,0 +1,124 @@ +[General] +Delay=2000 +roastertype_setup=TRINITAS T7 legacy +dropDuplicates=true +dropDuplicatesLimit=0.1 + +[Device] +id=29 + +[ExtraComm] +extrabaudrate=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\x96\0) +extrabytesize=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0\b) +extracomport=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\b\0\x43\0O\0M\0\x31) +extraparity=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\x2\0\x45) +extrastopbits=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0\x1) +extratimeout=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x6?\xf0\0\0\0\0\0\0) + +[ExtraDev] +extraCurveVisibility1=true, true, true, true, true, true, true, true, true, true +extraCurveVisibility2=false, true, true, true, true, true, true, true, true, true +extraDelta1=false, false, false, false, false, false, false, false, false, false +extraDelta2=false, false, false, false, false, false, false, false, false, false +extraFill1=0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +extraFill2=0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +extraLCDvisibility1=true, false, false, false, false, false, false, false, false, false +extraLCDvisibility2=true, false, false, false, false, false, false, false, false, false +extradevicecolor1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\xe\0#\0\x32\0\x30\0\x38\0\x36\0\x62\0\x62) +extradevicecolor2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\xe\0#\0\x61\0\x62\0\x30\0\x34\0\x32\0\x36) +extradevices=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\x2\0\0\0!) +extramathexpression1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\0) +extramathexpression2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\0) +extraname1=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\x6\0{\0\x30\0}) +extraname2=@Variant(\0\0\0\t\0\0\0\x1\0\0\0\n\0\0\0\x6\0{\0\x33\0}) + +[Modbus] +PID_OFF_action= +PID_ON_action= +PID_SV_register=0 +PID_d_register=0 +PID_i_register=0 +PID_p_register=0 +PID_slave_ID=0 +PIDmultiplier=0 +SVmultiplier=0 +baudrate=9600 +bytesize=8 +comport=COM4 +host=127.0.0.1 +input1FloatsAsInt=false +input1bcd=false +input1code=3 +input1div=0 +input1float=false +input1mode=C +input1register=1 +input1slave=3 +input2FloatsAsInt=false +input2bcd=false +input2code=3 +input2div=0 +input2float=false +input2mode=C +input2register=0 +input2slave=3 +input3FloatsAsInt=false +input3bcd=false +input3code=3 +input3div=1 +input3float=false +input3mode= +input3register=106 +input3slave=10 +input4FloatsAsInt=false +input4bcd=false +input4code=3 +input4div=1 +input4float=false +input4mode= +input4register=3 +input4slave=0 +input5FloatsAsInt=false +input5bcd=false +input5code=3 +input5div=0 +input5float=false +input5mode=C +input5register=0 +input5slave=0 +input6FloatsAsInt=false +input6bcd=false +input6code=3 +input6div=0 +input6float=false +input6mode=C +input6register=0 +input6slave=0 +input7FloatsAsInt=false +input7bcd=false +input7code=3 +input7div=0 +input7float=false +input7mode=C +input7register=0 +input7slave=0 +input8FloatsAsInt=false +input8bcd=false +input8code=3 +input8div=0 +input8float=false +input8mode=C +input8register=0 +input8slave=0 +littleEndianFloats=false +parity=N +port=502 +stopbits=1 +timeout=0.4 +type=0 +wordorderLittle=true +optimizer=true +fetch_max_blocks=false + +[MachineSetup] +capacity=7 diff --git a/src/pyproject.toml b/src/pyproject.toml index 9abfb5bad..2e3586abb 100644 --- a/src/pyproject.toml +++ b/src/pyproject.toml @@ -304,7 +304,7 @@ line-length = 80 lint.ignore = ["E402", "E501", "E741", "PLR0913", "PLR2004", "COM812", "PLR0912", "PLR0915", "PLW0603", "PLR0911", "SIM105", "SIM114", "PLR2044"] # Exclude a variety of commonly ignored directories. -exclude = ["dist", "build", "proto", ".pytype", ".mypy_cache", ".git"] +exclude = ["dist", "build", "proto", ".pytype", ".mypy_cache", ".git", ".venv", ".venv_universal", ".venv_arm"] # Assume Python 3.8. target-version = "py38" diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index 4831d86b6..0cd57c878 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -1,12 +1,12 @@ types-openpyxl>=3.1.5.20240822 types-Pillow>=10.2.0.20240822 -types-protobuf>=5.27.0.20240626 +types-protobuf>=5.27.0.20240907 types-psutil>=6.0.0.20240901 types-pyserial>=3.5.0.20240826 types-python-dateutil==2.9.0.20240906 -types-pytz==2024.1.0.20240417 +types-pytz==2024.2.0.20240913 types-pyyaml>=6.0.12.20240808 -types-requests>=2.32.0.20240905 +types-requests>=2.32.0.20240907 types-setuptools>=74.1.0.20240906 types-urllib3>=1.26.25.14 lxml-stubs>=0.5.1 @@ -15,7 +15,7 @@ pyright==1.1.380 ruff>=0.6.4 pylint==3.2.7 pre-commit>=3.8.0 -pytest>=8.3.2 +pytest>=8.3.3 pytest-cov==5.0.0 #pytest-qt==4.4.0 #pytest-xvfb==3.0.0 @@ -24,7 +24,7 @@ pytest-cov==5.0.0 #pytest-bdd==6.1.1 #pytest-benchmark==4.0.0 #pytest-mock==3.11.1 -hypothesis>=6.112.0 +hypothesis>=6.112.1 coverage>=7.6.1 coverage-badge==1.1.2 codespell==2.3.0 diff --git a/src/requirements.txt b/src/requirements.txt index fcd14522f..d4cb1b1c7 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -33,7 +33,7 @@ pymodbus==3.6.9; python_version < '3.9' # last Python 3.8 release pymodbus==3.7.2; python_version >= '3.9' python-snap7==1.3; python_version < '3.10' # last Python 3.9 release python-snap7==2.0.0; python_version >= '3.10' -Phidget22==1.19.20240513 +Phidget22==1.20.20240911 Unidecode==1.3.8 qrcode==7.4.2 requests==2.32.3 @@ -42,7 +42,7 @@ pyusb==1.2.1 persist-queue==1.0.0 portalocker==2.10.1 xlrd==2.0.1 -websockets==13.0 +websockets==13.0.1 PyYAML==6.0.2 psutil==6.0.0 typing-extensions==4.10.0; python_version < '3.8' # required for supporting Final and TypeDict on Python <3.8 @@ -66,7 +66,7 @@ python-bidi==0.4.2; python_version < '3.9' # last Python 3.8 release python-bidi==0.6.0; python_version >= '3.9' arabic-reshaper==3.0.0 pillow>=10.4.0 -pydantic==2.9.0; (platform_system=='Windows' and python_version>'3.10') or sys_platform=='darwin' or platform_system=='Linux' +pydantic==2.9.1; (platform_system=='Windows' and python_version>'3.10') or sys_platform=='darwin' or platform_system=='Linux' pydantic==2.7.1; (platform_system=='Windows' and python_version<'3.9') # last version working with Windows 7/8 pending resolution of pydantic Issue #9920 babel==2.16.0 # @@ -76,7 +76,7 @@ babel==2.16.0 # ### yoctopuce 1.10.42060 on macOS 10.13 yoctopuce==1.10.42060; sys_platform=='darwin' and platform_release<'20.0' # last version supporting macOS 10.13 -yoctopuce==2.0.62388; sys_platform!='darwin' or (sys_platform=='darwin' and platform_release>='20.0') +yoctopuce==2.0.62554; sys_platform!='darwin' or (sys_platform=='darwin' and platform_release>='20.0') # last 1.x yoctopuce lib: 1.10.57762 # 2.0.59414 is no longer universal2 lacking arm64 support; 2.0.59503 should fix this ## diff --git a/src/setup-macos3-legacy.py b/src/setup-macos3-legacy.py index f223aaaa5..acdcfb266 100755 --- a/src/setup-macos3-legacy.py +++ b/src/setup-macos3-legacy.py @@ -169,6 +169,8 @@ 'matplotlib_backends': '-', # '-' for imported or explicit "Qt5Agg, PDF, PS, SVG" 'includes': ['serial'], 'excludes' : ['tkinter','curses', # 'sqlite3', + 'test', # don't bundle the Python tests + 'setuptools', # not needed ], 'plist' : plist} @@ -286,17 +288,20 @@ 'printsupport', 'styles' ] + qt_plugin_files = [ - 'libqsvgicon.dylib', - 'libqgif.dylib', - 'libqicns.dylib', - 'libqico.dylib', + 'libqsvgicon.dylib', # needed to render MPL toolbar as SVG icons (fallback is PNG; build-in support) +# 'libqgif.dylib', +# 'libqicns.dylib', +# 'libqico.dylib', 'libqjpeg.dylib', - 'libqmacjp2.dylib', +# 'libqmacjp2.dylib', +# 'libqpdf.dylib', 'libqsvg.dylib', - 'libqtga.dylib', - 'libqwbmp.dylib', - 'libqwebp.dylib', +# 'libqtga.dylib', +# 'libqtiff.dylib', +# 'libqwbmp.dylib', +# 'libqwebp.dylib', 'libqcocoa.dylib', 'libcocoaprintersupport.dylib', 'libqmacstyle.dylib' @@ -398,11 +403,7 @@ except Exception: # pylint: disable=broad-except pass try: - subprocess.check_call('rm -rf ./Artisan.app/Contents/Resources/lib/python3.9/matplotlib/mpl-data/sample_data',shell = True) -except Exception: # pylint: disable=broad-except - pass -try: - subprocess.check_call('rm -rf ./Artisan.app/Contents/Resources/lib/python3.11/matplotlib/mpl-data/sample_data',shell = True) + subprocess.check_call(f'rm -rf ./Artisan.app/Contents/Resources/lib/{python_version}/matplotlib/mpl-data/sample_data',shell = True) except Exception: # pylint: disable=broad-except pass diff --git a/src/setup-macos3.py b/src/setup-macos3.py index 8ae993505..8ab05e3fb 100644 --- a/src/setup-macos3.py +++ b/src/setup-macos3.py @@ -13,7 +13,7 @@ # # AUTHOR # Dave Baxter, Marko Luther 2023 -"""This is a setup.py script generated by py2applet +"""This is a setup.py script Usage: python3 setup-mac3.py py2app @@ -39,6 +39,9 @@ APP = ['artisan.py'] +SUPPORTED_LANGUAGES = ['ar', 'da', 'de','el','en','es','fa','fi','fr','gd', 'he','hu','id','it','ja','ko','lv', 'nl','no','pl','pt_BR','pt','sk', 'sv','th','tr','uk','vi','zh_CN','zh_TW'] + + DATA_FILES:List[Tuple[str,List[str]]] = [ ('../Resources', [ r'artisanProfile.icns', @@ -122,12 +125,22 @@ 'matplotlib_backends': '-', # '-' for imported or explicit "Qt5Agg, PDF, PS, SVG" 'includes': ['serial', 'charset_normalizer.md__mypyc'], 'excludes' : ['tkinter','curses', + 'test', # don't bundle the Python tests + 'pydoc_data', # python documentation tools + 'setuptools', # not needed 'PyInstaller', # if pyinstaller is installed, whyever, py2app tries to include pyinstaller and fails on some missing pyside modules 'PyQt5', # standard builds are now running on PyQt6. If PyQt5 is not excluded here it will be included in Resources/lib/python310.zip # 'sqlite3', ], 'plist' : plist} +# remove parts of the .dist_info content (removes 2MB of data) +#from py2app._pkg_meta import IGNORED_DISTINFO +#IGNORED_DISTINFO.add('METADATA') +##IGNORED_DISTINFO.add('LICENSE.txt') +##IGNORED_DISTINFO.add('LICENSE') +##IGNORED_DISTINFO.add('license_files') + setup( name='Artisan', version=VERSION, @@ -156,10 +169,13 @@ #except Exception: # pylint: disable=broad-except # PYTHONPATH = r'/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/' # -#try: -# PYTHON_V = os.environ["PYTHON_V"] -#except: -# PYTHON_V = '3.10' + +try: + PYTHON_V = os.environ['PYTHON_V'] +except Exception: + PYTHON_V = '3.12' +python_version = f'python{PYTHON_V}' + # (independent) matplotlib (installed via pip) shared libs are not copied by py2app (both cp are needed!) # UPDATE 9/2020: pip install of MPL v3.3.x does not come with a .dylibs directory any longer #subprocess.check_call(r'mkdir Artisan.app/Contents/Resources/lib/python' + PYTHON_V + '/lib-dynload/matplotlib/.dylibs',shell = True) @@ -167,7 +183,7 @@ #subprocess.check_call(r'cp ' + PYTHONPATH + r'site-packages/matplotlib/.dylibs/* Artisan.app/Contents/Frameworks',shell = True) # add localization stubs to make macOS translate the systems menu item and native dialogs -for lang in ['ar', 'da', 'de','el','en','es','fa','fi','fr','gd', 'he','hu','id','it','ja','ko','lv', 'nl','no','pl','pt_BR','pt','sk', 'sv','th','tr','uk','vi','zh_CN','zh_TW']: +for lang in SUPPORTED_LANGUAGES: loc_dir = r'Artisan.app/Contents/Resources/' + lang + r'.lproj' subprocess.check_call(r'mkdir ' + loc_dir,shell = True) subprocess.check_call(r'touch ' + loc_dir + r'/Localizable.string',shell = True) @@ -217,18 +233,20 @@ 'QtNetwork', 'QtDBus', 'QtBluetooth', - 'QtConcurrent', # not on PyQt6.2, but PyQt6.4 and PyQt5.x +# 'QtConcurrent', # not on PyQt6.2, but PyQt6.4 and PyQt5.x # needed for QtWebEngine HTML2PDF export: - 'QtWebEngineWidgets', 'QtWebEngineCore', 'QtWebEngine', # not on PyQt6 - 'QtQuick', - 'QtQuickWidgets', - 'QtQml', - 'QtQmlModels', - 'QtWebChannel', - 'QtPositioning', - 'QtOpenGL' # required by QtWebEngineCore + 'QtWebEngineWidgets', + +# the following seems not to be required on PyQt/Qt 6.7.x +# 'QtQuick', +# 'QtQuickWidgets', +# 'QtQml', +# 'QtQmlModels', +# 'QtWebChannel', +# 'QtPositioning', +# 'QtOpenGL' # required by QtWebEngineCore ] Qt_frameworks = [module + '.framework' for module in Qt_modules] @@ -240,16 +258,18 @@ 'styles' ] qt_plugin_files = [ - 'libqsvgicon.dylib', - 'libqgif.dylib', - 'libqicns.dylib', - 'libqico.dylib', + 'libqsvgicon.dylib', # needed to render MPL toolbar as SVG icons (fallback is PNG; build-in support) +# 'libqgif.dylib', +# 'libqicns.dylib', +# 'libqico.dylib', 'libqjpeg.dylib', - 'libqmacjp2.dylib', +# 'libqmacjp2.dylib', +# 'libqpdf.dylib', 'libqsvg.dylib', - 'libqtga.dylib', - 'libqwbmp.dylib', - 'libqwebp.dylib', +# 'libqtga.dylib', +# 'libqtiff.dylib', +# 'libqwbmp.dylib', +# 'libqwebp.dylib', 'libqcocoa.dylib', 'libcocoaprintersupport.dylib', 'libqmacstyle.dylib' @@ -273,77 +293,102 @@ pass -for python_version in ['python3.8', 'python3.9', 'python3.10', 'python3.11']: - rootdir = f'./Artisan.app/Contents/Resources/lib/{python_version}' - - if os.path.isdir(f'{rootdir}/PyQt6'): - # if PyQt6 exists we remove PyQt5 completely - try: - subprocess.check_call(f'rm -rf {rootdir}/PyQt5',shell = True) - except Exception: # pylint: disable=broad-except - pass - # remove Qt artefacts - for qt_dir in [ - 'PyQt5/Qt', - 'PyQt5/bindings', - 'PyQt5/uic', -# 'PyQt5/Qt5/translations', # qt translations are kept and loaded from their standard dir - 'PyQt5/Qt5/qml', - 'PyQt5/Qt5/qsci', -# "PyQt5/Qt5/lib", # comment for the non-Framework variant - 'PyQt6/Qt', - 'PyQt6/bindings', - 'PyQt6/lupdate', - 'PyQt6/uic', -# 'PyQt6/Qt6/translations', # qt translations are kept and loaded from their standard dir - 'PyQt6/Qt6/qml', - 'PyQt6/Qt6/qsci', -# "PyQt6/Qt6/lib", # comment for the non-Framework variant - ]: - try: - subprocess.check_call(f'rm -rf {rootdir}/{qt_dir}',shell = True) - except Exception: # pylint: disable=broad-except - pass - for pyqt_dir in ['PyQt5', 'PyQt6']: - # remove unused PyQt libs (not in Qt_modules) - for subdir, _dirs, files in os.walk(f'{rootdir}/{pyqt_dir}'): - for file in files: - if file.endswith('.pyi'): - file_path = os.path.join(subdir, file) - subprocess.check_call(f'rm -rf {file_path}',shell = True) - if file.endswith(('.abi3.so','.pyi')) and file.split('.')[0] not in Qt_modules: - file_path = os.path.join(subdir, file) - subprocess.check_call(f'rm -rf {file_path}',shell = True) +rootdir = f'./Artisan.app/Contents/Resources/lib/{python_version}' + +if os.path.isdir(f'{rootdir}/PyQt6'): + # if PyQt6 exists we remove PyQt5 completely + try: + subprocess.check_call(f'rm -rf {rootdir}/PyQt5',shell = True) + except Exception: # pylint: disable=broad-except + pass +# remove Qt artefacts +for qt_dir in [ + 'PyQt5/Qt', + 'PyQt5/bindings', + 'PyQt5/uic', +# 'PyQt5/Qt5/translations', # qt translations are kept and loaded from their standard dir + 'PyQt5/Qt5/qml', + 'PyQt5/Qt5/qsci', +# 'PyQt5/Qt5/lib', # comment for the non-Framework variant + 'PyQt6/Qt', + 'PyQt6/bindings', + 'PyQt6/lupdate', + 'PyQt6/uic', +# 'PyQt6/Qt6/translations', # qt translations are kept and loaded from their standard dir + 'PyQt6/Qt6/qml', + 'PyQt6/Qt6/qsci', +# 'PyQt6/Qt6/lib', # comment for the non-Framework variant + ]: + try: + subprocess.check_call(f'rm -rf {rootdir}/{qt_dir}',shell = True) + except Exception: # pylint: disable=broad-except + pass +for pyqt_dir in ['PyQt5', 'PyQt6']: + # remove unused PyQt libs (not in Qt_modules) + for subdir, _dirs, files in os.walk(f'{rootdir}/{pyqt_dir}'): + for file in files: + if file.endswith('.pyi'): + file_path = os.path.join(subdir, file) + subprocess.check_call(f'rm -rf {file_path}',shell = True) + if file.endswith(('.abi3.so','.pyi')) and file.split('.')[0] not in Qt_modules: + file_path = os.path.join(subdir, file) + subprocess.check_call(f'rm -rf {file_path}',shell = True) # uncomment for non-Framework variant - # remove unused Qt frameworks libs (not in Qt_modules_frameworks) - for qt_dir in ['PyQt5/Qt5/lib', 'PyQt6/Qt6/lib']: - qt = f'{rootdir}/{qt_dir}' - for _root, dirs, _ in os.walk(qt): - for di in dirs: - if di.startswith('Qt') and di.endswith('.framework') and di not in Qt_frameworks: - file_path = os.path.join(qt, di) - subprocess.check_call(f'rm -rf {file_path}',shell = True) - - # remove unused plugins - for qt_dir in ['PyQt5/Qt5/plugins', 'PyQt6/Qt6/plugins']: - for root, dirs, _ in os.walk(f'{rootdir}/{qt_dir}'): - for d in dirs: - if d not in qt_plugin_dirs: - subprocess.check_call('rm -rf ' + os.path.join(root,d),shell = True) - else: - for subdir, _, files in os.walk(os.path.join(root,d)): - for file in files: - if file not in qt_plugin_files: - file_path = os.path.join(subdir, file) - subprocess.check_call(f'rm -rf {file_path}',shell = True) +# remove unused Qt frameworks libs (not in Qt_modules_frameworks) +for qt_dir in ['PyQt5/Qt5/lib', 'PyQt6/Qt6/lib']: + qt = f'{rootdir}/{qt_dir}' + for _root, dirs, _ in os.walk(qt): + for di in dirs: + if di.startswith('Qt') and di.endswith('.framework') and di not in Qt_frameworks: + file_path = os.path.join(qt, di) + subprocess.check_call(f'rm -rf {file_path}',shell = True) + # remove all WebEngine locales + file_path = os.path.join(qt, 'QtWebEngineCore.framework/Resources/qtwebengine_locales') + subprocess.check_call(f'rm -rf {file_path}',shell = True) + + +# remove unused plugins +for qt_dir in ['PyQt5/Qt5/plugins', 'PyQt6/Qt6/plugins']: + for root, dirs, _ in os.walk(f'{rootdir}/{qt_dir}'): + for d in dirs: + if d not in qt_plugin_dirs: + subprocess.check_call('rm -rf ' + os.path.join(root,d),shell = True) + else: + for subdir, _, files in os.walk(os.path.join(root,d)): + for file in files: + if file not in qt_plugin_files: + file_path = os.path.join(subdir, file) + subprocess.check_call(f'rm -rf {file_path}',shell = True) # comment for non-Framework variant -# # move plugins directory from Resources/lib/python3.x/PyQtX/QtX/plugins to the root of the app -# try: -# shutil.move(f"{rootdir}/{qt_dir}", "./Artisan.app/Contents/PlugIns") -# except Exception: # pylint: disable=broad-except -# pass - +# # move plugins directory from Resources/lib/python3.x/PyQtX/QtX/plugins to the root of the app +# try: +# shutil.move(f"{rootdir}/{qt_dir}", "./Artisan.app/Contents/PlugIns") +# except Exception: # pylint: disable=broad-except +# pass + +helpfile_prefix_unused_modules = { + 'qt_help', +# 'qtconnectivity', # needed for BT communication/dialogs + 'qtwebengine', # module used, but without any GUI + 'qtdeclarative', + 'qtlocation', + 'qtmultimedia', + 'qtserialport', + 'qtwebsockets' +} + +# remove unused tranlsations of unused Qt modules +for qt_dir in ['PyQt5/Qt5/translations', 'PyQt6/Qt6/translations']: + qt = f'{rootdir}/{qt_dir}' + for root, _, files in os.walk(qt): + for file in files: + if any(file.startswith(x) for x in helpfile_prefix_unused_modules): + file_path = os.path.join(root, file) +# print('Deleting', file_path) + subprocess.check_call(f'rm -rf {file_path}',shell = True) + +print('*** Removing duplicate mpl_data folder and mpl_data/sample_data subfolder ***') # remove duplicate mpl_data folder try: @@ -351,19 +396,11 @@ except Exception: # pylint: disable=broad-except pass try: - subprocess.check_call('rm -rf ./Artisan.app/Contents/Resources/lib/python3.9/matplotlib/mpl-data/sample_data',shell = True) -except Exception: # pylint: disable=broad-except - pass -try: - subprocess.check_call('rm -rf ./Artisan.app/Contents/Resources/lib/python3.10/matplotlib/mpl-data/sample_data',shell = True) -except Exception: # pylint: disable=broad-except - pass -try: - subprocess.check_call('rm -rf ./Artisan.app/Contents/Resources/lib/python3.11/matplotlib/mpl-data/sample_data',shell = True) + subprocess.check_call(f'rm -rf ./Artisan.app/Contents/Resources/lib/{python_version}/matplotlib/mpl-data/sample_data',shell = True) except Exception: # pylint: disable=broad-except pass -print('*** Removing unused files ***') +print('*** Removing debug and test files and folders ***') for root, dirs, files in os.walk('.'): for file in files: if 'debug' in file: @@ -386,6 +423,9 @@ # print('Deleting', file) os.remove(os.path.join(root,file)) elif file.endswith('.cc'): +# print('Deleting', file) + os.remove(os.path.join(root,file)) + elif file.endswith('.c'): # print('Deleting', file) os.remove(os.path.join(root,file)) # .afm files should not be removed as without matplotlib will fail on startup @@ -400,6 +440,41 @@ # print('Deleting', os.path.join(r,fl)) os.remove(os.path.join(r,fl)) +print('*** Removing parts of scipy ***') + +# remove unused parts of scipy +for sdir in [ + 'scipy/io', + 'scipy/misc', + 'scipy/cluster', + 'scipy/odr', + ]: + try: + subprocess.check_call(f'rm -rf ./Artisan.app/Contents/Resources/lib/{python_version}/{sdir}',shell = True) + except Exception: # pylint: disable=broad-except + pass + +# remove unused language support from babel + +print('*** Removing unused language support from babel ***') + +for root, _, files in os.walk(f'./Artisan.app/Contents/Resources/lib/{python_version}/babel/locale-data'): + for file in files: + if file.endswith('.dat') and (('_' not in file and file.split('.')[0] not in SUPPORTED_LANGUAGES) or + ('_' in file and file.split('.')[0] not in SUPPORTED_LANGUAGES)): +# print('Deleting', file) + os.remove(os.path.join(root,file)) + + +print('*** Removing yoctopuce driver libs not for this platforms ***') +try: + subprocess.check_call(f'rm -f ./Artisan.app/Contents/Resources/lib/{python_version}/yoctopuce/cdll/*.so',shell = True) + subprocess.check_call(f'rm -f ./Artisan.app/Contents/Resources/lib/{python_version}/yoctopuce/cdll/*.dll',shell = True) +except Exception: # pylint: disable=broad-except + pass + +#### + os.chdir('..') subprocess.check_call(r'rm -f artisan-mac-' + VERSION + r'.dmg',shell = True) subprocess.check_call(r'hdiutil create artisan-mac-' + VERSION + r'.dmg -volname "artisan" -fs HFS+ -srcfolder "dist"',shell = True)